From 9b43fb7f8db4bf277ab77f4f1165dd13678d6876 Mon Sep 17 00:00:00 2001 From: ZetaoZhuang <106119232+ZetaoZhuang@users.noreply.github.com> Date: Tue, 12 Nov 2024 12:50:09 -0800 Subject: [PATCH] expose MinTLSVersion config for TLS handshake (#3103) * expose MinTLSVersion config * address comment * use valid TLS version in config test --- cns/configuration/cns_config.json | 3 ++- cns/configuration/configuration.go | 5 ++++ cns/configuration/configuration_test.go | 10 ++++--- cns/configuration/testdata/good.json | 3 ++- cns/service.go | 28 ++++++++++++++++++-- cns/service/main.go | 1 + cns/service_test.go | 35 ++++++++++++++++++++++++- server/tls/tlscertificate_retriever.go | 1 + 8 files changed, 78 insertions(+), 8 deletions(-) diff --git a/cns/configuration/cns_config.json b/cns/configuration/cns_config.json index 6be0daafd..0f6548892 100644 --- a/cns/configuration/cns_config.json +++ b/cns/configuration/cns_config.json @@ -33,5 +33,6 @@ "MellanoxMonitorIntervalSecs": 30, "AZRSettings": { "PopulateHomeAzCacheRetryIntervalSecs": 60 - } + }, + "MinTLSVersion": "TLS 1.2" } diff --git a/cns/configuration/configuration.go b/cns/configuration/configuration.go index 80535192c..1f8b3ab9f 100644 --- a/cns/configuration/configuration.go +++ b/cns/configuration/configuration.go @@ -54,6 +54,7 @@ type CNSConfig struct { WatchPods bool `json:"-"` WireserverIP string GRPCSettings GRPCSettings + MinTLSVersion string } type TelemetrySettings struct { @@ -229,6 +230,10 @@ func SetCNSConfigDefaults(config *CNSConfig) { if config.GRPCSettings.Port == 0 { config.GRPCSettings.Port = 8080 } + + if config.MinTLSVersion == "" { + config.MinTLSVersion = "TLS 1.2" + } config.GRPCSettings.Enable = false config.WatchPods = config.EnableIPAMv2 || config.EnableSwiftV2 } diff --git a/cns/configuration/configuration_test.go b/cns/configuration/configuration_test.go index 589fd97a7..7a27f327d 100644 --- a/cns/configuration/configuration_test.go +++ b/cns/configuration/configuration_test.go @@ -86,9 +86,10 @@ func TestReadConfigFromFile(t *testing.T) { AZRSettings: AZRSettings{ PopulateHomeAzCacheRetryIntervalSecs: 60, }, - UseHTTPS: true, - UseMTLS: true, - WireserverIP: "168.63.129.16", + UseHTTPS: true, + UseMTLS: true, + WireserverIP: "168.63.129.16", + MinTLSVersion: "TLS 1.3", }, wantErr: false, }, @@ -220,6 +221,7 @@ func TestSetCNSConfigDefaults(t *testing.T) { IPAddress: "localhost", Port: 8080, }, + MinTLSVersion: "TLS 1.2", }, }, { @@ -250,6 +252,7 @@ func TestSetCNSConfigDefaults(t *testing.T) { IPAddress: "192.168.1.1", Port: 9090, }, + MinTLSVersion: "TLS 1.3", }, want: CNSConfig{ ChannelMode: "Other", @@ -279,6 +282,7 @@ func TestSetCNSConfigDefaults(t *testing.T) { IPAddress: "192.168.1.1", Port: 9090, }, + MinTLSVersion: "TLS 1.3", }, }, } diff --git a/cns/configuration/testdata/good.json b/cns/configuration/testdata/good.json index 185484ede..c2471f3ed 100644 --- a/cns/configuration/testdata/good.json +++ b/cns/configuration/testdata/good.json @@ -34,5 +34,6 @@ "WireserverIP": "168.63.129.16", "AZRSettings": { "PopulateHomeAzCacheRetryIntervalSecs": 60 - } + }, + "MinTLSVersion": "TLS 1.3" } diff --git a/cns/service.go b/cns/service.go index 5e850473b..ab7a0be3c 100644 --- a/cns/service.go +++ b/cns/service.go @@ -28,6 +28,8 @@ const ( genericData = "com.microsoft.azure.network.generic" ) +var errTLSConfig = errors.New("unsupported TLS version name from config") + // Service defines Container Networking Service. type Service struct { *common.Service @@ -179,10 +181,14 @@ func getTLSConfigFromFile(tlsSettings localtls.TlsSettings) (*tls.Config, error) PrivateKey: privateKey, Leaf: leafCertificate, } + minTLSVersionNumber, err := parseTLSVersionName(tlsSettings.MinTLSVersion) + if err != nil { + return nil, errors.Wrap(err, "parsing MinTLSVersion from config") + } tlsConfig := &tls.Config{ MaxVersion: tls.VersionTLS13, - MinVersion: tls.VersionTLS12, + MinVersion: minTLSVersionNumber, Certificates: []tls.Certificate{ tlsCert, }, @@ -226,8 +232,13 @@ func getTLSConfigFromKeyVault(tlsSettings localtls.TlsSettings, errChan chan<- e errChan <- cr.Refresh(ctx, tlsSettings.KeyVaultCertificateRefreshInterval) }() + minTLSVersionNumber, err := parseTLSVersionName(tlsSettings.MinTLSVersion) + if err != nil { + return nil, errors.Wrap(err, "parsing MinTLSVersion from config") + } + tlsConfig := tls.Config{ - MinVersion: tls.VersionTLS12, + MinVersion: minTLSVersionNumber, MaxVersion: tls.VersionTLS13, GetCertificate: func(_ *tls.ClientHelloInfo) (*tls.Certificate, error) { return cr.GetCertificate(), nil @@ -316,3 +327,16 @@ func (service *Service) SendErrorResponse(w http.ResponseWriter, errMsg error) { err := acn.Encode(w, &resp) logger.Errorf("[%s] %+v %s.", service.Name, &resp, err.Error()) } + +// parseTLSVersionName returns the version number for the provided TLS version name +// (e.g. 0x0301) +func parseTLSVersionName(versionName string) (uint16, error) { + switch versionName { + case "TLS 1.2": + return tls.VersionTLS12, nil + case "TLS 1.3": + return tls.VersionTLS13, nil + default: + return 0, errors.Wrapf(errTLSConfig, "version name %s", versionName) + } +} diff --git a/cns/service/main.go b/cns/service/main.go index ffe51ca9c..5438115eb 100644 --- a/cns/service/main.go +++ b/cns/service/main.go @@ -776,6 +776,7 @@ func main() { MSIResourceID: cnsconfig.MSISettings.ResourceID, KeyVaultCertificateRefreshInterval: time.Duration(cnsconfig.KeyVaultSettings.RefreshIntervalInHrs) * time.Hour, UseMTLS: cnsconfig.UseMTLS, + MinTLSVersion: cnsconfig.MinTLSVersion, } } diff --git a/cns/service_test.go b/cns/service_test.go index 9bf4af8ce..d20c2ef11 100644 --- a/cns/service_test.go +++ b/cns/service_test.go @@ -76,6 +76,7 @@ func TestNewService(t *testing.T) { TLSPort: "10091", TLSSubjectName: "localhost", TLSCertificatePath: testCertFilePath, + MinTLSVersion: "TLS 1.2", } svc, err := NewService(config.Name, config.Version, config.ChannelMode, config.Store) @@ -94,10 +95,13 @@ func TestNewService(t *testing.T) { err = svc.StartListener(config) require.NoError(t, err) + minTLSVersionNumber, err := parseTLSVersionName(config.TLSSettings.MinTLSVersion) + require.NoError(t, err) + tlsClient := &http.Client{ Transport: &http.Transport{ TLSClientConfig: &tls.Config{ - MinVersion: tls.VersionTLS12, + MinVersion: minTLSVersionNumber, MaxVersion: tls.VersionTLS13, ServerName: config.TLSSettings.TLSSubjectName, // #nosec G402 for test purposes only @@ -134,6 +138,7 @@ func TestNewService(t *testing.T) { TLSSubjectName: "localhost", TLSCertificatePath: testCertFilePath, UseMTLS: true, + MinTLSVersion: "TLS 1.2", } svc, err := NewService(config.Name, config.Version, config.ChannelMode, config.Store) @@ -322,3 +327,31 @@ func createTestCertificate(t *testing.T) string { return testCertFilePath } + +func TestTLSVersionNumber(t *testing.T) { + t.Run("unsupported ServerSettings.MinTLSVersion TLS 1.0", func(t *testing.T) { + versionNumber, err := parseTLSVersionName("TLS 1.0") + require.Equal(t, uint16(0), versionNumber) + require.Error(t, err) + require.ErrorContains(t, err, "unsupported TLS version name") + }) + + t.Run("unsupported ServerSettings.MinTLSVersion TLS 1.1", func(t *testing.T) { + versionNumber, err := parseTLSVersionName("TLS 1.1") + require.Equal(t, uint16(0), versionNumber) + require.Error(t, err) + require.ErrorContains(t, err, "unsupported TLS version name") + }) + t.Run("unsupported ServerSettings.MinTLSVersion TLS 1.4", func(t *testing.T) { + versionNumber, err := parseTLSVersionName("TLS 1.4") + require.Equal(t, uint16(0), versionNumber) + require.Error(t, err) + require.ErrorContains(t, err, "unsupported TLS version name") + }) + + t.Run("valid ServerSettings.MinTLSVersion", func(t *testing.T) { + versionNumber, err := parseTLSVersionName("TLS 1.2") + require.Equal(t, uint16(tls.VersionTLS12), versionNumber) + require.NoError(t, err) + }) +} diff --git a/server/tls/tlscertificate_retriever.go b/server/tls/tlscertificate_retriever.go index d3037815b..a22a7336b 100644 --- a/server/tls/tlscertificate_retriever.go +++ b/server/tls/tlscertificate_retriever.go @@ -14,6 +14,7 @@ type TlsSettings struct { MSIResourceID string KeyVaultCertificateRefreshInterval time.Duration UseMTLS bool + MinTLSVersion string } func GetTlsCertificateRetriever(settings TlsSettings) (TlsCertificateRetriever, error) {