Add TLS configuration
Golang HTTPS server isn't secure by default. This commit introduces TLS minimum version and ciphers list to set up a secure TLS service. Signed-off-by: Julien Riou <julien@riou.xyz>
This commit is contained in:
parent
0023bb52ef
commit
be00ca79c0
4 changed files with 113 additions and 17 deletions
2
VERSION
2
VERSION
|
@ -1 +1 @@
|
||||||
1.0.0
|
1.1.0
|
|
@ -4,6 +4,14 @@ frontend:
|
||||||
port: 8443
|
port: 8443
|
||||||
certfile: /path/to/certificate.pem
|
certfile: /path/to/certificate.pem
|
||||||
keyfile: /pat/to/keyfile.key
|
keyfile: /pat/to/keyfile.key
|
||||||
|
tls-ciphers:
|
||||||
|
- TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384
|
||||||
|
- TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384
|
||||||
|
- TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256
|
||||||
|
- TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
|
||||||
|
- TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256
|
||||||
|
- TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256
|
||||||
|
tls-min-version: TLSv1.1
|
||||||
backend:
|
backend:
|
||||||
host: 127.0.0.1
|
host: 127.0.0.1
|
||||||
port: 8008
|
port: 8008
|
||||||
|
|
|
@ -17,11 +17,13 @@ type Config struct {
|
||||||
|
|
||||||
// FrontendConfig for storing Frontend settings
|
// FrontendConfig for storing Frontend settings
|
||||||
type FrontendConfig struct {
|
type FrontendConfig struct {
|
||||||
Host string `yaml:"host"`
|
Host string `yaml:"host"`
|
||||||
Port int `yaml:"port"`
|
Port int `yaml:"port"`
|
||||||
Certfile string `yaml:"certfile"`
|
Certfile string `yaml:"certfile"`
|
||||||
Keyfile string `yaml:"keyfile"`
|
Keyfile string `yaml:"keyfile"`
|
||||||
LogFormat string `yaml:"logformat"`
|
TLSMinVersion string `yaml:"tls-min-version"`
|
||||||
|
TLSCiphers []string `yaml:"tls-ciphers"`
|
||||||
|
LogFormat string `yaml:"logformat"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// BackendConfig for storing Backend settings
|
// BackendConfig for storing Backend settings
|
||||||
|
|
108
src/frontend.go
108
src/frontend.go
|
@ -1,6 +1,7 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"crypto/tls"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
@ -11,11 +12,13 @@ import (
|
||||||
|
|
||||||
// Frontend exposes statuses over HTTP(S)
|
// Frontend exposes statuses over HTTP(S)
|
||||||
type Frontend struct {
|
type Frontend struct {
|
||||||
backend Backend
|
backend Backend
|
||||||
host string
|
host string
|
||||||
port int
|
port int
|
||||||
certfile string
|
certfile string
|
||||||
keyfile string
|
keyfile string
|
||||||
|
tlsMinVersion string
|
||||||
|
tlsCiphers []string
|
||||||
}
|
}
|
||||||
|
|
||||||
var backend Backend
|
var backend Backend
|
||||||
|
@ -35,10 +38,30 @@ func (f *Frontend) Start() error {
|
||||||
|
|
||||||
Info("listening on %s", f)
|
Info("listening on %s", f)
|
||||||
var err error
|
var err error
|
||||||
|
server := &http.Server{
|
||||||
|
Addr: f.String(),
|
||||||
|
Handler: r,
|
||||||
|
}
|
||||||
if f.certfile != "" && f.keyfile != "" {
|
if f.certfile != "" && f.keyfile != "" {
|
||||||
err = http.ListenAndServeTLS(f.String(), f.certfile, f.keyfile, r)
|
config := &tls.Config{}
|
||||||
|
if f.tlsMinVersion != "" {
|
||||||
|
config.MinVersion, err = parseTLSVersion(f.tlsMinVersion)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(f.tlsCiphers) > 0 {
|
||||||
|
ciphers, err := parseCiphersSuite(f.tlsCiphers)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
config.CipherSuites = ciphers
|
||||||
|
}
|
||||||
|
|
||||||
|
server.TLSConfig = config
|
||||||
|
err = server.ListenAndServeTLS(f.certfile, f.keyfile)
|
||||||
} else {
|
} else {
|
||||||
err = http.ListenAndServe(f.String(), r)
|
err = server.ListenAndServe()
|
||||||
}
|
}
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -57,10 +80,12 @@ func NewFrontend(config FrontendConfig, b Backend) (*Frontend, error) {
|
||||||
backend = b
|
backend = b
|
||||||
logFormat = config.LogFormat
|
logFormat = config.LogFormat
|
||||||
return &Frontend{
|
return &Frontend{
|
||||||
host: config.Host,
|
host: config.Host,
|
||||||
port: config.Port,
|
port: config.Port,
|
||||||
certfile: config.Certfile,
|
certfile: config.Certfile,
|
||||||
keyfile: config.Keyfile,
|
keyfile: config.Keyfile,
|
||||||
|
tlsMinVersion: config.TLSMinVersion,
|
||||||
|
tlsCiphers: config.TLSCiphers,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -140,3 +165,64 @@ func ReplicaHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
w.WriteHeader(status)
|
w.WriteHeader(status)
|
||||||
io.WriteString(w, message)
|
io.WriteString(w, message)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Store TLS ciphers map from string to constant
|
||||||
|
// See full list at https://golang.org/pkg/crypto/tls/#pkg-constants
|
||||||
|
var tlsCiphers = map[string]uint16{
|
||||||
|
"TLS_RSA_WITH_RC4_128_SHA": tls.TLS_RSA_WITH_RC4_128_SHA,
|
||||||
|
"TLS_RSA_WITH_3DES_EDE_CBC_SHA": tls.TLS_RSA_WITH_3DES_EDE_CBC_SHA,
|
||||||
|
"TLS_RSA_WITH_AES_128_CBC_SHA": tls.TLS_RSA_WITH_AES_128_CBC_SHA,
|
||||||
|
"TLS_RSA_WITH_AES_256_CBC_SHA": tls.TLS_RSA_WITH_AES_256_CBC_SHA,
|
||||||
|
"TLS_RSA_WITH_AES_128_CBC_SHA256": tls.TLS_RSA_WITH_AES_128_CBC_SHA256,
|
||||||
|
"TLS_RSA_WITH_AES_128_GCM_SHA256": tls.TLS_RSA_WITH_AES_128_GCM_SHA256,
|
||||||
|
"TLS_RSA_WITH_AES_256_GCM_SHA384": tls.TLS_RSA_WITH_AES_256_GCM_SHA384,
|
||||||
|
"TLS_ECDHE_ECDSA_WITH_RC4_128_SHA": tls.TLS_ECDHE_ECDSA_WITH_RC4_128_SHA,
|
||||||
|
"TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA": tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,
|
||||||
|
"TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA": tls.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,
|
||||||
|
"TLS_ECDHE_RSA_WITH_RC4_128_SHA": tls.TLS_ECDHE_RSA_WITH_RC4_128_SHA,
|
||||||
|
"TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA": tls.TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA,
|
||||||
|
"TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA": tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,
|
||||||
|
"TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA": tls.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,
|
||||||
|
"TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256": tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256,
|
||||||
|
"TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256": tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256,
|
||||||
|
"TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256": tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
|
||||||
|
"TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256": tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
|
||||||
|
"TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384": tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
|
||||||
|
"TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384": tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
|
||||||
|
"TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305": tls.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305,
|
||||||
|
"TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305": tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305,
|
||||||
|
"TLS_AES_128_GCM_SHA256": tls.TLS_AES_128_GCM_SHA256,
|
||||||
|
"TLS_AES_256_GCM_SHA384": tls.TLS_AES_256_GCM_SHA384,
|
||||||
|
"TLS_CHACHA20_POLY1305_SHA256": tls.TLS_CHACHA20_POLY1305_SHA256,
|
||||||
|
"TLS_FALLBACK_SCSV": tls.TLS_FALLBACK_SCSV,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert a list of ciphers from string to TLS constants
|
||||||
|
func parseCiphersSuite(strings []string) (ciphers []uint16, err error) {
|
||||||
|
for _, s := range strings {
|
||||||
|
if cipher, ok := tlsCiphers[s]; ok {
|
||||||
|
ciphers = append(ciphers, cipher)
|
||||||
|
} else {
|
||||||
|
return nil, fmt.Errorf("unknown cipher detected: %s", s)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ciphers, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Store TLS versions map from string to constant
|
||||||
|
// See full list at https://golang.org/pkg/crypto/tls/#pkg-constants
|
||||||
|
var tlsVersions = map[string]uint16{
|
||||||
|
"SSLv3.0": tls.VersionSSL30,
|
||||||
|
"TLSv1.0": tls.VersionTLS10,
|
||||||
|
"TLSv1.1": tls.VersionTLS11,
|
||||||
|
"TLSv1.2": tls.VersionTLS12,
|
||||||
|
"TLSv1.3": tls.VersionTLS13,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert a list of ciphers from string to TLS constants
|
||||||
|
func parseTLSVersion(s string) (uint16, error) {
|
||||||
|
if version, ok := tlsVersions[s]; ok {
|
||||||
|
return version, nil
|
||||||
|
}
|
||||||
|
return 0, fmt.Errorf("unknown TLS version: %s", s)
|
||||||
|
}
|
||||||
|
|
Reference in a new issue