diff --git a/cmd/notary-server/main.go b/cmd/notary-server/main.go index d581c9988e..2ba9a66480 100644 --- a/cmd/notary-server/main.go +++ b/cmd/notary-server/main.go @@ -70,6 +70,30 @@ func serverTLS(configuration *viper.Viper) (*tls.Config, error) { return tlsConfig, nil } +// sets up TLS for the GRPC connection to notary-signer +func grpcTLS(configuration *viper.Viper) (*tls.Config, error) { + rootCA := configuration.GetString("trust_service.tls_ca_file") + serverName := configuration.GetString("trust_service.hostname") + clientCert := configuration.GetString("trust_service.tls_client_cert") + clientKey := configuration.GetString("trust_service.tls_client_key") + + if (clientCert == "" && clientKey != "") || (clientCert != "" && clientKey == "") { + return nil, fmt.Errorf("Partial TLS configuration found. Either include both a client cert and client key file in the configuration, or include neither.") + } + + tlsConfig, err := utils.ConfigureClientTLS(&utils.ClientTLSOpts{ + RootCAFile: rootCA, + ServerName: serverName, + ClientCertFile: clientCert, + ClientKeyFile: clientKey, + }) + if err != nil { + return nil, fmt.Errorf( + "Unable to configure TLS to the trust service: %s", err.Error()) + } + return tlsConfig, nil +} + func main() { flag.Usage = usage flag.Parse() @@ -135,10 +159,14 @@ func main() { var trust signed.CryptoService if mainViper.GetString("trust_service.type") == "remote" { logrus.Info("Using remote signing service") + clientTLS, err := grpcTLS(mainViper) + if err != nil { + logrus.Fatal(err.Error()) + } notarySigner := signer.NewNotarySigner( mainViper.GetString("trust_service.hostname"), mainViper.GetString("trust_service.port"), - mainViper.GetString("trust_service.tls_ca_file"), + clientTLS, ) trust = notarySigner minute := 1 * time.Minute diff --git a/cmd/notary-server/main_test.go b/cmd/notary-server/main_test.go index bd04a125dc..0ab2dfbbca 100644 --- a/cmd/notary-server/main_test.go +++ b/cmd/notary-server/main_test.go @@ -32,6 +32,7 @@ func TestServerTLSMissingCertAndKey(t *testing.T) { assert.Nil(t, tlsConfig) } +// Cert and Key either both have to be empty or both have to be provided. func TestServerTLSMissingCertAndOrKey(t *testing.T) { configs := []string{ fmt.Sprintf(`{"tls_cert_file": "%s"}`, Cert), @@ -63,7 +64,7 @@ func TestServerTLSSuccess(t *testing.T) { assert.Equal(t, []tls.Certificate{keypair}, tlsConfig.Certificates) } -// The rest of the functionality of singerTLS depends upon +// The rest of the functionality of serverTLS depends upon // utils.ConfigureServerTLS, so this test just asserts that if it fails, // the error is propogated. func TestServerTLSFailure(t *testing.T) { @@ -75,3 +76,69 @@ func TestServerTLSFailure(t *testing.T) { assert.Nil(t, tlsConfig) assert.True(t, strings.Contains(err.Error(), "Unable to set up TLS")) } + +// Client cert and Key either both have to be empty or both have to be +// provided. +func TestGrpcTLSMissingCertOrKey(t *testing.T) { + configs := []string{ + fmt.Sprintf(`"tls_client_cert": "%s"`, Cert), + fmt.Sprintf(`"tls_client_key": "%s"`, Key), + } + for _, trustConfig := range configs { + jsonConfig := fmt.Sprintf( + `{"trust_service": {"hostname": "notary-signer", %s}}`, + trustConfig) + config := configure([]byte(jsonConfig)) + tlsConfig, err := grpcTLS(config) + assert.Error(t, err) + assert.Nil(t, tlsConfig) + assert.True(t, + strings.Contains(err.Error(), "Partial TLS configuration found.")) + } +} + +// If no TLS configuration is provided for the host server, a tls config with +// the provided serverName is still returned. +func TestGrpcTLSNoConfig(t *testing.T) { + tlsConfig, err := grpcTLS( + configure([]byte(`{"trust_service": {"hostname": "notary-signer"}}`))) + assert.NoError(t, err) + assert.Equal(t, "notary-signer", tlsConfig.ServerName) + assert.Nil(t, tlsConfig.RootCAs) + assert.Nil(t, tlsConfig.Certificates) +} + +// The rest of the functionality of grpcTLS depends upon +// utils.ConfigureClientTLS, so this test just asserts that if successful, +// the correct tls.Config is returned based on all the configuration parameters +func TestGrpcTLSSuccess(t *testing.T) { + keypair, err := tls.LoadX509KeyPair(Cert, Key) + assert.NoError(t, err, "Unable to load cert and key for testing") + + config := fmt.Sprintf( + `{"trust_service": { + "hostname": "notary-server", + "tls_client_cert": "%s", + "tls_client_key": "%s"}}`, + Cert, Key) + tlsConfig, err := grpcTLS(configure([]byte(config))) + assert.NoError(t, err) + assert.Equal(t, []tls.Certificate{keypair}, tlsConfig.Certificates) +} + +// The rest of the functionality of grpcTLS depends upon +// utils.ConfigureServerTLS, so this test just asserts that if it fails, +// the error is propogated. +func TestGrpcTLSFailure(t *testing.T) { + config := fmt.Sprintf( + `{"trust_service": { + "hostname": "notary-server", + "tls_client_cert": "no-exist", + "tls_client_key": "%s"}}`, + Key) + tlsConfig, err := grpcTLS(configure([]byte(config))) + assert.Error(t, err) + assert.Nil(t, tlsConfig) + assert.True(t, strings.Contains(err.Error(), + "Unable to configure TLS to the trust service")) +} diff --git a/cmd/notary-signer/main_test.go b/cmd/notary-signer/main_test.go index 8dcf26f89e..15b2c2ad1e 100644 --- a/cmd/notary-signer/main_test.go +++ b/cmd/notary-signer/main_test.go @@ -41,7 +41,7 @@ func TestSignerTLSMissingCertAndOrKey(t *testing.T) { } } -// The rest of the functionality of singerTLS depends upon +// The rest of the functionality of signerTLS 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) { @@ -57,7 +57,7 @@ func TestSignerTLSSuccess(t *testing.T) { assert.NotNil(t, tlsConfig.ClientCAs) } -// The rest of the functionality of singerTLS depends upon +// The rest of the functionality of signerTLS depends upon // utils.ConfigureServerTLS, so this test just asserts that if it fails, // the error is propogated. func TestSignerTLSFailure(t *testing.T) { diff --git a/signer/signer_trust.go b/signer/signer_trust.go index a1cb082c48..e999d5fff2 100644 --- a/signer/signer_trust.go +++ b/signer/signer_trust.go @@ -1,14 +1,14 @@ package signer import ( + "crypto/tls" "fmt" "net" "time" "github.com/Sirupsen/logrus" pb "github.com/docker/notary/proto" - "github.com/docker/notary/tuf/data" - "github.com/docker/notary/utils" + "github.com/endophage/gotuf/data" "golang.org/x/net/context" "google.golang.org/grpc" "google.golang.org/grpc/codes" @@ -28,16 +28,9 @@ type NotarySigner struct { } // NewNotarySigner is a convinience method that returns NotarySigner -func NewNotarySigner(hostname string, port string, tlscafile string) *NotarySigner { +func NewNotarySigner(hostname string, port string, tlsConfig *tls.Config) *NotarySigner { var opts []grpc.DialOption netAddr := net.JoinHostPort(hostname, port) - tlsConfig, err := utils.ConfigureClientTLS(&utils.ClientTLSOpts{ - RootCAFile: tlscafile, - ServerName: hostname, - }) - if err != nil { - logrus.Fatal("Unable to set up TLS: ", err) - } creds := credentials.NewTLS(tlsConfig) opts = append(opts, grpc.WithTransportCredentials(creds)) conn, err := grpc.Dial(netAddr, opts...)