tls: enable the selection of more TLS settings (#1695)

tls: enable the selection of more TLS settings
* Rename `tls_config` to `tls_server_config`.
* Add new http server config with HTTP/2 enabled by default.

Signed-off-by: Julien Pivotto <roidelapluie@inuits.eu>
This commit is contained in:
Julien Pivotto 2020-05-13 20:26:01 +02:00 committed by GitHub
commit f87e566df9
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
27 changed files with 433 additions and 40 deletions

View file

@ -17,6 +17,7 @@ package https
import (
"crypto/tls"
"crypto/x509"
"fmt"
"io/ioutil"
"net/http"
@ -32,15 +33,25 @@ var (
)
type Config struct {
TLSConfig TLSStruct `yaml:"tls_config"`
Users map[string]config_util.Secret `yaml:"basic_auth_users"`
TLSConfig TLSStruct `yaml:"tls_server_config"`
HTTPConfig HTTPStruct `yaml:"http_server_config"`
Users map[string]config_util.Secret `yaml:"basic_auth_users"`
}
type TLSStruct struct {
TLSCertPath string `yaml:"cert_file"`
TLSKeyPath string `yaml:"key_file"`
ClientAuth string `yaml:"client_auth_type"`
ClientCAs string `yaml:"client_ca_file"`
TLSCertPath string `yaml:"cert_file"`
TLSKeyPath string `yaml:"key_file"`
ClientAuth string `yaml:"client_auth_type"`
ClientCAs string `yaml:"client_ca_file"`
CipherSuites []cipher `yaml:"cipher_suites"`
CurvePreferences []curve `yaml:"curve_preferences"`
MinVersion tlsVersion `yaml:"min_version"`
MaxVersion tlsVersion `yaml:"max_version"`
PreferServerCipherSuites bool `yaml:"prefer_server_cipher_suites"`
}
type HTTPStruct struct {
HTTP2 bool `yaml:"http2"`
}
func getConfig(configPath string) (*Config, error) {
@ -48,7 +59,14 @@ func getConfig(configPath string) (*Config, error) {
if err != nil {
return nil, err
}
c := &Config{}
c := &Config{
TLSConfig: TLSStruct{
MinVersion: tls.VersionTLS12,
MaxVersion: tls.VersionTLS13,
PreferServerCipherSuites: true,
},
HTTPConfig: HTTPStruct{HTTP2: true},
}
err = yaml.UnmarshalStrict(content, c)
return c, err
}
@ -70,12 +88,11 @@ func ConfigToTLSConfig(c *TLSStruct) (*tls.Config, error) {
if c.TLSCertPath == "" {
return nil, errors.New("missing cert_file")
}
if c.TLSKeyPath == "" {
return nil, errors.New("missing key_file")
}
cfg := &tls.Config{
MinVersion: tls.VersionTLS12,
}
loadCert := func() (*tls.Certificate, error) {
cert, err := tls.LoadX509KeyPair(c.TLSCertPath, c.TLSKeyPath)
if err != nil {
@ -83,14 +100,38 @@ func ConfigToTLSConfig(c *TLSStruct) (*tls.Config, error) {
}
return &cert, nil
}
// Confirm that certificate and key paths are valid.
if _, err := loadCert(); err != nil {
return nil, err
}
cfg := &tls.Config{
MinVersion: (uint16)(c.MinVersion),
MaxVersion: (uint16)(c.MaxVersion),
PreferServerCipherSuites: c.PreferServerCipherSuites,
}
cfg.GetCertificate = func(*tls.ClientHelloInfo) (*tls.Certificate, error) {
return loadCert()
}
var cf []uint16
for _, c := range c.CipherSuites {
cf = append(cf, (uint16)(c))
}
if len(cf) > 0 {
cfg.CipherSuites = cf
}
var cp []tls.CurveID
for _, c := range c.CurvePreferences {
cp = append(cp, (tls.CurveID)(c))
}
if len(cp) > 0 {
cfg.CurvePreferences = cp
}
if c.ClientCAs != "" {
clientCAPool := x509.NewCertPool()
clientCAFile, err := ioutil.ReadFile(c.ClientCAs)
@ -126,7 +167,7 @@ func ConfigToTLSConfig(c *TLSStruct) (*tls.Config, error) {
// Listen starts the server on the given address. If tlsConfigPath isn't empty the server connection will be started using TLS.
func Listen(server *http.Server, tlsConfigPath string, logger log.Logger) error {
if tlsConfigPath == "" {
level.Info(logger).Log("msg", "TLS is disabled and it cannot be enabled on the fly.")
level.Info(logger).Log("msg", "TLS is disabled and it cannot be enabled on the fly.", "http2", false)
return server.ListenAndServe()
}
@ -145,14 +186,21 @@ func Listen(server *http.Server, tlsConfigPath string, logger log.Logger) error
handler: handler,
}
config, err := getTLSConfig(tlsConfigPath)
c, err := getConfig(tlsConfigPath)
if err != nil {
return err
}
config, err := ConfigToTLSConfig(&c.TLSConfig)
switch err {
case nil:
if !c.HTTPConfig.HTTP2 {
server.TLSNextProto = make(map[string]func(*http.Server, *tls.Conn, http.Handler))
}
// Valid TLS config.
level.Info(logger).Log("msg", "TLS is enabled and it cannot be disabled on the fly.")
level.Info(logger).Log("msg", "TLS is enabled and it cannot be disabled on the fly.", "http2", c.HTTPConfig.HTTP2)
case errNoTLSConfig:
// No TLS config, back to plain HTTP.
level.Info(logger).Log("msg", "TLS is disabled and it cannot be enabled on the fly.")
level.Info(logger).Log("msg", "TLS is disabled and it cannot be enabled on the fly.", "http2", false)
return server.ListenAndServe()
default:
// Invalid TLS config.
@ -168,3 +216,86 @@ func Listen(server *http.Server, tlsConfigPath string, logger log.Logger) error
}
return server.ListenAndServeTLS("", "")
}
type cipher uint16
func (c *cipher) UnmarshalYAML(unmarshal func(interface{}) error) error {
var s string
err := unmarshal((*string)(&s))
if err != nil {
return err
}
for _, cs := range tls.CipherSuites() {
if cs.Name == s {
*c = (cipher)(cs.ID)
return nil
}
}
return errors.New("unknown cipher: " + s)
}
func (c cipher) MarshalYAML() (interface{}, error) {
return tls.CipherSuiteName((uint16)(c)), nil
}
type curve tls.CurveID
var curves = map[string]curve{
"CurveP256": (curve)(tls.CurveP256),
"CurveP384": (curve)(tls.CurveP384),
"CurveP521": (curve)(tls.CurveP521),
"X25519": (curve)(tls.X25519),
}
func (c *curve) UnmarshalYAML(unmarshal func(interface{}) error) error {
var s string
err := unmarshal((*string)(&s))
if err != nil {
return err
}
if curveid, ok := curves[s]; ok {
*c = curveid
return nil
}
return errors.New("unknown curve: " + s)
}
func (c *curve) MarshalYAML() (interface{}, error) {
for s, curveid := range curves {
if *c == curveid {
return s, nil
}
}
return fmt.Sprintf("%v", c), nil
}
type tlsVersion uint16
var tlsVersions = map[string]tlsVersion{
"TLS13": (tlsVersion)(tls.VersionTLS13),
"TLS12": (tlsVersion)(tls.VersionTLS12),
"TLS11": (tlsVersion)(tls.VersionTLS11),
"TLS10": (tlsVersion)(tls.VersionTLS10),
}
func (tv *tlsVersion) UnmarshalYAML(unmarshal func(interface{}) error) error {
var s string
err := unmarshal((*string)(&s))
if err != nil {
return err
}
if v, ok := tlsVersions[s]; ok {
*tv = v
return nil
}
return errors.New("unknown TLS version: " + s)
}
func (tv *tlsVersion) MarshalYAML() (interface{}, error) {
for s, v := range tlsVersions {
if *tv == v {
return s, nil
}
}
return fmt.Sprintf("%v", tv), nil
}