agent: convert listener config to TLS types (#12522)

* tlsutil: initial implementation of types/TLSVersion

tlsutil: add test for parsing deprecated agent TLS version strings

tlsutil: return TLSVersionInvalid with error

tlsutil: start moving tlsutil cipher suite lookups over to types/tls

tlsutil: rename tlsLookup to ParseTLSVersion, add cipherSuiteLookup

agent: attempt to use types in runtime config

agent: implement b.tlsVersion validation in config builder

agent: fix tlsVersion nil check in builder

tlsutil: update to renamed ParseTLSVersion and goTLSVersions

tlsutil: fixup TestConfigurator_CommonTLSConfigTLSMinVersion

tlsutil: disable invalid config parsing tests

tlsutil: update tests

auto_config: lookup old config strings from base.TLSMinVersion

auto_config: update endpoint tests to use TLS types

agent: update runtime_test to use TLS types

agent: update TestRuntimeCinfig_Sanitize.golden

agent: update config runtime tests to expect TLS types

* website: update Consul agent tls_min_version values

* agent: fixup TLS parsing and compilation errors

* test: fixup lint issues in agent/config_runtime_test and tlsutil/config_test

* tlsutil: add CHACHA20_POLY1305 cipher suites to goTLSCipherSuites

* test: revert autoconfig tls min version fixtures to old format

* types: add TLSVersions public function

* agent: add warning for deprecated TLS version strings

* agent: move agent config specific logic from tlsutil.ParseTLSVersion into agent config builder

* tlsutil(BREAKING): change default TLS min version to TLS 1.2

* agent: move ParseCiphers logic from tlsutil into agent config builder

* tlsutil: remove unused CipherString function

* agent: fixup import for types package

* Revert "tlsutil: remove unused CipherString function"

This reverts commit 6ca7f6f58d268e617501b7db9500113c13bae70c.

* agent: fixup config builder and runtime tests

* tlsutil: fixup one remaining ListenerConfig -> ProtocolConfig

* test: move TLS cipher suites parsing test from tlsutil into agent config builder tests

* agent: remove parseCiphers helper from auto_config_endpoint_test

* test: remove unused imports from tlsutil

* agent: remove resolved FIXME comment

* tlsutil: remove TODO and FIXME in cipher suite validation

* agent: prevent setting inherited cipher suite config when TLS 1.3 is specified

* changelog: add entry for converting agent config to TLS types

* agent: remove FIXME in runtime test, this is covered in builder tests with invalid tls9 value now

* tlsutil: remove config tests for values checked at agent config builder boundary

* tlsutil: remove tls version check from loadProtocolConfig

* tlsutil: remove tests and TODOs for logic checked in TestBuilder_tlsVersion and TestBuilder_tlsCipherSuites

* website: update search link for supported Consul agent cipher suites

* website: apply review suggestions for tls_min_version description

* website: attempt to clean up markdown list formatting for tls_min_version

* website: moar linebreaks to fix tls_min_version formatting

* Revert "website: moar linebreaks to fix tls_min_version formatting"

This reverts commit 38585927422f73ebf838a7663e566ac245f2a75c.

* autoconfig: translate old values for TLSMinVersion

* agent: rename var for translated value of deprecated TLS version value

* Update agent/config/deprecated.go

Co-authored-by: Dan Upton <daniel@floppy.co>

* agent: fix lint issue

* agent: fixup deprecated config test assertions for updated warning

Co-authored-by: Dan Upton <daniel@floppy.co>
This commit is contained in:
Mike Morris 2022-03-24 15:32:25 -04:00 committed by GitHub
parent 4d6229a3ab
commit 8020fb2098
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 318 additions and 242 deletions

15
.changelog/12522.txt Normal file
View File

@ -0,0 +1,15 @@
```release-note:deprecation
agent: deprecate older syntax for specifying TLS min version values
```
```release-note:deprecation
agent: remove support for specifying insecure TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256 and TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256 cipher suites
```
```release-note:enhancement
agent: add additional validation to TLS config
```
```release-note:enhancement
agent: bump default min version for connections to TLS 1.2
```
```release-note:enhancement
agent: add support for specifying TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256 and TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256 cipher suites
```

View File

@ -6,6 +6,7 @@ import (
"github.com/hashicorp/consul/proto/pbautoconf"
"github.com/hashicorp/consul/proto/pbconfig"
"github.com/hashicorp/consul/proto/pbconnect"
"github.com/hashicorp/consul/types"
)
// translateAgentConfig is meant to take in a proto/pbconfig.Config type
@ -83,9 +84,19 @@ func translateConfig(c *pbconfig.Config) config.Config {
if t := c.TLS; t != nil {
result.TLS.Defaults = config.TLSProtocolConfig{
VerifyOutgoing: &t.VerifyOutgoing,
TLSMinVersion: stringPtrOrNil(t.MinVersion),
TLSCipherSuites: stringPtrOrNil(t.CipherSuites),
}
// NOTE: This inner check for deprecated values should eventually be
// removed, and possibly replaced with a versioning scheme for autoconfig
// or a proper integration with the deprecated config handling in
// agent/config/deprecated.go
if v, ok := types.DeprecatedConsulAgentTLSVersions[t.MinVersion]; ok {
result.TLS.Defaults.TLSMinVersion = stringPtrOrNil(v.String())
} else {
result.TLS.Defaults.TLSMinVersion = stringPtrOrNil(t.MinVersion)
}
result.TLS.InternalRPC.VerifyServerHostname = &t.VerifyServerHostname
}

View File

@ -108,7 +108,7 @@ func TestTranslateConfig(t *testing.T) {
Defaults: config.TLSProtocolConfig{
VerifyOutgoing: boolPointer(true),
TLSCipherSuites: stringPointer("stuff"),
TLSMinVersion: stringPointer("tls13"),
TLSMinVersion: stringPointer("TLSv1_3"),
},
InternalRPC: config.TLSProtocolConfig{
VerifyServerHostname: boolPointer(true),

View File

@ -1971,15 +1971,63 @@ func (b *builder) cidrsVal(name string, v []string) (nets []*net.IPNet) {
return
}
func (b *builder) tlsCipherSuites(name string, v *string) []uint16 {
func (b *builder) tlsVersion(name string, v *string) types.TLSVersion {
// Handles unspecified config and empty string case.
//
// This check is not inside types.ValidateTLSVersionString because Envoy config
// distinguishes between an unset empty string which inherits parent config and
// an explicit TLS_AUTO which allows overriding parent config with the proxy
// defaults.
if v == nil || *v == "" {
return types.TLSVersionAuto
}
a := types.TLSVersion(*v)
err := types.ValidateTLSVersion(a)
if err != nil {
b.err = multierror.Append(b.err, fmt.Errorf("%s: invalid TLS version: %s", name, err))
return types.TLSVersionInvalid
}
return a
}
// validateTLSVersionCipherSuitesCompat checks that the specified TLS version supports
// specifying cipher suites
func validateTLSVersionCipherSuitesCompat(tlsMinVersion types.TLSVersion) error {
if tlsMinVersion == types.TLSv1_3 {
return fmt.Errorf("TLS 1.3 cipher suites are not configurable")
}
return nil
}
// tlsCipherSuites parses cipher suites from a comma-separated string into a
// recognized slice
func (b *builder) tlsCipherSuites(name string, v *string, tlsMinVersion types.TLSVersion) []types.TLSCipherSuite {
if v == nil {
return nil
}
var a []uint16
a, err := tlsutil.ParseCiphers(*v)
if err := validateTLSVersionCipherSuitesCompat(tlsMinVersion); err != nil {
b.err = multierror.Append(b.err, fmt.Errorf("%s: %s", name, err))
return nil
}
*v = strings.TrimSpace(*v)
if *v == "" {
return []types.TLSCipherSuite{}
}
ciphers := strings.Split(*v, ",")
a := make([]types.TLSCipherSuite, len(ciphers))
for i, cipher := range ciphers {
a[i] = types.TLSCipherSuite(cipher)
}
err := types.ValidateConsulAgentCipherSuites(a)
if err != nil {
b.err = multierror.Append(b.err, fmt.Errorf("%s: invalid tls cipher suites: %s", name, err))
b.err = multierror.Append(b.err, fmt.Errorf("%s: invalid TLS cipher suites: %s", name, err))
return []types.TLSCipherSuite{}
}
return a
}
@ -2477,14 +2525,14 @@ func (b *builder) buildTLSConfig(rt RuntimeConfig, t TLS) (tlsutil.Config, error
b.warn("tls.grpc was provided but TLS will NOT be enabled on the gRPC listener without an HTTPS listener configured (e.g. via ports.https)")
}
defaultCipherSuites := b.tlsCipherSuites("tls.defaults.tls_cipher_suites", t.Defaults.TLSCipherSuites)
defaultTLSMinVersion := b.tlsVersion("tls.defaults.tls_min_version", t.Defaults.TLSMinVersion)
defaultCipherSuites := b.tlsCipherSuites("tls.defaults.tls_cipher_suites", t.Defaults.TLSCipherSuites, defaultTLSMinVersion)
mapCommon := func(name string, src TLSProtocolConfig, dst *tlsutil.ProtocolConfig) {
dst.CAPath = stringValWithDefault(src.CAPath, stringVal(t.Defaults.CAPath))
dst.CAFile = stringValWithDefault(src.CAFile, stringVal(t.Defaults.CAFile))
dst.CertFile = stringValWithDefault(src.CertFile, stringVal(t.Defaults.CertFile))
dst.KeyFile = stringValWithDefault(src.KeyFile, stringVal(t.Defaults.KeyFile))
dst.TLSMinVersion = stringValWithDefault(src.TLSMinVersion, stringVal(t.Defaults.TLSMinVersion))
dst.VerifyIncoming = boolValWithDefault(src.VerifyIncoming, boolVal(t.Defaults.VerifyIncoming))
// We prevent this from being set explicity in the tls.grpc stanza above, but
@ -2494,12 +2542,26 @@ func (b *builder) buildTLSConfig(rt RuntimeConfig, t TLS) (tlsutil.Config, error
dst.VerifyOutgoing = boolValWithDefault(src.VerifyOutgoing, boolVal(t.Defaults.VerifyOutgoing))
}
if src.TLSMinVersion == nil {
dst.TLSMinVersion = defaultTLSMinVersion
} else {
dst.TLSMinVersion = b.tlsVersion(
fmt.Sprintf("tls.%s.tls_min_version", name),
src.TLSMinVersion,
)
}
if src.TLSCipherSuites == nil {
// If cipher suite config incompatible with a specified TLS min version
// would be inherited, omit it but don't return an error in the builder.
if validateTLSVersionCipherSuitesCompat(dst.TLSMinVersion) == nil {
dst.CipherSuites = defaultCipherSuites
}
} else {
dst.CipherSuites = b.tlsCipherSuites(
fmt.Sprintf("tls.%s.tls_cipher_suites", name),
src.TLSCipherSuites,
dst.TLSMinVersion,
)
}
}

View File

@ -12,6 +12,8 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/hashicorp/consul/types"
)
func TestLoad(t *testing.T) {
@ -327,3 +329,58 @@ func TestBuilder_ServiceVal_MultiError(t *testing.T) {
func intPtr(v int) *int {
return &v
}
func TestBuilder_tlsVersion(t *testing.T) {
b := builder{}
validTLSVersion := "TLSv1_3"
b.tlsVersion("tls.defaults.tls_min_version", &validTLSVersion)
deprecatedTLSVersion := "tls11"
b.tlsVersion("tls.defaults.tls_min_version", &deprecatedTLSVersion)
invalidTLSVersion := "tls9"
b.tlsVersion("tls.defaults.tls_min_version", &invalidTLSVersion)
require.Error(t, b.err)
require.Contains(t, b.err.Error(), "2 errors")
require.Contains(t, b.err.Error(), deprecatedTLSVersion)
require.Contains(t, b.err.Error(), invalidTLSVersion)
}
func TestBuilder_tlsCipherSuites(t *testing.T) {
b := builder{}
validCipherSuites := strings.Join([]string{
"TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256",
"TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA",
"TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256",
"TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA",
"TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384",
"TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256",
"TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA",
"TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
"TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA",
"TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384",
}, ",")
b.tlsCipherSuites("tls.defaults.tls_cipher_suites", &validCipherSuites, types.TLSv1_2)
require.NoError(t, b.err)
unsupportedCipherSuites := strings.Join([]string{
"TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256",
}, ",")
b.tlsCipherSuites("tls.defaults.tls_cipher_suites", &unsupportedCipherSuites, types.TLSv1_2)
invalidCipherSuites := strings.Join([]string{
"cipherX",
}, ",")
b.tlsCipherSuites("tls.defaults.tls_cipher_suites", &invalidCipherSuites, types.TLSv1_2)
b.tlsCipherSuites("tls.defaults.tls_cipher_suites", &validCipherSuites, types.TLSv1_3)
require.Error(t, b.err)
require.Contains(t, b.err.Error(), "3 errors")
require.Contains(t, b.err.Error(), unsupportedCipherSuites)
require.Contains(t, b.err.Error(), invalidCipherSuites)
require.Contains(t, b.err.Error(), "cipher suites are not configurable")
}

View File

@ -58,7 +58,7 @@ func DefaultSource() Source {
tls = {
defaults = {
tls_min_version = "tls12"
tls_min_version = "TLSv1_2"
}
}

View File

@ -2,6 +2,8 @@ package config
import (
"fmt"
"github.com/hashicorp/consul/types"
)
type DeprecatedConfig struct {
@ -219,8 +221,17 @@ func applyDeprecatedTLSConfig(dep DeprecatedConfig, cfg *Config) []string {
if v := dep.TLSMinVersion; v != nil {
if defaults.TLSMinVersion == nil {
// NOTE: This inner check for deprecated values should eventually be
// removed
if version, ok := types.DeprecatedConsulAgentTLSVersions[*v]; ok {
// Log warning about deprecated config values
warns = append(warns, fmt.Sprintf("'tls_min_version' value '%s' is deprecated, please specify '%s' instead", *v, version))
versionString := version.String()
defaults.TLSMinVersion = &versionString
} else {
defaults.TLSMinVersion = v
}
}
warns = append(warns, deprecationWarning("tls_min_version", "tls.defaults.tls_min_version"))
}

View File

@ -1,7 +1,7 @@
package config
import (
"crypto/tls"
"fmt"
"sort"
"testing"
"time"
@ -9,6 +9,7 @@ import (
"github.com/stretchr/testify/require"
"github.com/hashicorp/consul/tlsutil"
"github.com/hashicorp/consul/types"
)
func TestLoad_DeprecatedConfig(t *testing.T) {
@ -33,8 +34,8 @@ ca_file = "some-ca-file"
ca_path = "some-ca-path"
cert_file = "some-cert-file"
key_file = "some-key-file"
tls_cipher_suites = "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256"
tls_min_version = "some-tls-version"
tls_cipher_suites = "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA"
tls_min_version = "tls11"
verify_incoming = true
verify_incoming_https = false
verify_incoming_rpc = false
@ -61,6 +62,7 @@ tls_prefer_server_cipher_suites = true
deprecationWarning("cert_file", "tls.defaults.cert_file"),
deprecationWarning("key_file", "tls.defaults.key_file"),
deprecationWarning("tls_cipher_suites", "tls.defaults.tls_cipher_suites"),
fmt.Sprintf("'tls_min_version' value 'tls11' is deprecated, please specify 'TLSv1_1' instead"),
deprecationWarning("tls_min_version", "tls.defaults.tls_min_version"),
deprecationWarning("verify_incoming", "tls.defaults.verify_incoming"),
deprecationWarning("verify_incoming_https", "tls.https.verify_incoming"),
@ -90,8 +92,8 @@ tls_prefer_server_cipher_suites = true
require.Equal(t, "some-ca-path", l.CAPath)
require.Equal(t, "some-cert-file", l.CertFile)
require.Equal(t, "some-key-file", l.KeyFile)
require.Equal(t, "some-tls-version", l.TLSMinVersion)
require.Equal(t, []uint16{tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256}, l.CipherSuites)
require.Equal(t, types.TLSVersion("TLSv1_1"), l.TLSMinVersion)
require.Equal(t, []types.TLSCipherSuite{types.TLSCipherSuite("TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA")}, l.CipherSuites)
}
require.False(t, rt.TLS.InternalRPC.VerifyIncoming)

View File

@ -2,7 +2,6 @@ package config
import (
"bytes"
"crypto/tls"
"encoding/base64"
"encoding/json"
"errors"
@ -5395,8 +5394,8 @@ func TestLoad_IntegrationWithFlags(t *testing.T) {
ca_file = "default_ca_file"
ca_path = "default_ca_path"
cert_file = "default_cert_file"
tls_min_version = "tls12"
tls_cipher_suites = "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256"
tls_min_version = "TLSv1_2"
tls_cipher_suites = "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256"
verify_incoming = true
}
@ -5406,7 +5405,7 @@ func TestLoad_IntegrationWithFlags(t *testing.T) {
https {
cert_file = "https_cert_file"
tls_min_version = "tls13"
tls_min_version = "TLSv1_3"
}
grpc {
@ -5425,8 +5424,8 @@ func TestLoad_IntegrationWithFlags(t *testing.T) {
"ca_file": "default_ca_file",
"ca_path": "default_ca_path",
"cert_file": "default_cert_file",
"tls_min_version": "tls12",
"tls_cipher_suites": "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256",
"tls_min_version": "TLSv1_2",
"tls_cipher_suites": "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256",
"verify_incoming": true
},
"internal_rpc": {
@ -5434,7 +5433,7 @@ func TestLoad_IntegrationWithFlags(t *testing.T) {
},
"https": {
"cert_file": "https_cert_file",
"tls_min_version": "tls13"
"tls_min_version": "TLSv1_3"
},
"grpc": {
"verify_incoming": false,
@ -5455,22 +5454,21 @@ func TestLoad_IntegrationWithFlags(t *testing.T) {
rt.TLS.InternalRPC.CAFile = "internal_rpc_ca_file"
rt.TLS.InternalRPC.CAPath = "default_ca_path"
rt.TLS.InternalRPC.CertFile = "default_cert_file"
rt.TLS.InternalRPC.TLSMinVersion = "tls12"
rt.TLS.InternalRPC.CipherSuites = []uint16{tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256}
rt.TLS.InternalRPC.TLSMinVersion = "TLSv1_2"
rt.TLS.InternalRPC.CipherSuites = []types.TLSCipherSuite{types.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256}
rt.TLS.InternalRPC.VerifyIncoming = true
rt.TLS.HTTPS.CAFile = "default_ca_file"
rt.TLS.HTTPS.CAPath = "default_ca_path"
rt.TLS.HTTPS.CertFile = "https_cert_file"
rt.TLS.HTTPS.TLSMinVersion = "tls13"
rt.TLS.HTTPS.CipherSuites = []uint16{tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256}
rt.TLS.HTTPS.TLSMinVersion = "TLSv1_3"
rt.TLS.HTTPS.VerifyIncoming = true
rt.TLS.GRPC.CAFile = "default_ca_file"
rt.TLS.GRPC.CAPath = "default_ca_path"
rt.TLS.GRPC.CertFile = "default_cert_file"
rt.TLS.GRPC.TLSMinVersion = "tls12"
rt.TLS.GRPC.CipherSuites = []uint16{tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA}
rt.TLS.GRPC.TLSMinVersion = "TLSv1_2"
rt.TLS.GRPC.CipherSuites = []types.TLSCipherSuite{types.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA}
rt.TLS.GRPC.VerifyIncoming = false
},
})
@ -6310,8 +6308,8 @@ func TestLoad_FullConfig(t *testing.T) {
CAPath: "lOp1nhPa",
CertFile: "dfJ4oPln",
KeyFile: "aL1Knkpo",
TLSMinVersion: "lPo1MklP",
CipherSuites: []uint16{tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256},
TLSMinVersion: types.TLSv1_1,
CipherSuites: []types.TLSCipherSuite{types.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256, types.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA},
VerifyOutgoing: true,
VerifyServerHostname: true,
},
@ -6321,8 +6319,8 @@ func TestLoad_FullConfig(t *testing.T) {
CAPath: "fLponKpl",
CertFile: "a674klPn",
KeyFile: "1y4prKjl",
TLSMinVersion: "lPo4fNkl",
CipherSuites: []uint16{tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256},
TLSMinVersion: types.TLSv1_0,
CipherSuites: []types.TLSCipherSuite{types.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256, types.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA},
VerifyOutgoing: false,
},
HTTPS: tlsutil.ProtocolConfig{
@ -6331,8 +6329,7 @@ func TestLoad_FullConfig(t *testing.T) {
CAPath: "nu4PlHzn",
CertFile: "1yrhPlMk",
KeyFile: "1bHapOkL",
TLSMinVersion: "mK14iOpz",
CipherSuites: []uint16{tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256},
TLSMinVersion: types.TLSv1_3,
VerifyOutgoing: true,
},
NodeName: "otlLxGaI",

View File

@ -653,8 +653,8 @@ tls {
ca_path = "bN63LpXu"
cert_file = "hB4PoxkL"
key_file = "Po0hB1tY"
tls_cipher_suites = "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256"
tls_min_version = "yU0uIp1A"
tls_cipher_suites = "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256"
tls_min_version = "TLSv1_2"
verify_incoming = true
verify_outgoing = true
}
@ -663,8 +663,8 @@ tls {
ca_path = "lOp1nhPa"
cert_file = "dfJ4oPln"
key_file = "aL1Knkpo"
tls_cipher_suites = "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256"
tls_min_version = "lPo1MklP"
tls_cipher_suites = "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256,TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA"
tls_min_version = "TLSv1_1"
verify_incoming = true
verify_outgoing = true
verify_server_hostname = true
@ -674,8 +674,7 @@ tls {
ca_path = "nu4PlHzn"
cert_file = "1yrhPlMk"
key_file = "1bHapOkL"
tls_cipher_suites = "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256"
tls_min_version = "mK14iOpz"
tls_min_version = "TLSv1_3"
verify_incoming = true
verify_outgoing = true
}
@ -684,13 +683,13 @@ tls {
ca_path = "fLponKpl"
cert_file = "a674klPn"
key_file = "1y4prKjl"
tls_cipher_suites = "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256"
tls_min_version = "lPo4fNkl"
tls_cipher_suites = "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256,TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA"
tls_min_version = "TLSv1_0"
verify_incoming = true
}
}
tls_cipher_suites = "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256"
tls_min_version = "pAOWafkR"
tls_cipher_suites = "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256"
tls_min_version = "tls11"
tls_prefer_server_cipher_suites = true
translate_wan_addrs = true
ui_config {

View File

@ -650,8 +650,8 @@
"ca_path": "bN63LpXu",
"cert_file": "hB4PoxkL",
"key_file": "Po0hB1tY",
"tls_cipher_suites": "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256",
"tls_min_version": "yU0uIp1A",
"tls_cipher_suites": "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256",
"tls_min_version": "TLSv1_2",
"verify_incoming": true,
"verify_outgoing": true
},
@ -660,8 +660,8 @@
"ca_path": "lOp1nhPa",
"cert_file": "dfJ4oPln",
"key_file": "aL1Knkpo",
"tls_cipher_suites": "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256",
"tls_min_version": "lPo1MklP",
"tls_cipher_suites": "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256,TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA",
"tls_min_version": "TLSv1_1",
"verify_incoming": true,
"verify_outgoing": true
},
@ -670,8 +670,7 @@
"ca_path": "nu4PlHzn",
"cert_file": "1yrhPlMk",
"key_file": "1bHapOkL",
"tls_cipher_suites": "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256",
"tls_min_version": "mK14iOpz",
"tls_min_version": "TLSv1_3",
"verify_incoming": true,
"verify_outgoing": true
},
@ -680,13 +679,13 @@
"ca_path": "fLponKpl",
"cert_file": "a674klPn",
"key_file": "1y4prKjl",
"tls_cipher_suites": "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256",
"tls_min_version": "lPo4fNkl",
"tls_cipher_suites": "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256,TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA",
"tls_min_version": "TLSv1_0",
"verify_incoming": true
}
},
"tls_cipher_suites": "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256",
"tls_min_version": "pAOWafkR",
"tls_cipher_suites": "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256",
"tls_min_version": "tls11",
"tls_prefer_server_cipher_suites": true,
"translate_wan_addrs": true,
"ui_config": {

View File

@ -281,6 +281,7 @@ func (ac *AutoConfig) updateTLSSettingsInConfig(_ AutoConfigOptions, resp *pbaut
}
var err error
resp.Config.TLS, err = ac.tlsConfigurator.AutoConfigTLSSettings()
return err
}

View File

@ -25,6 +25,7 @@ import (
"github.com/hashicorp/consul/proto/pbconnect"
"github.com/hashicorp/consul/sdk/testutil"
"github.com/hashicorp/consul/tlsutil"
"github.com/hashicorp/consul/types"
"gopkg.in/square/go-jose.v2/jwt"
)
@ -173,7 +174,7 @@ func TestAutoConfigInitialConfiguration(t *testing.T) {
c.TLSConfig.InternalRPC.VerifyOutgoing = true
c.TLSConfig.InternalRPC.VerifyIncoming = true
c.TLSConfig.InternalRPC.VerifyServerHostname = true
c.TLSConfig.InternalRPC.TLSMinVersion = "tls12"
c.TLSConfig.InternalRPC.TLSMinVersion = types.TLSv1_2
c.ConnectEnabled = true
c.AutoEncryptAllowTLS = true
@ -391,13 +392,6 @@ func TestAutoConfig_baseConfig(t *testing.T) {
}
}
func parseCiphers(t *testing.T, cipherStr string) []uint16 {
t.Helper()
ciphers, err := tlsutil.ParseCiphers(cipherStr)
require.NoError(t, err)
return ciphers
}
func TestAutoConfig_updateTLSSettingsInConfig(t *testing.T) {
_, _, cacert, err := testTLSCertificates("server.dc1.consul")
require.NoError(t, err)
@ -418,9 +412,9 @@ func TestAutoConfig_updateTLSSettingsInConfig(t *testing.T) {
InternalRPC: tlsutil.ProtocolConfig{
VerifyServerHostname: true,
VerifyOutgoing: true,
TLSMinVersion: "tls12",
TLSMinVersion: types.TLSv1_2,
CAFile: cafile,
CipherSuites: parseCiphers(t, "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"),
CipherSuites: []types.TLSCipherSuite{"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"},
},
},
expected: pbautoconf.AutoConfigResponse{
@ -439,9 +433,9 @@ func TestAutoConfig_updateTLSSettingsInConfig(t *testing.T) {
InternalRPC: tlsutil.ProtocolConfig{
VerifyServerHostname: false,
VerifyOutgoing: true,
TLSMinVersion: "tls10",
TLSMinVersion: types.TLSv1_0,
CAFile: cafile,
CipherSuites: parseCiphers(t, "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384"),
CipherSuites: []types.TLSCipherSuite{"TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256", "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384"},
},
},
expected: pbautoconf.AutoConfigResponse{
@ -635,9 +629,9 @@ func TestAutoConfig_updateTLSCertificatesInConfig(t *testing.T) {
InternalRPC: tlsutil.ProtocolConfig{
VerifyServerHostname: true,
VerifyOutgoing: true,
TLSMinVersion: "tls12",
TLSMinVersion: types.TLSv1_2,
CAFile: cafile,
CipherSuites: parseCiphers(t, "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"),
CipherSuites: []types.TLSCipherSuite{"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"},
},
},
expected: pbautoconf.AutoConfigResponse{
@ -654,9 +648,9 @@ func TestAutoConfig_updateTLSCertificatesInConfig(t *testing.T) {
InternalRPC: tlsutil.ProtocolConfig{
VerifyServerHostname: true,
VerifyOutgoing: true,
TLSMinVersion: "tls12",
TLSMinVersion: types.TLSv1_2,
CAFile: cafile,
CipherSuites: parseCiphers(t, "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"),
CipherSuites: []types.TLSCipherSuite{"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"},
},
},
opts: AutoConfigOptions{

View File

@ -8,7 +8,6 @@ import (
"net"
"os"
"path/filepath"
"sort"
"strings"
"sync"
"sync/atomic"
@ -19,6 +18,7 @@ import (
"github.com/hashicorp/consul/logging"
"github.com/hashicorp/consul/proto/pbconfig"
"github.com/hashicorp/consul/types"
)
// ALPNWrapper is a function that is used to wrap a non-TLS connection and
@ -36,13 +36,13 @@ type DCWrapper func(dc string, conn net.Conn) (net.Conn, error)
// a constant value. This is usually done by currying DCWrapper.
type Wrapper func(conn net.Conn) (net.Conn, error)
// tlsLookup maps the tls_min_version configuration to the internal value
var tlsLookup = map[string]uint16{
"": tls.VersionTLS10, // default in golang
"tls10": tls.VersionTLS10,
"tls11": tls.VersionTLS11,
"tls12": tls.VersionTLS12,
"tls13": tls.VersionTLS13,
// goTLSVersions maps types.TLSVersion to the Go internal value
var goTLSVersions = map[types.TLSVersion]uint16{
types.TLSVersionAuto: tls.VersionTLS12,
types.TLSv1_0: tls.VersionTLS10,
types.TLSv1_1: tls.VersionTLS11,
types.TLSv1_2: tls.VersionTLS12,
types.TLSv1_3: tls.VersionTLS13,
}
// ProtocolConfig contains configuration for a given protocol.
@ -71,27 +71,16 @@ type ProtocolConfig struct {
KeyFile string
// TLSMinVersion is the minimum accepted TLS version that can be used.
TLSMinVersion string
TLSMinVersion types.TLSVersion
// CipherSuites is the list of TLS cipher suites to use.
//
// The values should be a list of the following values:
//
// TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA
// TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256
// TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256
// TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA
// TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384
// TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA
// TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256
// TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
// TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA
// TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384
//
// todo(fs): IMHO, we should also support the raw 0xNNNN values from
// todo(fs): https://golang.org/pkg/crypto/tls/#pkg-constants
// todo(fs): since they are standardized by IANA.
CipherSuites []uint16
// We don't support the raw 0xNNNN values from
// https://golang.org/pkg/crypto/tls/#pkg-constants
// even though they are standardized by IANA because it would increase
// the likelihood of an operator inadvertently setting an insecure configuration
CipherSuites []types.TLSCipherSuite
// VerifyOutgoing is used to verify the authenticity of outgoing
// connections. This means that TLS requests are used, and TCP
@ -148,17 +137,6 @@ type Config struct {
AutoTLS bool
}
func tlsVersions() []string {
versions := []string{}
for v := range tlsLookup {
if v != "" {
versions = append(versions, v)
}
}
sort.Strings(versions)
return versions
}
// SpecificDC is used to invoke a static datacenter
// and turns a DCWrapper into a Wrapper type.
func SpecificDC(dc string, tlsWrap DCWrapper) Wrapper {
@ -289,19 +267,12 @@ func (c *Configurator) Update(config Config) error {
// loadProtocolConfig loads the certificates etc. for a given ProtocolConfig
// and performs validation.
func (c *Configurator) loadProtocolConfig(base Config, lc ProtocolConfig) (*protocolConfig, error) {
if min := lc.TLSMinVersion; min != "" {
if _, ok := tlsLookup[min]; !ok {
versions := strings.Join(tlsVersions(), ", ")
return nil, fmt.Errorf("TLSMinVersion: value %s not supported, please specify one of [%s]", min, versions)
}
}
cert, err := loadKeyPair(lc.CertFile, lc.KeyFile)
func (c *Configurator) loadProtocolConfig(base Config, pc ProtocolConfig) (*protocolConfig, error) {
cert, err := loadKeyPair(pc.CertFile, pc.KeyFile)
if err != nil {
return nil, err
}
pems, err := LoadCAs(lc.CAFile, lc.CAPath)
pems, err := LoadCAs(pc.CAFile, pc.CAPath)
if err != nil {
return nil, err
}
@ -314,7 +285,7 @@ func (c *Configurator) loadProtocolConfig(base Config, lc ProtocolConfig) (*prot
return nil, err
}
if lc.VerifyIncoming {
if pc.VerifyIncoming {
// Both auto-config and auto-encrypt require verifying the connection from the
// client to the server for secure operation. In order to be able to verify the
// server's certificate we must have some CA certs already provided. Therefore,
@ -336,7 +307,7 @@ func (c *Configurator) loadProtocolConfig(base Config, lc ProtocolConfig) (*prot
}
// Ensure we have a CA if VerifyOutgoing is set.
if lc.VerifyOutgoing && combinedPool == nil {
if pc.VerifyOutgoing && combinedPool == nil {
return nil, fmt.Errorf("VerifyOutgoing set but no CA certificates were provided")
}
@ -572,7 +543,11 @@ func (c *Configurator) commonTLSConfig(state protocolConfig, cfg ProtocolConfig,
// Set the cipher suites
if len(cfg.CipherSuites) != 0 {
tlsConfig.CipherSuites = cfg.CipherSuites
// TLS cipher suites are validated on input in agent config builder,
// so it's safe to ignore the error case here.
cipherSuites, _ := cipherSuiteLookup(cfg.CipherSuites)
tlsConfig.CipherSuites = cipherSuites
}
// GetCertificate is used when acting as a server and responding to
@ -607,10 +582,11 @@ func (c *Configurator) commonTLSConfig(state protocolConfig, cfg ProtocolConfig,
tlsConfig.ClientCAs = state.combinedCAPool
tlsConfig.RootCAs = state.combinedCAPool
// This is possible because tlsLookup also contains "" with golang's
// default (tls10). And because the initial check makes sure the
// version correctly matches.
tlsConfig.MinVersion = tlsLookup[cfg.TLSMinVersion]
// Error handling is not needed here because agent config builder handles ""
// or a nil value as TLSVersionAuto with goTLSVersions mapping TLSVersionAuto
// to TLS 1.2 and because the initial check makes sure a specified version is
// not invalid.
tlsConfig.MinVersion = goTLSVersions[cfg.TLSMinVersion]
// Set ClientAuth if necessary
if verifyIncoming {
@ -736,7 +712,7 @@ func (c *Configurator) AutoConfigTLSSettings() (*pbconfig.TLS, error) {
return &pbconfig.TLS{
VerifyOutgoing: cfg.VerifyOutgoing,
VerifyServerHostname: cfg.VerifyServerHostname || c.autoTLS.verifyServerHostname,
MinVersion: cfg.TLSMinVersion,
MinVersion: types.ConsulAutoConfigTLSVersionStrings[cfg.TLSMinVersion],
CipherSuites: cipherString,
}, nil
}
@ -1080,32 +1056,32 @@ func (c *Configurator) AuthorizeServerConn(dc string, conn TLSConn) error {
}
// ParseCiphers parse ciphersuites from the comma-separated string into
// recognized slice
func ParseCiphers(cipherStr string) ([]uint16, error) {
// NOTE: any new cipher suites will also need to be added in types/tls.go
// TODO: should this be moved into types/tls.go? Would importing Go's tls
// package in there be acceptable?
var goTLSCipherSuites = map[types.TLSCipherSuite]uint16{
types.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256: tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256,
types.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA: tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,
types.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256: tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
types.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA: tls.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,
types.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384: tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
types.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256: tls.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256,
types.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA: tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,
types.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256: tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
types.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA: tls.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,
types.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384: tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
}
func cipherSuiteLookup(ciphers []types.TLSCipherSuite) ([]uint16, error) {
suites := []uint16{}
cipherStr = strings.TrimSpace(cipherStr)
if cipherStr == "" {
if len(ciphers) == 0 {
return []uint16{}, nil
}
ciphers := strings.Split(cipherStr, ",")
// Note: this needs to be kept up to date with the cipherMap in CipherString
cipherMap := map[string]uint16{
"TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA": tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,
"TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256": tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256,
"TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256": tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
"TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA": tls.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,
"TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384": tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
"TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA": tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,
"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_RSA_WITH_AES_256_CBC_SHA": tls.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,
"TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384": tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
}
for _, cipher := range ciphers {
if v, ok := cipherMap[cipher]; ok {
if v, ok := goTLSCipherSuites[cipher]; ok {
suites = append(suites, v)
} else {
return suites, fmt.Errorf("unsupported cipher %q", cipher)
@ -1115,29 +1091,16 @@ func ParseCiphers(cipherStr string) ([]uint16, error) {
return suites, nil
}
// CipherString performs the inverse operation of ParseCiphers
func CipherString(ciphers []uint16) (string, error) {
// Note: this needs to be kept up to date with the cipherMap in ParseCiphers
cipherMap := map[uint16]string{
tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA: "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA",
tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256: "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256",
tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256: "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256",
tls.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA: "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA",
tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384: "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384",
tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA: "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA",
tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256: "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256",
tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256: "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
tls.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA: "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA",
tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384: "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384",
// CipherString performs the inverse operation of types.ParseCiphers
func CipherString(ciphers []types.TLSCipherSuite) (string, error) {
err := types.ValidateConsulAgentCipherSuites(ciphers)
if err != nil {
return "", err
}
cipherStrings := make([]string, len(ciphers))
for i, cipher := range ciphers {
if v, ok := cipherMap[cipher]; ok {
cipherStrings[i] = v
} else {
return "", fmt.Errorf("unsupported cipher %d", cipher)
}
cipherStrings[i] = string(cipher)
}
return strings.Join(cipherStrings, ","), nil

View File

@ -8,8 +8,6 @@ import (
"io/ioutil"
"net"
"path/filepath"
"reflect"
"strings"
"testing"
"github.com/google/go-cmp/cmp"
@ -19,6 +17,7 @@ import (
"github.com/stretchr/testify/require"
"github.com/hashicorp/consul/sdk/testutil"
"github.com/hashicorp/consul/types"
)
func TestConfigurator_IncomingConfig_Common(t *testing.T) {
@ -46,7 +45,7 @@ func TestConfigurator_IncomingConfig_Common(t *testing.T) {
t.Run(desc, func(t *testing.T) {
t.Run("MinTLSVersion", func(t *testing.T) {
cfg := ProtocolConfig{
TLSMinVersion: "tls13",
TLSMinVersion: "TLSv1_3",
CertFile: "../test/hostname/Alice.crt",
KeyFile: "../test/hostname/Alice.key",
}
@ -69,7 +68,7 @@ func TestConfigurator_IncomingConfig_Common(t *testing.T) {
t.Run("CipherSuites", func(t *testing.T) {
cfg := ProtocolConfig{
CipherSuites: []uint16{tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384},
CipherSuites: []types.TLSCipherSuite{types.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384},
CertFile: "../test/hostname/Alice.crt",
KeyFile: "../test/hostname/Alice.key",
}
@ -760,45 +759,6 @@ func TestConfigurator_outgoingWrapperALPN_serverHasNoNodeNameInSAN(t *testing.T)
<-errc
}
func TestConfig_ParseCiphers(t *testing.T) {
testOk := strings.Join([]string{
"TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA",
"TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256",
"TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256",
"TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA",
"TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384",
"TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA",
"TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256",
"TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
"TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA",
"TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384",
}, ",")
ciphers := []uint16{
tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,
tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256,
tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
tls.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,
tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,
tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256,
tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
tls.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,
tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
}
v, err := ParseCiphers(testOk)
require.NoError(t, err)
if got, want := v, ciphers; !reflect.DeepEqual(got, want) {
t.Fatalf("got ciphers %#v want %#v", got, want)
}
_, err = ParseCiphers("TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,cipherX")
require.Error(t, err)
v, err = ParseCiphers("")
require.NoError(t, err)
require.Equal(t, []uint16{}, v)
}
func TestLoadKeyPair(t *testing.T) {
type variant struct {
cert, key string
@ -866,14 +826,6 @@ func TestConfigurator_Validation(t *testing.T) {
}
testCases := map[string]testCase{
"invalid TLSMinVersion": {
ProtocolConfig{TLSMinVersion: "tls9"},
false,
},
"default TLSMinVersion": {
ProtocolConfig{TLSMinVersion: ""},
true,
},
"invalid CAFile": {
ProtocolConfig{CAFile: "bogus"},
false,
@ -986,13 +938,6 @@ func TestConfigurator_Validation(t *testing.T) {
},
}
for _, v := range tlsVersions() {
testCases[fmt.Sprintf("MinTLSVersion(%s)", v)] = testCase{
ProtocolConfig{TLSMinVersion: v},
true,
}
}
for desc, tc := range testCases {
for _, p := range []string{"internal", "grpc", "https"} {
info := fmt.Sprintf("%s => %s", p, desc)
@ -1229,7 +1174,7 @@ func TestConfigurator_OutgoingTLSConfigForCheck(t *testing.T) {
conf: func() (*Configurator, error) {
return NewConfigurator(Config{
InternalRPC: ProtocolConfig{
TLSMinVersion: "tls12",
TLSMinVersion: types.TLSv1_2,
},
EnableAgentTLSForChecks: false,
}, nil)
@ -1242,7 +1187,7 @@ func TestConfigurator_OutgoingTLSConfigForCheck(t *testing.T) {
conf: func() (*Configurator, error) {
return NewConfigurator(Config{
InternalRPC: ProtocolConfig{
TLSMinVersion: "tls12",
TLSMinVersion: types.TLSv1_2,
},
EnableAgentTLSForChecks: false,
ServerName: "servername",
@ -1257,7 +1202,7 @@ func TestConfigurator_OutgoingTLSConfigForCheck(t *testing.T) {
conf: func() (*Configurator, error) {
return NewConfigurator(Config{
InternalRPC: ProtocolConfig{
TLSMinVersion: "tls12",
TLSMinVersion: types.TLSv1_2,
},
EnableAgentTLSForChecks: false,
ServerName: "servername",
@ -1275,7 +1220,7 @@ func TestConfigurator_OutgoingTLSConfigForCheck(t *testing.T) {
conf: func() (*Configurator, error) {
return NewConfigurator(Config{
InternalRPC: ProtocolConfig{
TLSMinVersion: "tls12",
TLSMinVersion: types.TLSv1_2,
},
EnableAgentTLSForChecks: true,
NodeName: "nodename",
@ -1292,7 +1237,7 @@ func TestConfigurator_OutgoingTLSConfigForCheck(t *testing.T) {
conf: func() (*Configurator, error) {
return NewConfigurator(Config{
InternalRPC: ProtocolConfig{
TLSMinVersion: "tls12",
TLSMinVersion: types.TLSv1_2,
},
EnableAgentTLSForChecks: true,
NodeName: "nodename",
@ -1310,7 +1255,7 @@ func TestConfigurator_OutgoingTLSConfigForCheck(t *testing.T) {
conf: func() (*Configurator, error) {
return NewConfigurator(Config{
InternalRPC: ProtocolConfig{
TLSMinVersion: "tls12",
TLSMinVersion: types.TLSv1_2,
},
EnableAgentTLSForChecks: true,
ServerName: "servername",
@ -1517,12 +1462,6 @@ func TestConfigurator_AuthorizeInternalRPCServerConn(t *testing.T) {
})
}
func TestConfig_tlsVersions(t *testing.T) {
require.Equal(t, []string{"tls10", "tls11", "tls12", "tls13"}, tlsVersions())
expected := "tls10, tls11, tls12, tls13"
require.Equal(t, expected, strings.Join(tlsVersions(), ", "))
}
func TestConfigurator_GRPCTLSConfigured(t *testing.T) {
t.Run("certificate manually configured", func(t *testing.T) {
c := makeConfigurator(t, Config{

View File

@ -2,6 +2,7 @@ package types
import (
"fmt"
"sort"
"strings"
)
@ -92,9 +93,19 @@ func (a TLSVersion) LessThan(b TLSVersion) (error, bool) {
return nil, tlsVersionComparison[a] < tlsVersionComparison[b]
}
func TLSVersions() string {
versions := []string{}
for v := range tlsVersions {
versions = append(versions, string(v))
}
sort.Strings(versions)
return strings.Join(versions, ", ")
}
func ValidateTLSVersion(v TLSVersion) error {
if _, ok := tlsVersions[v]; !ok {
return fmt.Errorf("no matching TLS version found for %s", v.String())
return fmt.Errorf("no matching TLS version found for %s, please specify one of [%s]", v.String(), TLSVersions())
}
return nil

View File

@ -2495,15 +2495,30 @@ specially crafted certificate signed by the CA can be used to gain full access t
[`cert_file`](#tls_defaults_cert_file).
- `tls_min_version` ((#tls_defaults_tls_min_version)) This specifies the
minimum supported version of TLS. Accepted values are "tls10", "tls11",
"tls12", or "tls13". This defaults to "tls12". **WARNING: TLS 1.1 and
lower are generally considered less secure; avoid using these if
possible.**
minimum supported version of TLS. The following values are accepted:
* `TLSv1_0`
* `TLSv1_1`
* `TLSv1_2` (default)
* `TLSv1_3`
**WARNING: TLS 1.1 and lower are generally considered less secure and
should not be used if possible.**
The following values are also valid, but only when using the
[deprecated top-level `tls_min_version` config](#tls_deprecated_options),
and will be removed in a future release:
* `tls10`
* `tls11`
* `tls12`
* `tls13`
A warning message will appear if a deprecated value is specified.
- `tls_cipher_suites` ((#tls_defaults_tls_cipher_suites)) This specifies
the list of supported ciphersuites as a comma-separated-list. Applicable
to TLS 1.2 and below only. The list of all supported ciphersuites is
available through [this search](https://github.com/hashicorp/consul/search?q=cipherMap+%3A%3D+map&unscoped_q=cipherMap+%3A%3D+map).
available through [this search](https://github.com/hashicorp/consul/search?q=goTLSCipherSuites+%3D+map).
~> **Note:** The ordering of cipher suites will not be guaranteed from
Consul 1.11 onwards. See this [post](https://go.dev/blog/tls-cipher-suites)