Skip to content

Commit

Permalink
tls: enable the selection of more TLS settings
Browse files Browse the repository at this point in the history
Signed-off-by: Julien Pivotto <[email protected]>
  • Loading branch information
roidelapluie committed May 3, 2020
1 parent 0c53298 commit 00c01b0
Show file tree
Hide file tree
Showing 14 changed files with 488 additions and 20 deletions.
73 changes: 70 additions & 3 deletions https/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,17 +15,47 @@ The config file should be written in YAML format, and is reloaded on each connec

```
tls_config:
# Certificate and key files for server to use to authenticate to client
# Certificate and key files for server to use to authenticate to client.
cert_file: <filename>
key_file: <filename>
# Server policy for client authentication. Maps to ClientAuth Policies
# Server policy for client authentication. Maps to ClientAuth Policies.
# For more detail on clientAuth options: [ClientAuthType](https://golang.org/pkg/crypto/tls/#ClientAuthType)
[ client_auth_type: <string> | default = "NoClientCert" ]
# CA certificate for client certificate authentication to the server
# CA certificate for client certificate authentication to the server.
[ client_ca_file: <filename> ]
# Minimum TLS version that is acceptable.
[ min_version: <string> | default = "TLS1.2" ]
# Maximum TLS version that is acceptable.
[ max_version: <string> | default = "TLS1.3" ]
# List of supported cipher suites for TLS versions up to TLS 1.2. If empty,
# Go default cipher suites are used. Available cipher suites are documented
# below.
[ cipher_suites:
- <string> ] ]
# Disable HTTP/2 support. This can not be toggled on the fly.
[ disable_http2: <bool> | default = false ]
# prefer_server_cipher_suites controls whether the server selects the
# client's most preferred ciphersuite, or the server's most preferred
# ciphersuite. If true then the server's preference, as expressed in
# the order of elements in CipherSuites, is used.
[ prefer_server_cipher_suites: <bool> | default = false ]
# session_tickets_disabled may be set to true to disable session ticket and
# PSK (resumption) support.
[ session_tickets_disabled: <bool> | default = false ]
# Elliptic curves that will be used in an ECDHE handshake, in preference
# order. Available supported groups are documented below.
[ supported_groups:
- <string> ] ]
# List of usernames and hashed passwords that have full access to the web
# server via basic authentication. If empty, no basic authentication is
# required. Passwords are hashed with bcrypt.
Expand All @@ -49,3 +79,40 @@ hash. A higher cost will en up slowing down the authentication process.
Depending on the machine, a cost of 10 will take about ~70ms where a cost of
18 can take up to a few seconds. That hash will be computed on every
password-protected request.


## Available cipher suites

List of available cipher suites for TLS up to 1.2 (we only permit
ciphersuites marked by go as 'secure'):

- TLS_RSA_WITH_3DES_EDE_CBC_SHA
- TLS_RSA_WITH_AES_128_CBC_SHA
- TLS_RSA_WITH_AES_256_CBC_SHA
- TLS_RSA_WITH_AES_128_GCM_SHA256
- TLS_RSA_WITH_AES_256_GCM_SHA384
- TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA
- TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA
- TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA
- TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA
- TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA
- TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256
- TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384
- TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
- TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384
- TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256
- TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256

List of available cipher suites for TLS 1.3 (TLS 1.3 ciphersuites are not
configurable):

- TLS_AES_128_GCM_SHA256
- TLS_AES_256_GCM_SHA384
- TLS_CHACHA20_POLY1305_SHA256

## Available supported groups

- CurveP256
- CurveP384
- CurveP521
- X25519
26 changes: 26 additions & 0 deletions https/testdata/tls_config_noAuth_allCiphers.good.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
tls_config :
cert_file : "testdata/server.crt"
key_file : "testdata/server.key"
client_auth_type : "VerifyClientCertIfGiven"
client_ca_file : "testdata/tls-ca-chain.pem"
cipher_suites:
- TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256
- TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384
- TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
- TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384
- TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256
- TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256
- TLS_AES_128_GCM_SHA256
- TLS_AES_256_GCM_SHA384
- TLS_CHACHA20_POLY1305_SHA256
- TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA
- TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA
- TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA
- TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA
- TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA
- TLS_RSA_WITH_3DES_EDE_CBC_SHA
- TLS_RSA_WITH_AES_128_CBC_SHA
- TLS_RSA_WITH_AES_256_CBC_SHA
- TLS_RSA_WITH_AES_128_GCM_SHA256
- TLS_RSA_WITH_AES_256_GCM_SHA384

10 changes: 10 additions & 0 deletions https/testdata/tls_config_noAuth_allCurves.good.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
tls_config :
cert_file : "testdata/server.crt"
key_file : "testdata/server.key"
client_auth_type : "VerifyClientCertIfGiven"
client_ca_file : "testdata/tls-ca-chain.pem"
supported_groups:
- CurveP256
- CurveP384
- CurveP521
- X25519
8 changes: 8 additions & 0 deletions https/testdata/tls_config_noAuth_inventedCiphers.bad.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
tls_config :
cert_file : "testdata/server.crt"
key_file : "testdata/server.key"
client_auth_type : "VerifyClientCertIfGiven"
client_ca_file : "testdata/tls-ca-chain.pem"
cipher_suites:
- TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA2048

7 changes: 7 additions & 0 deletions https/testdata/tls_config_noAuth_inventedCurves.bad.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
tls_config :
cert_file : "testdata/server.crt"
key_file : "testdata/server.key"
client_auth_type : "VerifyClientCertIfGiven"
client_ca_file : "testdata/tls-ca-chain.pem"
supported_groups:
- CurveP257
9 changes: 9 additions & 0 deletions https/testdata/tls_config_noAuth_noHTTP2.good.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
tls_config :
cert_file : "testdata/server.crt"
key_file : "testdata/server.key"
client_auth_type : "VerifyClientCertIfGiven"
client_ca_file : "testdata/tls-ca-chain.pem"
cipher_suites:
- TLS_RSA_WITH_AES_128_CBC_SHA
max_version: TLS1.2
disable_http2: true
8 changes: 8 additions & 0 deletions https/testdata/tls_config_noAuth_noHTTP2Cipher.bad.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
tls_config :
cert_file : "testdata/server.crt"
key_file : "testdata/server.key"
client_auth_type : "VerifyClientCertIfGiven"
client_ca_file : "testdata/tls-ca-chain.pem"
cipher_suites:
- TLS_RSA_WITH_AES_128_CBC_SHA
max_version: TLS1.2
11 changes: 11 additions & 0 deletions https/testdata/tls_config_noAuth_someCiphers.good.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
tls_config :
cert_file : "testdata/server.crt"
key_file : "testdata/server.key"
client_auth_type : "VerifyClientCertIfGiven"
client_ca_file : "testdata/tls-ca-chain.pem"
cipher_suites:
- TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384
- TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
- TLS_CHACHA20_POLY1305_SHA256
min_version: TLS1.2
max_version: TLS1.2
8 changes: 8 additions & 0 deletions https/testdata/tls_config_noAuth_someCurves.good.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
tls_config :
cert_file : "testdata/server.crt"
key_file : "testdata/server.key"
client_auth_type : "VerifyClientCertIfGiven"
client_ca_file : "testdata/tls-ca-chain.pem"
min_version: TLS1.3
supported_groups:
- CurveP521
43 changes: 43 additions & 0 deletions https/tls_ciphers.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
// Copyright 2020 The Prometheus Authors
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// +build go1.14

package https

import (
"crypto/tls"
"strings"

"github.com/pkg/errors"
)

func (c *cipher) UnmarshalYAML(unmarshal func(interface{}) error) error {
var s string
err := unmarshal((*string)(&s))
if err != nil {
return err
}
s = strings.ToUpper(s)
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
}
109 changes: 100 additions & 9 deletions https/tls_config.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,10 @@ package https
import (
"crypto/tls"
"crypto/x509"
"fmt"
"io/ioutil"
"net/http"
"strings"

"github.com/go-kit/kit/log"
"github.com/go-kit/kit/log/level"
Expand All @@ -37,10 +39,17 @@ type Config struct {
}

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"`
CurveIDs []curve `yaml:"supported_groups"`
MinVersion string `yaml:"min_version"`
MaxVersion string `yaml:"max_version"`
PreferServerCipherSuites bool `yaml:"prefer_server_cipher_suites"`
DisableHTTP2 bool `yaml:"disable_http2"`
SessionTicketsDisabled bool `yaml:"session_tickets_disabled"`
}

