From 13dc01c55391cb2aaeb4821d18a0578e2b344922 Mon Sep 17 00:00:00 2001 From: freddygv Date: Fri, 9 Sep 2022 13:01:47 -0600 Subject: [PATCH] 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. --- tlsutil/config.go | 30 ++++++++++++++++++--- tlsutil/config_test.go | 61 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 88 insertions(+), 3 deletions(-) diff --git a/tlsutil/config.go b/tlsutil/config.go index 2e1614165..b65c9ddd7 100644 --- a/tlsutil/config.go +++ b/tlsutil/config.go @@ -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 } diff --git a/tlsutil/config_test.go b/tlsutil/config_test.go index fc817aec6..dd3c03bd4 100644 --- a/tlsutil/config_test.go +++ b/tlsutil/config_test.go @@ -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