package main import ( "crypto/tls" "errors" "flag" "io/ioutil" "log" "net/http" "net/http/httputil" "net/url" "sync" "gopkg.in/yaml.v2" ) var ( config = flag.String("conf", "certs/goxy.yml", "configuration file") listen = flag.String("listen", ":http", "HTTP") listenTLS = flag.String("listentls", ":https", "TLS") ) type Config map[string]Route type Route struct { CertFile string KeyFile string Upstream string cert *tls.Certificate } func (c Config) SNI(h *tls.ClientHelloInfo) (*tls.Certificate, error) { if r, ok := c[h.ServerName]; ok && r.cert != nil { return r.cert, nil } return nil, errors.New("no cert for " + h.ServerName) } func (r *Route) LoadCert() error { if r.CertFile == "" && r.KeyFile == "" { return nil } cert, err := tls.LoadX509KeyPair(r.CertFile, r.KeyFile) if err != nil { return err } r.cert = &cert return nil } func (r Route) String() string { if r.cert != nil { return r.Upstream + " with TLS" } else { return r.Upstream } } func LoadConfig(fname string) (Config, error) { conf, err := ioutil.ReadFile(fname) if err != nil { return Config{}, err } var c Config return c, yaml.Unmarshal(conf, &c) } func main() { flag.Parse() c, err := LoadConfig(*config) if err != nil { log.Fatal(err) } for k, v := range c { if err := v.LoadCert(); err != nil { log.Println("load", err) continue } c[k] = v up, err := url.Parse(v.Upstream) if err != nil { log.Println("upstream", err) continue } log.Println("map", k, "to", v) http.Handle(k+"/", httputil.NewSingleHostReverseProxy(up)) } var wg sync.WaitGroup wg.Add(2) go func() { defer wg.Done() log.Println("listen", *listenTLS) s := http.Server{ Addr: *listenTLS, TLSConfig: &tls.Config{GetCertificate: c.SNI}, } log.Fatal(s.ListenAndServeTLS("", "")) }() go func() { defer wg.Done() log.Println("listen", *listen) s := http.Server{ Addr: *listen, } log.Fatal(s.ListenAndServe()) }() wg.Wait() }