Last active
January 28, 2018 08:48
-
-
Save dmitshur/d3b1c1c2215bf5b34190 to your computer and use it in GitHub Desktop.
Server for HTTPS protocol. Redirects to canonical hosts, reverse proxies requests to internal backing servers.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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 := &canonicalHostRedirector{"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) | |
} | |
} | |
// 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" | |
} | |
} | |
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) { | |
c := &tls.Config{} | |
for _, d := range domains { | |
cert, err := tls.LoadX509KeyPair(d.certFile, d.keyFile) | |
if err != nil { | |
return nil, err | |
} | |
c.Certificates = append(c.Certificates, cert) | |
} | |
c.BuildNameToCertificate() | |
return c, nil | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment