From b399783eeef9e5cb79cd0bb0fafc772d906cd800 Mon Sep 17 00:00:00 2001 From: Ying Li Date: Fri, 16 Oct 2015 18:31:03 -0700 Subject: [PATCH] Slight refactoring of ConfigureServerTLS and added a ConfigureClientTLS as well. Signed-off-by: Ying Li --- utils/tls_config.go | 66 ++++++++++++++++++++++++--------- utils/tls_config_test.go | 79 +++++++++++++++++++++++++++++----------- 2 files changed, 107 insertions(+), 38 deletions(-) diff --git a/utils/tls_config.go b/utils/tls_config.go index 57905d2faa..3b3885b7de 100644 --- a/utils/tls_config.go +++ b/utils/tls_config.go @@ -3,6 +3,7 @@ package utils import ( "crypto/rand" "crypto/tls" + "crypto/x509" "fmt" "os" @@ -38,25 +39,56 @@ func ConfigureServerTLS(serverCert string, serverKey string, clientAuth bool, ca if clientAuth { tlsConfig.ClientAuth = tls.RequireAndVerifyClientCert - if caCertDir != "" { - // Check to see if the given directory exists - fi, err := os.Stat(caCertDir) - if err != nil { - return nil, err - } - if !fi.IsDir() { - return nil, fmt.Errorf("No such directory: %s", caCertDir) - } + } - certStore, err := trustmanager.NewX509FileStore(caCertDir) - if err != nil { - return nil, err - } - if certStore.Empty() { - return nil, fmt.Errorf("No certificates in %s", caCertDir) - } - tlsConfig.ClientCAs = certStore.GetCertificatePool() + if caCertDir != "" { + // Check to see if the given directory exists + fi, err := os.Stat(caCertDir) + if err != nil { + return nil, err } + if !fi.IsDir() { + return nil, fmt.Errorf("No such directory: %s", caCertDir) + } + + certStore, err := trustmanager.NewX509FileStore(caCertDir) + if err != nil { + return nil, err + } + if certStore.Empty() { + return nil, fmt.Errorf("No certificates in %s", caCertDir) + } + tlsConfig.ClientCAs = certStore.GetCertificatePool() + } + + return tlsConfig, nil +} + +// ConfigureClientTLS generates a tls configuration for clients using the +// provided. +func ConfigureClientTLS(rootCA string, skipVerify bool, clientCert string, clientKey string) (*tls.Config, error) { + tlsConfig := &tls.Config{ + InsecureSkipVerify: skipVerify, + MinVersion: tls.VersionTLS12, + } + + if rootCA != "" { + rootCert, err := trustmanager.LoadCertFromFile(rootCA) + if err != nil { + return nil, fmt.Errorf( + "Could not load root ca file. %s", err.Error()) + } + rootPool := x509.NewCertPool() + rootPool.AddCert(rootCert) + tlsConfig.RootCAs = rootPool + } + + if clientCert != "" && clientKey != "" { + keypair, err := tls.LoadX509KeyPair(clientCert, clientKey) + if err != nil { + return nil, err + } + tlsConfig.Certificates = []tls.Certificate{keypair} } return tlsConfig, nil diff --git a/utils/tls_config_test.go b/utils/tls_config_test.go index 4d738a15c6..c86ddcf12c 100644 --- a/utils/tls_config_test.go +++ b/utils/tls_config_test.go @@ -35,8 +35,8 @@ func makeTempCertDir(t *testing.T) string { return tempDir } -// TestTLSConfigFailsIfUnableToLoadCerts fails if unable to load either of the -// server files or the client cert info +// If the cert files and directory are provided but are invalid, an error is +// returned. func TestConfigServerTLSFailsIfUnableToLoadCerts(t *testing.T) { tempDir := makeTempCertDir(t) @@ -50,9 +50,8 @@ func TestConfigServerTLSFailsIfUnableToLoadCerts(t *testing.T) { } } -// TestConfigServerTLSServerCertsOnly returns a valid tls config with the -// provided server certificate, and since clientAuth was false, no client auth -// or CAs configured. +// If server cert and key are provided, and client auth is disabled, then +// a valid tls.Config is returned with ClientAuth set to NoClientCert func TestConfigServerTLSServerCertsOnly(t *testing.T) { keypair, err := tls.LoadX509KeyPair(ServerCert, ServerKey) assert.NoError(t, err) @@ -65,10 +64,10 @@ func TestConfigServerTLSServerCertsOnly(t *testing.T) { assert.Nil(t, tlsConfig.ClientCAs) } -// TestConfigServerTLSNoCACertsIfNoClientAuth returns a valid tls config with -// the provided server certificate, and since clientAuth was false, no client -// auth or CAs configured even though a client CA cert was provided. -func TestConfigServerTLSNoCACertsIfNoClientAuth(t *testing.T) { +// If server cert and key are provided, and client cert directory is provided, +// a valid tls.Config is returned with the clientCAs set to the certs in that +// directory. +func TestConfigServerTLSWithCACerts(t *testing.T) { tempDir := makeTempCertDir(t) keypair, err := tls.LoadX509KeyPair(ServerCert, ServerKey) assert.NoError(t, err) @@ -78,12 +77,13 @@ func TestConfigServerTLSNoCACertsIfNoClientAuth(t *testing.T) { assert.Equal(t, []tls.Certificate{keypair}, tlsConfig.Certificates) assert.True(t, tlsConfig.PreferServerCipherSuites) assert.Equal(t, tls.NoClientCert, tlsConfig.ClientAuth) - assert.Nil(t, tlsConfig.ClientCAs) + assert.Len(t, tlsConfig.ClientCAs.Subjects(), 1) } -// TestTLSConfigClientAuthEnabledNoCACerts returns a valid tls config with the -// provided server certificate client auth enabled, but no CAs configured. -func TestTLSConfigClientAuthEnabledNoCACerts(t *testing.T) { +// If server cert and key are provided, and client auth is disabled, then +// a valid tls.Config is returned with ClientAuth set to +// RequireAndVerifyClientCert +func TestConfigServerTLSClientAuthEnabled(t *testing.T) { keypair, err := tls.LoadX509KeyPair(ServerCert, ServerKey) assert.NoError(t, err) @@ -95,17 +95,54 @@ func TestTLSConfigClientAuthEnabledNoCACerts(t *testing.T) { assert.Nil(t, tlsConfig.ClientCAs) } -// TestTLSConfigClientAuthEnabledWithCACert returns a valid tls config with the -// provided server certificate, client auth enabled, and a client CA. -func TestTLSConfigClientAuthEnabledWithCACert(t *testing.T) { - tempDir := makeTempCertDir(t) +// The skipVerify boolean gets set on the tls.Config's InsecureSkipBoolean +func TestConfigClientTLSNoVerify(t *testing.T) { + for _, skip := range []bool{true, false} { + tlsConfig, err := ConfigureClientTLS("", skip, "", "") + assert.NoError(t, err) + assert.Nil(t, tlsConfig.Certificates) + assert.Equal(t, skip, tlsConfig.InsecureSkipVerify) + assert.Nil(t, tlsConfig.RootCAs) + } +} + +// The RootCA is set if it is provided and valid +func TestConfigClientTLSValidRootCA(t *testing.T) { + tlsConfig, err := ConfigureClientTLS(RootCA, false, "", "") + assert.NoError(t, err) + assert.Nil(t, tlsConfig.Certificates) + assert.Equal(t, false, tlsConfig.InsecureSkipVerify) + assert.Len(t, tlsConfig.RootCAs.Subjects(), 1) +} + +// An error is returned if a root CA is provided but not valid +func TestConfigClientTLSInValidRootCA(t *testing.T) { + tlsConfig, err := ConfigureClientTLS("not-a-file.crt", false, "", "") + assert.Error(t, err) + assert.Nil(t, tlsConfig) +} + +// An error is returned if either the client cert or the key are provided +// but invalid. +func TestConfigClientTLSClientCertOrKeyInvalid(t *testing.T) { + for i := 0; i < 2; i++ { + files := []string{ServerCert, ServerKey} + files[i] = "not-a-file.crt" + tlsConfig, err := ConfigureClientTLS("", false, files[0], files[1]) + assert.Error(t, err) + assert.Nil(t, tlsConfig) + } +} + +// The certificate is set if the client cert and client key are provided and +// valid. +func TestConfigClientTLSValidClientCertAndKey(t *testing.T) { keypair, err := tls.LoadX509KeyPair(ServerCert, ServerKey) assert.NoError(t, err) - tlsConfig, err := ConfigureServerTLS(ServerCert, ServerKey, true, tempDir) + tlsConfig, err := ConfigureClientTLS("", false, ServerCert, ServerKey) assert.NoError(t, err) assert.Equal(t, []tls.Certificate{keypair}, tlsConfig.Certificates) - assert.True(t, tlsConfig.PreferServerCipherSuites) - assert.Equal(t, tls.RequireAndVerifyClientCert, tlsConfig.ClientAuth) - assert.Equal(t, 1, len(tlsConfig.ClientCAs.Subjects())) + assert.Equal(t, false, tlsConfig.InsecureSkipVerify) + assert.Nil(t, tlsConfig.RootCAs) }