Use certmagic, simplify config, set HTTP timeouts and a modern TLSConfig

This commit is contained in:
Ken-Håvard Lieng 2018-12-16 12:19:16 +01:00
parent c5a9a5b1c1
commit 6c3a5777c4
171 changed files with 30701 additions and 4912 deletions

View file

@ -5,16 +5,16 @@ import (
"log"
"net"
"net/http"
"net/http/httputil"
"net/url"
"strings"
"sync"
"time"
"github.com/gorilla/websocket"
"github.com/khlieng/dispatch/config"
"github.com/khlieng/dispatch/pkg/letsencrypt"
"github.com/khlieng/dispatch/pkg/session"
"github.com/khlieng/dispatch/storage"
"github.com/mholt/certmagic"
)
var channelStore = storage.NewChannelStore()
@ -128,58 +128,87 @@ func (d *Dispatch) loadUser(user *storage.User) {
func (d *Dispatch) startHTTP() {
cfg := d.Config()
addr := cfg.Address
port := cfg.Port
if cfg.Dev {
// The node dev server will proxy index page requests and
// websocket connections to this port
port = "1337"
}
httpSrv := &http.Server{
Addr: net.JoinHostPort(cfg.Address, port),
}
if cfg.HTTPS.Enabled {
portHTTPS := cfg.HTTPS.Port
redirect := cfg.HTTPS.Redirect
httpSrv.ReadTimeout = 5 * time.Second
httpSrv.WriteTimeout = 5 * time.Second
if redirect {
log.Println("[HTTP] Listening on port", port, "(HTTPS Redirect)")
go http.ListenAndServe(net.JoinHostPort(addr, port), d.createHTTPSRedirect(portHTTPS))
httpsSrv := &http.Server{
Addr: net.JoinHostPort(cfg.Address, cfg.HTTPS.Port),
ReadHeaderTimeout: 5 * time.Second,
WriteTimeout: 10 * time.Second,
IdleTimeout: 120 * time.Second,
Handler: d,
}
server := &http.Server{
Addr: net.JoinHostPort(addr, portHTTPS),
Handler: d,
}
redirect := createHTTPSRedirect(cfg.HTTPS.Port)
if d.certExists() {
log.Println("[HTTPS] Listening on port", portHTTPS)
server.ListenAndServeTLS(cfg.HTTPS.Cert, cfg.HTTPS.Key)
} else if domain := cfg.LetsEncrypt.Domain; domain != "" {
dir := storage.Path.LetsEncrypt()
email := cfg.LetsEncrypt.Email
lePort := cfg.LetsEncrypt.Port
httpSrv.Handler = redirect
log.Println("[HTTP] Listening on port", port, "(HTTPS Redirect)")
go httpSrv.ListenAndServe()
if cfg.LetsEncrypt.Proxy && lePort != "" && (port != "80" || !redirect) {
log.Println("[HTTP] Listening on port 80 (Let's Encrypt Proxy))")
go http.ListenAndServe(net.JoinHostPort(addr, "80"), http.HandlerFunc(d.letsEncryptProxy))
log.Println("[HTTPS] Listening on port", cfg.HTTPS.Port)
log.Fatal(httpsSrv.ListenAndServeTLS(cfg.HTTPS.Cert, cfg.HTTPS.Key))
} else {
cache := certmagic.NewCache(certmagic.FileStorage{
Path: storage.Path.LetsEncrypt(),
})
magic := certmagic.NewWithCache(cache, certmagic.Config{
Agreed: true,
Email: cfg.LetsEncrypt.Email,
MustStaple: true,
})
domains := []string{cfg.LetsEncrypt.Domain}
if cfg.LetsEncrypt.Domain == "" {
domains = []string{}
magic.OnDemand = &certmagic.OnDemandConfig{MaxObtain: 3}
}
le, err := letsencrypt.Run(dir, domain, email, ":"+lePort)
err := magic.Manage(domains)
if err != nil {
log.Fatal(err)
}
server.TLSConfig = &tls.Config{
GetCertificate: le.GetCertificate,
tlsConfig := magic.TLSConfig()
tlsConfig.MinVersion = tls.VersionTLS12
tlsConfig.CipherSuites = getCipherSuites()
tlsConfig.CurvePreferences = []tls.CurveID{
tls.X25519,
tls.CurveP256,
}
tlsConfig.PreferServerCipherSuites = true
httpsSrv.TLSConfig = tlsConfig
log.Println("[HTTPS] Listening on port", portHTTPS)
log.Fatal(server.ListenAndServeTLS("", ""))
} else {
log.Fatal("Could not locate SSL certificate or private key")
httpSrv.Handler = magic.HTTPChallengeHandler(redirect)
log.Println("[HTTP] Listening on port", port, "(HTTPS Redirect)")
go httpSrv.ListenAndServe()
log.Println("[HTTPS] Listening on port", cfg.HTTPS.Port)
log.Fatal(httpsSrv.ListenAndServeTLS("", ""))
}
} else {
if cfg.Dev {
// The node dev server will proxy index page requests and
// websocket connections to this port
port = "1337"
}
httpSrv.ReadHeaderTimeout = 5 * time.Second
httpSrv.WriteTimeout = 10 * time.Second
httpSrv.IdleTimeout = 120 * time.Second
httpSrv.Handler = d
log.Println(httpSrv.Addr)
log.Println("[HTTP] Listening on port", port)
log.Fatal(http.ListenAndServe(net.JoinHostPort(addr, port), d))
log.Fatal(httpSrv.ListenAndServe())
}
}
@ -229,13 +258,8 @@ func (d *Dispatch) upgradeWS(w http.ResponseWriter, r *http.Request, state *Stat
newWSHandler(conn, state, r).run()
}
func (d *Dispatch) createHTTPSRedirect(portHTTPS string) http.HandlerFunc {
func createHTTPSRedirect(portHTTPS string) http.HandlerFunc {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if strings.HasPrefix(r.URL.Path, "/.well-known/acme-challenge") {
d.letsEncryptProxy(w, r)
return
}
host, _, err := net.SplitHostPort(r.Host)
if err != nil {
host = r.Host
@ -247,25 +271,12 @@ func (d *Dispatch) createHTTPSRedirect(portHTTPS string) http.HandlerFunc {
Path: r.RequestURI,
}
w.Header().Set("Connection", "close")
w.Header().Set("Location", u.String())
w.WriteHeader(http.StatusMovedPermanently)
})
}
func (d *Dispatch) letsEncryptProxy(w http.ResponseWriter, r *http.Request) {
host, _, err := net.SplitHostPort(r.Host)
if err != nil {
host = r.Host
}
upstream := &url.URL{
Scheme: "http",
Host: net.JoinHostPort(host, d.Config().LetsEncrypt.Port),
}
httputil.NewSingleHostReverseProxy(upstream).ServeHTTP(w, r)
}
func fail(w http.ResponseWriter, code int) {
http.Error(w, http.StatusText(code), code)
}

View file

@ -1,9 +1,35 @@
package server
import (
"crypto/tls"
"os"
"github.com/klauspost/cpuid"
)
func getCipherSuites() []uint16 {
if cpuid.CPU.AesNi() {
return []uint16{
tls.TLS_FALLBACK_SCSV,
tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305,
tls.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305,
}
}
return []uint16{
tls.TLS_FALLBACK_SCSV,
tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305,
tls.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305,
tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256}
}
func (d *Dispatch) certExists() bool {
cfg := d.Config().HTTPS