package main import ( "crypto/tls" "errors" "net/http" "net/http/httputil" "net/url" ) // Route defines a set of routes including correspondent TLS certificates type Route map[string]Entry // Entry holds routing settings type Entry struct { ServerName string Upstream string Cert []byte Key []byte cert *tls.Certificate } // GetCertificate returns certificate for SNI negotiation func (r Route) GetCertificate(h *tls.ClientHelloInfo) (*tls.Certificate, error) { if e, ok := r[h.ServerName]; ok && e.cert != nil { return e.cert, nil } return nil, errors.New("no cert for " + h.ServerName) } // Restore and update routes from in-memory state func (r Route) Restore() error { mux := http.NewServeMux() for k, v := range route { if v.Cert != nil && v.Key != nil { cert, err := tls.X509KeyPair(v.Cert, v.Key) if err != nil { return err } v.cert = &cert r[k] = v } up, err := url.Parse(v.Upstream) if err != nil { return err } mux.Handle(v.ServerName+"/", httputil.NewSingleHostReverseProxy(up)) } server.Handler = mux return nil } func (e Entry) String() string { ret := e.ServerName + " → " + e.Upstream if e.cert != nil { ret += " with TLS" } return ret }