func getConfig(configPath string) (*Config, error) {
Expand Down Expand Up @@ -73,16 +82,32 @@ func ConfigToTLSConfig(c *TLSStruct) (*tls.Config, error) {
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 {
return nil, errors.Wrap(err, "failed to load X509KeyPair")
}
return &cert, nil
}

minVersion, err := pickMinVersion(c.MinVersion)
if err != nil {
return nil, err
}

maxVersion, err := pickMaxVersion(c.MaxVersion)
if err != nil {
return nil, err
}

cfg := &tls.Config{
MinVersion: minVersion,
MaxVersion: maxVersion,
PreferServerCipherSuites: c.PreferServerCipherSuites,
SessionTicketsDisabled: c.SessionTicketsDisabled,
}

// Confirm that certificate and key paths are valid.
if _, err := loadCert(); err != nil {
return nil, err
Expand All @@ -91,6 +116,22 @@ func ConfigToTLSConfig(c *TLSStruct) (*tls.Config, error) {
return loadCert()
}

var cf []uint16
for _, c := range c.CipherSuites {
cf = append(cf, (uint16)(c))
}
if len(cf) > 0 {
cfg.CipherSuites = cf
}

var sg []tls.CurveID
for _, c := range c.CurveIDs {
sg = append(sg, (tls.CurveID)(c))
}
if len(sg) > 0 {
cfg.CurvePreferences = sg
}

if c.ClientCAs != "" {
clientCAPool := x509.NewCertPool()
clientCAFile, err := ioutil.ReadFile(c.ClientCAs)
Expand Down Expand Up @@ -145,11 +186,20 @@ 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:
withHTTP2 := "with HTTP/2 support"
if c.TLSConfig.DisableHTTP2 {
withHTTP2 = "without HTTP/2 support"
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 "+withHTTP2+" is enabled and it cannot be disabled on the fly.")
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.")
Expand All @@ -168,3 +218,44 @@ func Listen(server *http.Server, tlsConfigPath string, logger log.Logger) error
}
return server.ListenAndServeTLS("", "")
}

// For go1.14, unmarshalling is done properly in tls_ciphers.go.
type cipher uint16

type curve tls.CurveID

func (c *curve) UnmarshalYAML(unmarshal func(interface{}) error) error {
var s string
err := unmarshal((*string)(&s))
if err != nil {
return err
}
switch strings.ToUpper(s) {
case "CURVEP256":
*c = (curve)(tls.CurveP256)
case "CURVEP384":
*c = (curve)(tls.CurveP384)
case "CURVEP521":
*c = (curve)(tls.CurveP521)
case "X25519":
*c = (curve)(tls.X25519)
default:
return errors.New("unknown supported group: " + s)
}
return nil
}

func (c curve) MarshalYAML() (interface{}, error) {
switch c {
case (curve)(tls.CurveP256):
return "CurveP256", nil
case (curve)(tls.CurveP384):
return "CurveP384", nil
case (curve)(tls.CurveP521):
return "CurveP521", nil
case (curve)(tls.X25519):
return "X25519", nil
default:
return fmt.Sprintf("%v", c), nil
}
}
Loading

0 comments on commit 00c01b0

Please sign in to comment.