Added new auto_encrypt.grpc_server_tls config option to control AutoTLS enabling of GRPC Server's TLS usage

Fix for #14253

Co-authored-by: trujillo-adam <47586768+trujillo-adam@users.noreply.github.com>
This commit is contained in:
Pablo Ruiz García 2022-08-24 18:31:38 +02:00 committed by GitHub
parent bb56a3ee50
commit 4188769c32
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 162 additions and 33 deletions

View File

@ -2531,10 +2531,9 @@ func (b *builder) buildTLSConfig(rt RuntimeConfig, t TLS) (tlsutil.Config, error
return c, errors.New("verify_server_hostname is only valid in the tls.internal_rpc stanza")
}
// TLS is only enabled on the gRPC listener if there's an HTTPS port configured
// for historic and backwards-compatibility reasons.
if rt.HTTPSPort <= 0 && (t.GRPC != TLSProtocolConfig{} && t.GRPCModifiedByDeprecatedConfig == nil) {
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)")
// And UseAutoCert right now only applies to external gRPC interface.
if t.Defaults.UseAutoCert != nil || t.HTTPS.UseAutoCert != nil || t.InternalRPC.UseAutoCert != nil {
return c, errors.New("use_auto_cert is only valid in the tls.grpc stanza")
}
defaultTLSMinVersion := b.tlsVersion("tls.defaults.tls_min_version", t.Defaults.TLSMinVersion)
@ -2591,6 +2590,7 @@ func (b *builder) buildTLSConfig(rt RuntimeConfig, t TLS) (tlsutil.Config, error
mapCommon("https", t.HTTPS, &c.HTTPS)
mapCommon("grpc", t.GRPC, &c.GRPC)
c.GRPC.UseAutoCert = boolValWithDefault(t.GRPC.UseAutoCert, false)
c.ServerName = rt.ServerName
c.NodeName = rt.NodeName

View File

@ -867,6 +867,7 @@ type TLSProtocolConfig struct {
VerifyIncoming *bool `mapstructure:"verify_incoming"`
VerifyOutgoing *bool `mapstructure:"verify_outgoing"`
VerifyServerHostname *bool `mapstructure:"verify_server_hostname"`
UseAutoCert *bool `mapstructure:"use_auto_cert"`
}
type TLS struct {

View File

@ -5516,7 +5516,70 @@ func TestLoad_IntegrationWithFlags(t *testing.T) {
},
})
run(t, testCase{
desc: "tls.grpc without ports.https",
desc: "tls.grpc.use_auto_cert defaults to false",
args: []string{
`-data-dir=` + dataDir,
},
json: []string{`
{
"tls": {
"grpc": {}
}
}
`},
hcl: []string{`
tls {
grpc {}
}
`},
expected: func(rt *RuntimeConfig) {
rt.DataDir = dataDir
rt.TLS.Domain = "consul."
rt.TLS.NodeName = "thehostname"
rt.TLS.GRPC.UseAutoCert = false
},
})
run(t, testCase{
desc: "tls.grpc.use_auto_cert defaults to false (II)",
args: []string{
`-data-dir=` + dataDir,
},
json: []string{`
{
"tls": {}
}
`},
hcl: []string{`
tls {
}
`},
expected: func(rt *RuntimeConfig) {
rt.DataDir = dataDir
rt.TLS.Domain = "consul."
rt.TLS.NodeName = "thehostname"
rt.TLS.GRPC.UseAutoCert = false
},
})
run(t, testCase{
desc: "tls.grpc.use_auto_cert defaults to false (III)",
args: []string{
`-data-dir=` + dataDir,
},
json: []string{`
{
}
`},
hcl: []string{`
`},
expected: func(rt *RuntimeConfig) {
rt.DataDir = dataDir
rt.TLS.Domain = "consul."
rt.TLS.NodeName = "thehostname"
rt.TLS.GRPC.UseAutoCert = false
},
})
run(t, testCase{
desc: "tls.grpc.use_auto_cert enabled when true",
args: []string{
`-data-dir=` + dataDir,
},
@ -5524,7 +5587,7 @@ func TestLoad_IntegrationWithFlags(t *testing.T) {
{
"tls": {
"grpc": {
"cert_file": "cert-1234"
"use_auto_cert": true
}
}
}
@ -5532,20 +5595,43 @@ func TestLoad_IntegrationWithFlags(t *testing.T) {
hcl: []string{`
tls {
grpc {
cert_file = "cert-1234"
use_auto_cert = true
}
}
`},
expected: func(rt *RuntimeConfig) {
rt.DataDir = dataDir
rt.TLS.Domain = "consul."
rt.TLS.NodeName = "thehostname"
rt.TLS.GRPC.CertFile = "cert-1234"
rt.TLS.GRPC.UseAutoCert = true
},
expectedWarnings: []string{
"tls.grpc was provided but TLS will NOT be enabled on the gRPC listener without an HTTPS listener configured (e.g. via ports.https)",
})
run(t, testCase{
desc: "tls.grpc.use_auto_cert disabled when false",
args: []string{
`-data-dir=` + dataDir,
},
json: []string{`
{
"tls": {
"grpc": {
"use_auto_cert": false
}
}
}
`},
hcl: []string{`
tls {
grpc {
use_auto_cert = false
}
}
`},
expected: func(rt *RuntimeConfig) {
rt.DataDir = dataDir
rt.TLS.Domain = "consul."
rt.TLS.NodeName = "thehostname"
rt.TLS.GRPC.UseAutoCert = false
},
})
}
@ -6340,6 +6426,7 @@ func TestLoad_FullConfig(t *testing.T) {
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,
UseAutoCert: true,
},
HTTPS: tlsutil.ProtocolConfig{
VerifyIncoming: true,

View File

@ -374,7 +374,8 @@
"TLSMinVersion": "",
"VerifyIncoming": false,
"VerifyOutgoing": false,
"VerifyServerHostname": false
"VerifyServerHostname": false,
"UseAutoCert": false
},
"HTTPS": {
"CAFile": "",
@ -385,7 +386,8 @@
"TLSMinVersion": "",
"VerifyIncoming": false,
"VerifyOutgoing": false,
"VerifyServerHostname": false
"VerifyServerHostname": false,
"UseAutoCert": false
},
"InternalRPC": {
"CAFile": "",
@ -396,7 +398,8 @@
"TLSMinVersion": "",
"VerifyIncoming": false,
"VerifyOutgoing": false,
"VerifyServerHostname": false
"VerifyServerHostname": false,
"UseAutoCert": false
},
"NodeName": "",
"ServerName": ""
@ -466,4 +469,4 @@
"VersionMetadata": "",
"VersionPrerelease": "",
"Watches": []
}
}

