Update TLS configurator for peering traffic

When the TLS-enabled gRPC port receives a request for the expected
it must use the auto-tls certificates.
This commit is contained in:
freddygv 2022-09-09 13:01:47 -06:00
parent 0e5131bd33
commit 650e1e32e0
2 changed files with 88 additions and 3 deletions

View File

@ -199,13 +199,15 @@ type Configurator struct {
https protocolConfig
internalRPC protocolConfig
// autoTLS stores configuration that is received from the auto-encrypt or
// auto-config features.
// autoTLS stores configuration that is received from:
// - The auto-encrypt or auto-config features for client agents
// - The servercert.CertManager for server agents.
autoTLS struct {
extraCAPems []string
connectCAPems []string
cert *tls.Certificate
verifyServerHostname bool
peeringServerName string
}
// logger is not protected by a lock. It must never be changed after
@ -372,7 +374,7 @@ func (c *Configurator) UpdateAutoTLSCA(connectCAPems []string) error {
return nil
}
// UpdateAutoTLSCert receives the updated Auto-Encrypt certificate.
// UpdateAutoTLSCert receives the updated automatically-provisioned certificate.
func (c *Configurator) UpdateAutoTLSCert(pub, priv string) error {
cert, err := tls.X509KeyPair([]byte(pub), []byte(priv))
if err != nil {
@ -388,6 +390,16 @@ func (c *Configurator) UpdateAutoTLSCert(pub, priv string) error {
return nil
}
// UpdateAutoTLSPeeringServerName receives the updated automatically-provisioned certificate.
func (c *Configurator) UpdateAutoTLSPeeringServerName(name string) {
c.lock.Lock()
defer c.lock.Unlock()
c.autoTLS.peeringServerName = name
atomic.AddUint64(&c.version, 1)
c.log("UpdateAutoTLSPeeringServerName")
}
// UpdateAutoTLS receives updates from Auto-Config, only expected to be called on
// client agents.
func (c *Configurator) UpdateAutoTLS(manualCAPems, connectCAPems []string, pub, priv string, verifyServerHostname bool) error {
@ -754,6 +766,18 @@ func (c *Configurator) IncomingGRPCConfig() *tls.Config {
config.GetConfigForClient = func(*tls.ClientHelloInfo) (*tls.Config, error) {
return c.IncomingGRPCConfig(), nil
}
config.GetCertificate = func(info *tls.ClientHelloInfo) (*tls.Certificate, error) {
if c.autoTLS.peeringServerName != "" && info.ServerName == c.autoTLS.peeringServerName {
// For peering control plane traffic we exclusively use the internally managed certificate.
// For all other traffic it is only a fallback if no manual certificate is provisioned.
return c.autoTLS.cert, nil
}
if c.grpc.cert != nil {
return c.grpc.cert, nil
}
return c.autoTLS.cert, nil
}
return config
}

View File

@ -225,6 +225,67 @@ func TestConfigurator_IncomingConfig_Common(t *testing.T) {
}
}
func TestConfigurator_IncomingGRPCConfig_Peering(t *testing.T) {
// Manually configure Alice's certificates
cfg := Config{
GRPC: ProtocolConfig{
CertFile: "../test/hostname/Alice.crt",
KeyFile: "../test/hostname/Alice.key",
},
}
c := makeConfigurator(t, cfg)
// Set Bob's certificate via auto TLS.
bobCert := loadFile(t, "../test/hostname/Bob.crt")
bobKey := loadFile(t, "../test/hostname/Bob.key")
require.NoError(t, c.UpdateAutoTLSCert(bobCert, bobKey))
peeringServerName := "server.dc1.peering.1234"
c.UpdateAutoTLSPeeringServerName(peeringServerName)
testutil.RunStep(t, "with peering name", func(t *testing.T) {
client, errc, _ := startTLSServer(c.IncomingGRPCConfig())
if client == nil {
t.Fatalf("startTLSServer err: %v", <-errc)
}
tlsClient := tls.Client(client, &tls.Config{
// When the peering server name is provided the server should present
// the certificates configured via AutoTLS (Bob).
ServerName: peeringServerName,
InsecureSkipVerify: true,
})
require.NoError(t, tlsClient.Handshake())
certificates := tlsClient.ConnectionState().PeerCertificates
require.NotEmpty(t, certificates)
require.Equal(t, "Bob", certificates[0].Subject.CommonName)
// Check the server side of the handshake succeded.
require.NoError(t, <-errc)
})
testutil.RunStep(t, "without name", func(t *testing.T) {
client, errc, _ := startTLSServer(c.IncomingGRPCConfig())
if client == nil {
t.Fatalf("startTLSServer err: %v", <-errc)
}
tlsClient := tls.Client(client, &tls.Config{
// ServerName: peeringServerName,
InsecureSkipVerify: true,
})
require.NoError(t, tlsClient.Handshake())
certificates := tlsClient.ConnectionState().PeerCertificates
require.NotEmpty(t, certificates)
// Should default to presenting the manually configured certificates.
require.Equal(t, "Alice", certificates[0].Subject.CommonName)
// Check the server side of the handshake succeded.
require.NoError(t, <-errc)
})
}
func TestConfigurator_IncomingInsecureRPCConfig(t *testing.T) {
// if this test is failing because of expired certificates
// use the procedure in test/CA-GENERATION.md