/* godlighty -- highly-customizable HTTP, HTTP/2, HTTPS server Copyright (C) 2021 Sergey Matveev This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 3 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ package godlighty import ( "crypto/tls" "crypto/x509" "encoding/pem" "errors" "fmt" "io/ioutil" "log" ) var ( NextProtos = []string{"h2", "http/1.1"} HostToCertificate map[string]*tls.Certificate HostClientAuth map[string]*x509.CertPool ) func GetCertificate(chi *tls.ClientHelloInfo) (*tls.Certificate, error) { cert := HostToCertificate[chi.ServerName] if cert == nil { return nil, errors.New("no certificate found") } return cert, nil } func GetConfigForClient(chi *tls.ClientHelloInfo) (*tls.Config, error) { pool := HostClientAuth[chi.ServerName] if pool == nil { return nil, nil } return &tls.Config{ GetCertificate: GetCertificate, NextProtos: NextProtos, ClientCAs: pool, ClientAuth: tls.RequireAndVerifyClientCert, }, nil } func LoadCertificates() { HostToCertificate = make(map[string]*tls.Certificate, len(Hosts)) HostClientAuth = make(map[string]*x509.CertPool) for host, cfg := range Hosts { if cfg.TLS == nil { continue } cert, err := tls.LoadX509KeyPair(cfg.TLS.Cert, cfg.TLS.Key) if err != nil { log.Fatalln(err) } if cfg.TLS.CACert != "" { data, err := ioutil.ReadFile(cfg.TLS.CACert) if err != nil { log.Fatalln(err) } block, _ := pem.Decode(data) if block == nil { log.Fatalln(fmt.Errorf("no PEM found: %s", cfg.TLS.CACert)) } if block.Type != "CERTIFICATE" { log.Fatalln(fmt.Errorf("non CERTIFICATE: %s", cfg.TLS.CACert)) } cert.Certificate = append(cert.Certificate, block.Bytes) } HostToCertificate[host] = &cert pool := x509.NewCertPool() for _, p := range cfg.TLS.ClientCAs { data, err := ioutil.ReadFile(p) if err != nil { log.Fatalln(err) } var block *pem.Block for len(data) > 0 { block, data = pem.Decode(data) if block == nil { log.Fatalln("can not decode PEM:", p) } if block.Type != "CERTIFICATE" { continue } ca, err := x509.ParseCertificate(block.Bytes) if err != nil { log.Fatalln(err) } pool.AddCert(ca) } } if len(pool.Subjects()) > 0 { HostClientAuth[host] = pool } } } func NewTLSConfig() *tls.Config { return &tls.Config{ NextProtos: NextProtos, GetCertificate: GetCertificate, GetConfigForClient: GetConfigForClient, } }