View File

@ -697,6 +697,7 @@ tls {
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
use_auto_cert = true
}
}
tls_cipher_suites = "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256"

View File

@ -692,7 +692,8 @@
"key_file": "1y4prKjl",
"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
"verify_incoming": true,
"use_auto_cert": true
}
},
"tls_cipher_suites": "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256",

View File

@ -1,12 +1,13 @@
package external
import (
"time"
middleware "github.com/grpc-ecosystem/go-grpc-middleware"
recovery "github.com/grpc-ecosystem/go-grpc-middleware/recovery"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials"
"google.golang.org/grpc/keepalive"
"time"
agentmiddleware "github.com/hashicorp/consul/agent/grpc-middleware"
"github.com/hashicorp/consul/tlsutil"
@ -34,7 +35,7 @@ func NewServer(logger agentmiddleware.Logger, tls *tlsutil.Configurator) *grpc.S
MinTime: 15 * time.Second,
}),
}
if tls != nil && tls.GRPCTLSConfigured() {
if tls != nil && tls.GRPCServerUseTLS() {
creds := credentials.NewTLS(tls.IncomingGRPCConfig())
opts = append(opts, grpc.Creds(creds))
}

View File

@ -102,6 +102,10 @@ type ProtocolConfig struct {
//
// Note: this setting only applies to the Internal RPC configuration.
VerifyServerHostname bool
// UseAutoCert is used to enable usage of auto_encrypt/auto_config generated
// certificate & key material on external gRPC listener.
UseAutoCert bool
}
// Config configures the Configurator.
@ -167,6 +171,10 @@ type protocolConfig struct {
// combinedCAPool is a pool containing both manualCAPEMs and the certificates
// received from auto-config/auto-encrypt.
combinedCAPool *x509.CertPool
// useAutoCert indicates wether we should use auto-encrypt/config data
// for TLS server/listener. NOTE: Only applies to external GRPC Server.
useAutoCert bool
}
// Configurator provides tls.Config and net.Dial wrappers to enable TLS for
@ -323,6 +331,7 @@ func (c *Configurator) loadProtocolConfig(base Config, pc ProtocolConfig) (*prot
manualCAPEMs: pems,
manualCAPool: manualPool,
combinedCAPool: combinedPool,
useAutoCert: pc.UseAutoCert,
}, nil
}
@ -620,16 +629,15 @@ func (c *Configurator) Cert() *tls.Certificate {
return cert
}
// GRPCTLSConfigured returns whether there's a TLS certificate configured for
// gRPC (either manually or by auto-config/auto-encrypt). It is checked, along
// with the presence of an HTTPS port, to determine whether to enable TLS on
// incoming gRPC connections.
// GRPCServerUseTLS returns whether there's a TLS certificate configured for
// (external) gRPC (either manually or by auto-config/auto-encrypt), and use
// of TLS for gRPC has not been explicitly disabled at auto-encrypt.
//
// This function acquires a read lock because it reads from the config.
func (c *Configurator) GRPCTLSConfigured() bool {
func (c *Configurator) GRPCServerUseTLS() bool {
c.lock.RLock()
defer c.lock.RUnlock()
return c.grpc.cert != nil || c.autoTLS.cert != nil
return c.grpc.cert != nil || (c.grpc.useAutoCert && c.autoTLS.cert != nil)
}
// VerifyIncomingRPC returns true if we should verify incoming connnections to

View File

@ -1465,7 +1465,7 @@ func TestConfigurator_AuthorizeInternalRPCServerConn(t *testing.T) {
})
}
func TestConfigurator_GRPCTLSConfigured(t *testing.T) {
func TestConfigurator_GRPCServerUseTLS(t *testing.T) {
t.Run("certificate manually configured", func(t *testing.T) {
c := makeConfigurator(t, Config{
GRPC: ProtocolConfig{
@ -1473,22 +1473,47 @@ func TestConfigurator_GRPCTLSConfigured(t *testing.T) {
KeyFile: "../test/hostname/Alice.key",
},
})
require.True(t, c.GRPCTLSConfigured())
require.True(t, c.GRPCServerUseTLS())
})
t.Run("AutoTLS", func(t *testing.T) {
t.Run("no certificate", func(t *testing.T) {
c := makeConfigurator(t, Config{})
require.False(t, c.GRPCServerUseTLS())
})
t.Run("AutoTLS (default)", func(t *testing.T) {
c := makeConfigurator(t, Config{})
bobCert := loadFile(t, "../test/hostname/Bob.crt")
bobKey := loadFile(t, "../test/hostname/Bob.key")
require.NoError(t, c.UpdateAutoTLSCert(bobCert, bobKey))
require.True(t, c.GRPCTLSConfigured())
require.False(t, c.GRPCServerUseTLS())
})
t.Run("no certificate", func(t *testing.T) {
c := makeConfigurator(t, Config{})
require.False(t, c.GRPCTLSConfigured())
t.Run("AutoTLS w/ UseAutoCert Disabled", func(t *testing.T) {
c := makeConfigurator(t, Config{
GRPC: ProtocolConfig{
UseAutoCert: false,
},
})
bobCert := loadFile(t, "../test/hostname/Bob.crt")
bobKey := loadFile(t, "../test/hostname/Bob.key")
require.NoError(t, c.UpdateAutoTLSCert(bobCert, bobKey))
require.False(t, c.GRPCServerUseTLS())
})
t.Run("AutoTLS w/ UseAutoCert Enabled", func(t *testing.T) {
c := makeConfigurator(t, Config{
GRPC: ProtocolConfig{
UseAutoCert: true,
},
})
bobCert := loadFile(t, "../test/hostname/Bob.crt")
bobKey := loadFile(t, "../test/hostname/Bob.key")
require.NoError(t, c.UpdateAutoTLSCert(bobCert, bobKey))
require.True(t, c.GRPCServerUseTLS())
})
}

View File

@ -2019,6 +2019,8 @@ specially crafted certificate signed by the CA can be used to gain full access t
- `verify_incoming` - ((#tls_grpc_verify_incoming)) Overrides [`tls.defaults.verify_incoming`](#tls_defaults_verify_incoming).
- `use_auto_cert` - (Defaults to `false`) Enables or disables TLS on gRPC servers. Set to `true` to allow `auto_encrypt` TLS settings to apply to gRPC listeners. We recommend disabling TLS on gRPC servers if you are using `auto_encrypt` for other TLS purposes, such as enabling HTTPS.
- `https` ((#tls_https)) Provides settings for the HTTPS interface. To enable
the HTTPS interface you must define a port via [`ports.https`](#https_port).