Skip to content

Instantly share code, notes, and snippets.

@dmitshur
Last active January 28, 2018 08:48

Revisions

  1. dmitshur revised this gist Sep 13, 2017. 1 changed file with 0 additions and 1 deletion.
    1 change: 0 additions & 1 deletion main.go
    Original file line number Diff line number Diff line change
    @@ -76,7 +76,6 @@ func newRouter() http.Handler {
    req.URL.Scheme = "http"
    req.URL.Host = "127.0.0.1:10002"
    }
    req.Host = "" // TODO: Figure out if this is needed; document it if so.
    }
    return &httputil.ReverseProxy{
    Director: director,
  2. dmitshur revised this gist Jun 27, 2016. 1 changed file with 4 additions and 4 deletions.
    8 changes: 4 additions & 4 deletions main.go
    Original file line number Diff line number Diff line change
    @@ -102,14 +102,14 @@ var domains = []struct {
    }

    func loadCertificates() (*tls.Config, error) {
    config := &tls.Config{}
    c := &tls.Config{}
    for _, d := range domains {
    cert, err := tls.LoadX509KeyPair(d.certFile, d.keyFile)
    if err != nil {
    return nil, err
    }
    config.Certificates = append(config.Certificates, cert)
    c.Certificates = append(c.Certificates, cert)
    }
    config.BuildNameToCertificate()
    return config, nil
    c.BuildNameToCertificate()
    return c, nil
    }
  3. dmitshur revised this gist Jun 27, 2016. 1 changed file with 0 additions and 2 deletions.
    2 changes: 0 additions & 2 deletions main.go
    Original file line number Diff line number Diff line change
    @@ -103,7 +103,6 @@ var domains = []struct {

    func loadCertificates() (*tls.Config, error) {
    config := &tls.Config{}

    for _, d := range domains {
    cert, err := tls.LoadX509KeyPair(d.certFile, d.keyFile)
    if err != nil {
    @@ -112,6 +111,5 @@ func loadCertificates() (*tls.Config, error) {
    config.Certificates = append(config.Certificates, cert)
    }
    config.BuildNameToCertificate()

    return config, nil
    }
  4. dmitshur revised this gist Feb 29, 2016. 1 changed file with 0 additions and 4 deletions.
    4 changes: 0 additions & 4 deletions main.go
    Original file line number Diff line number Diff line change
    @@ -105,10 +105,6 @@ func loadCertificates() (*tls.Config, error) {
    config := &tls.Config{}

    for _, d := range domains {
    if d.certFile == "" && d.keyFile == "" {
    continue
    }

    cert, err := tls.LoadX509KeyPair(d.certFile, d.keyFile)
    if err != nil {
    return nil, err
  5. dmitshur revised this gist Feb 28, 2016. 1 changed file with 1 addition and 8 deletions.
    9 changes: 1 addition & 8 deletions main.go
    Original file line number Diff line number Diff line change
    @@ -17,7 +17,7 @@ var (
    func main() {
    flag.Parse()

    httpsCanonicalHostRedirector := newCanonicalHostRedirector("https", newRouter())
    httpsCanonicalHostRedirector := &canonicalHostRedirector{"https", newRouter()}

    tlsConfig, err := loadCertificates()
    if err != nil {
    @@ -30,13 +30,6 @@ func main() {
    }
    }

    func newCanonicalHostRedirector(scheme string, handler http.Handler) http.Handler {
    return &canonicalHostRedirector{
    scheme: scheme,
    handler: handler,
    }
    }

    // canonicalHostRedirector redirects to a canonical host, when visiting a non-canonical one.
    // Otherwise it defers to handler.
    type canonicalHostRedirector struct {
  6. dmitshur created this gist Feb 28, 2016.
    128 changes: 128 additions & 0 deletions main.go
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,128 @@
    // Server for HTTPS protocol. Redirects to canonical hosts, reverse proxies requests to internal backing servers.
    package main

    import (
    "crypto/tls"
    "flag"
    "log"
    "net/http"
    "net/http/httputil"
    "time"
    )

    var (
    httpsFlag = flag.String("https", ":https", "Listen for HTTPS connections on this address.")
    )

    func main() {
    flag.Parse()

    httpsCanonicalHostRedirector := newCanonicalHostRedirector("https", newRouter())

    tlsConfig, err := loadCertificates()
    if err != nil {
    log.Fatalln(err)
    }
    server := &http.Server{Addr: *httpsFlag, TLSConfig: tlsConfig, Handler: httpsCanonicalHostRedirector}
    err = server.ListenAndServeTLS("", "")
    if err != nil {
    log.Fatalln(err)
    }
    }

    func newCanonicalHostRedirector(scheme string, handler http.Handler) http.Handler {
    return &canonicalHostRedirector{
    scheme: scheme,
    handler: handler,
    }
    }

    // canonicalHostRedirector redirects to a canonical host, when visiting a non-canonical one.
    // Otherwise it defers to handler.
    type canonicalHostRedirector struct {
    scheme string // Scheme to redirect to. E.g., "https".
    handler http.Handler // Handler to defer to when canonical host is used.
    }

    func (r *canonicalHostRedirector) ServeHTTP(w http.ResponseWriter, req *http.Request) {
    var canonicalHost string
    switch req.Host {
    default: // Primary domain.
    canonicalHost = "example.com"
    case "anotherdomain.org", "www.anotherdomain.org":
    canonicalHost = "anotherdomain.org"
    case "foobar.thirdexample.org", "www.thirdexample.org", "thirdexample.org":
    canonicalHost = "foobar.thirdexample.org"
    }

    // Redirect to canonical host, if needed.
    // TODO: Is it worth it to try to do a case insensitive comparison here, to avoid it needlessly triggerring redirection?
    // It can only happen with weird clients; latest Chrome will always send requests with lowercase domains.
    if req.Host != canonicalHost {
    u := *req.URL
    u.Scheme = r.scheme
    u.Host = canonicalHost
    http.Redirect(w, req, u.String(), http.StatusTemporaryRedirect)
    return
    }

    r.handler.ServeHTTP(w, req)
    }

    // newRouter returns a reverse proxy that routes to internal backing servers for each domain.
    func newRouter() http.Handler {
    director := func(req *http.Request) {
    switch req.Host {
    default: // Primary domain.
    req.URL.Scheme = "http"
    req.URL.Host = "127.0.0.1:10000"
    case "anotherdomain.org":
    req.URL.Scheme = "http"
    req.URL.Host = "127.0.0.1:10001"
    case "foobar.thirdexample.org":
    req.URL.Scheme = "http"
    req.URL.Host = "127.0.0.1:10002"
    }
    req.Host = "" // TODO: Figure out if this is needed; document it if so.
    }
    return &httputil.ReverseProxy{
    Director: director,
    FlushInterval: 1 * time.Second,
    }
    }

    // HTTPS certificate details.

    var domains = []struct {
    certFile, keyFile string
    }{
    {
    certFile: "/home/user/.lego/certificates/example.com.crt",
    keyFile: "/home/user/.lego/certificates/example.com.key",
    }, {
    certFile: "/home/user/.lego/certificates/anotherdomain.org.crt",
    keyFile: "/home/user/.lego/certificates/anotherdomain.org.key",
    }, {
    certFile: "/home/user/.lego/certificates/foobar.thirdexample.org.crt",
    keyFile: "/home/user/.lego/certificates/foobar.thirdexample.org.key",
    },
    }

    func loadCertificates() (*tls.Config, error) {
    config := &tls.Config{}

    for _, d := range domains {
    if d.certFile == "" && d.keyFile == "" {
    continue
    }

    cert, err := tls.LoadX509KeyPair(d.certFile, d.keyFile)
    if err != nil {
    return nil, err
    }
    config.Certificates = append(config.Certificates, cert)
    }
    config.BuildNameToCertificate()

    return config, nil
    }