From bbf941d198e7ad86abcdf5e8eb700df64fa0b308 Mon Sep 17 00:00:00 2001 From: Ying Li Date: Mon, 19 Oct 2015 11:27:34 -0700 Subject: [PATCH] Allow client CAs to be provided to notary-signer. Signed-off-by: Ying Li --- cmd/notary-signer/main.go | 47 ++++++++++++++--------- cmd/notary-signer/main_test.go | 70 ++++++++++++++++++++++++++++++++++ 2 files changed, 100 insertions(+), 17 deletions(-) diff --git a/cmd/notary-signer/main.go b/cmd/notary-signer/main.go index 892bdeec96..b17819dc43 100644 --- a/cmd/notary-signer/main.go +++ b/cmd/notary-signer/main.go @@ -1,10 +1,12 @@ package main import ( + "crypto/tls" "database/sql" "errors" _ "expvar" "flag" + "fmt" "log" "net" "net/http" @@ -68,6 +70,30 @@ func passphraseRetriever(keyName, alias string, createNew bool, attempts int) (p return passphrase, false, nil } +// parses and sets up the TLS for the signer http + grpc server +func signerTLS(configuration *viper.Viper, printUsage bool) (*tls.Config, error) { + certFile := configuration.GetString("server.cert_file") + keyFile := configuration.GetString("server.key_file") + if certFile == "" || keyFile == "" { + if printUsage { + usage() + } + return nil, fmt.Errorf("Certificate and key are mandatory") + } + + clientCAFile := configuration.GetString("server.client_ca_file") + tlsConfig, err := utils.ConfigureServerTLS(&utils.ServerTLSOpts{ + ServerCertFile: certFile, + ServerKeyFile: keyFile, + RequireClientAuth: clientCAFile != "", + ClientCAFile: clientCAFile, + }) + if err != nil { + return nil, fmt.Errorf("Unable to set up TLS: %s", err.Error()) + } + return tlsConfig, nil +} + func main() { flag.Usage = usage flag.Parse() @@ -95,19 +121,9 @@ func main() { logrus.SetLevel(logrus.Level(mainViper.GetInt("logging.level"))) - certFile := mainViper.GetString("server.cert_file") - keyFile := mainViper.GetString("server.key_file") - if certFile == "" || keyFile == "" { - usage() - log.Fatalf("Certificate and key are mandatory") - } - - tlsConfig, err := utils.ConfigureServerTLS(&utils.ServerTLSOpts{ - ServerCertFile: certFile, - ServerKeyFile: keyFile, - }) + tlsConfig, err := signerTLS(mainViper, true) if err != nil { - logrus.Fatalf("Unable to set up TLS: %s", err.Error()) + logrus.Fatalf(err.Error()) } cryptoServices := make(signer.CryptoServiceIndex) @@ -163,10 +179,7 @@ func main() { if err != nil { log.Fatalf("failed to listen %v", err) } - creds, err := credentials.NewServerTLSFromFile(certFile, keyFile) - if err != nil { - log.Fatalf("failed to generate credentials %v", err) - } + creds := credentials.NewTLS(tlsConfig) opts := []grpc.ServerOption{grpc.Creds(creds)} grpcServer := grpc.NewServer(opts...) @@ -191,7 +204,7 @@ func main() { log.Println("HTTP server listening on", httpAddr) } - err = server.ListenAndServeTLS(certFile, keyFile) + err = server.ListenAndServe() if err != nil { log.Fatal("HTTP server failed to start:", err) } diff --git a/cmd/notary-signer/main_test.go b/cmd/notary-signer/main_test.go index 06ab7d0f9a..8dcf26f89e 100644 --- a/cmd/notary-signer/main_test.go +++ b/cmd/notary-signer/main_test.go @@ -1 +1,71 @@ package main + +import ( + "bytes" + "crypto/tls" + "fmt" + "strings" + "testing" + + "github.com/spf13/viper" + "github.com/stretchr/testify/assert" +) + +const ( + Cert = "../../fixtures/notary-signer.crt" + Key = "../../fixtures/notary-signer.key" + Root = "../../fixtures/root-ca.crt" +) + +// initializes a viper object with test configuration +func configure(jsonConfig []byte) *viper.Viper { + config := viper.New() + config.SetConfigType("json") + config.ReadConfig(bytes.NewBuffer(jsonConfig)) + return config +} + +func TestSignerTLSMissingCertAndOrKey(t *testing.T) { + configs := []string{ + "{}", + fmt.Sprintf(`{"cert_file": "%s"}`, Cert), + fmt.Sprintf(`{"key_file": "%s"}`, Key), + } + for _, serverConfig := range configs { + config := configure( + []byte(fmt.Sprintf(`{"server": %s}`, serverConfig))) + tlsConfig, err := signerTLS(config, false) + assert.Error(t, err) + assert.Nil(t, tlsConfig) + assert.Equal(t, "Certificate and key are mandatory", err.Error()) + } +} + +// The rest of the functionality of singerTLS depends upon +// utils.ConfigureServerTLS, so this test just asserts that if successful, +// the correct tls.Config is returned based on all the configuration parameters +func TestSignerTLSSuccess(t *testing.T) { + keypair, err := tls.LoadX509KeyPair(Cert, Key) + assert.NoError(t, err, "Unable to load cert and key for testing") + + config := fmt.Sprintf( + `{"server": {"cert_file": "%s", "key_file": "%s", "client_ca_file": "%s"}}`, + Cert, Key, Cert) + tlsConfig, err := signerTLS(configure([]byte(config)), false) + assert.NoError(t, err) + assert.Equal(t, []tls.Certificate{keypair}, tlsConfig.Certificates) + assert.NotNil(t, tlsConfig.ClientCAs) +} + +// The rest of the functionality of singerTLS depends upon +// utils.ConfigureServerTLS, so this test just asserts that if it fails, +// the error is propogated. +func TestSignerTLSFailure(t *testing.T) { + config := fmt.Sprintf( + `{"server": {"cert_file": "%s", "key_file": "%s", "client_ca_file": "%s"}}`, + Cert, Key, "non-existant") + tlsConfig, err := signerTLS(configure([]byte(config)), false) + assert.Error(t, err) + assert.Nil(t, tlsConfig) + assert.True(t, strings.Contains(err.Error(), "Unable to set up TLS")) +}