diff --git a/cmd/notary-server/config.go b/cmd/notary-server/config.go index 61e9d5f722..8c9adb8924 100644 --- a/cmd/notary-server/config.go +++ b/cmd/notary-server/config.go @@ -89,7 +89,12 @@ func getStore(configuration *viper.Viper, hRegister healthRegister) ( if err != nil { return nil, err } - sess, err = rethinkdb.Connection(storeConfig.CA, storeConfig.Source) + tlsOpts := tlsconfig.Options{ + CAFile: storeConfig.CA, + CertFile: storeConfig.Cert, + KeyFile: storeConfig.Key, + } + sess, err = rethinkdb.Connection(tlsOpts, storeConfig.Source) if err != nil { return nil, fmt.Errorf("Error starting %s driver: %s", backend, err.Error()) } diff --git a/cmd/notary-server/main_test.go b/cmd/notary-server/main_test.go index ffb87cea26..6302313209 100644 --- a/cmd/notary-server/main_test.go +++ b/cmd/notary-server/main_test.go @@ -343,6 +343,8 @@ func TestGetStoreRethinkDBStoreConnectionFails(t *testing.T) { "backend": "%s", "db_url": "host:port", "tls_ca_file": "/tls/ca.pem", + "client_cert_file": "/tls/cert.pem", + "client_key_file": "/tls/key.pem", "database": "rethinkdbtest" } }`, diff --git a/cmd/notary-signer/config.go b/cmd/notary-signer/config.go index 292555874e..4910a25b58 100644 --- a/cmd/notary-signer/config.go +++ b/cmd/notary-signer/config.go @@ -16,6 +16,7 @@ import ( "github.com/Sirupsen/logrus" "github.com/dancannon/gorethink" "github.com/docker/distribution/health" + "github.com/docker/go-connections/tlsconfig" "github.com/docker/notary" "github.com/docker/notary/cryptoservice" "github.com/docker/notary/passphrase" @@ -116,7 +117,12 @@ func setUpCryptoservices(configuration *viper.Viper, allowedBackends []string) ( if err != nil { return nil, err } - sess, err = rethinkdb.Connection(storeConfig.CA, storeConfig.Source) + tlsOpts := tlsconfig.Options{ + CAFile: storeConfig.CA, + CertFile: storeConfig.Cert, + KeyFile: storeConfig.Key, + } + sess, err = rethinkdb.Connection(tlsOpts, storeConfig.Source) if err != nil { return nil, err } diff --git a/cmd/notary-signer/main_test.go b/cmd/notary-signer/main_test.go index 4cbd6a343f..4fdb02e3ea 100644 --- a/cmd/notary-signer/main_test.go +++ b/cmd/notary-signer/main_test.go @@ -121,6 +121,8 @@ func TestSetupCryptoServicesRethinkDBStoreNoDefaultAlias(t *testing.T) { "backend": "%s", "db_url": "host:port", "tls_ca_file": "/tls/ca.pem", + "client_cert_file": "/tls/cert.pem", + "client_key_file": "/tls/key.pem", "database": "rethinkdbtest" } }`, @@ -137,7 +139,9 @@ func TestSetupCryptoServicesRethinkDBStoreConnectionFails(t *testing.T) { `{"storage": { "backend": "%s", "db_url": "host:port", - "tls_ca_file": "../../fixtures/root-ca.crt", + "tls_ca_file": "../../fixtures/rethinkdb/ca.pem", + "client_cert_file": "../../fixtures/rethinkdb/cert.pem", + "client_key_file": "../../fixtures/rethinkdb/key.pem", "database": "rethinkdbtest" }, "default_alias": "timestamp" diff --git a/fixtures/server-config.rethink.json b/fixtures/server-config.rethink.json index 936275c938..06c3a909eb 100644 --- a/fixtures/server-config.rethink.json +++ b/fixtures/server-config.rethink.json @@ -20,6 +20,8 @@ "backend": "rethinkdb", "db_url": "rdb-proxy.rdb", "database": "notaryserver", - "tls_ca_file": "./rethinkdb/ca.pem" + "tls_ca_file": "./rethinkdb/ca.pem", + "client_key_file": "./rethinkdb/key.pem", + "client_cert_file": "./rethinkdb/cert.pem" } } diff --git a/fixtures/signer-config.rethink.json b/fixtures/signer-config.rethink.json index 6f61bc7f3b..a36e7e6b3d 100644 --- a/fixtures/signer-config.rethink.json +++ b/fixtures/signer-config.rethink.json @@ -13,6 +13,8 @@ "backend": "rethinkdb", "db_url": "rdb-proxy.rdb", "database": "notarysigner", - "tls_ca_file": "./rethinkdb/ca.pem" + "tls_ca_file": "./rethinkdb/ca.pem", + "client_key_file": "./rethinkdb/key.pem", + "client_cert_file": "./rethinkdb/cert.pem" } } diff --git a/storage/rethinkdb/rethinkdb.go b/storage/rethinkdb/rethinkdb.go index d272f2c806..17146a4754 100644 --- a/storage/rethinkdb/rethinkdb.go +++ b/storage/rethinkdb/rethinkdb.go @@ -19,10 +19,7 @@ type Timing struct { // Connection sets up a RethinkDB connection to the host (`host:port` format) // using the CA .pem file provided at path `caFile` -func Connection(caFile, host string) (*gorethink.Session, error) { - tlsOpts := tlsconfig.Options{ - CAFile: caFile, - } +func Connection(tlsOpts tlsconfig.Options, host string) (*gorethink.Session, error) { t, err := tlsconfig.Client(tlsOpts) if err != nil { return nil, err diff --git a/utils/configuration.go b/utils/configuration.go index cdf6af0039..d0ae87222a 100644 --- a/utils/configuration.go +++ b/utils/configuration.go @@ -27,7 +27,9 @@ type Storage struct { type RethinkDBStorage struct { Storage CA string + Cert string DBName string + Key string } // GetPathRelativeToConfig gets a configuration key which is a path, and if @@ -117,6 +119,8 @@ func ParseRethinkDBStorage(configuration *viper.Viper) (*RethinkDBStorage, error Source: configuration.GetString("storage.db_url"), }, CA: GetPathRelativeToConfig(configuration, "storage.tls_ca_file"), + Cert: GetPathRelativeToConfig(configuration, "storage.client_cert_file"), + Key: GetPathRelativeToConfig(configuration, "storage.client_key_file"), DBName: configuration.GetString("storage.database"), } @@ -136,6 +140,11 @@ func ParseRethinkDBStorage(configuration *viper.Viper) (*RethinkDBStorage, error "cowardly refusal to connect to %s without a CA cert", store.Backend, ) + case store.Cert == "" || store.Key == "": + return nil, fmt.Errorf( + "cowardly refusal to connect to %s without a client cert and key", + store.Backend, + ) case store.DBName == "": return nil, fmt.Errorf( "%s requires a specific database to connect to", diff --git a/utils/configuration_test.go b/utils/configuration_test.go index 806d49ed5a..ff23707ab5 100644 --- a/utils/configuration_test.go +++ b/utils/configuration_test.go @@ -215,6 +215,8 @@ func TestParseRethinkStorageDBStoreInvalidBackend(t *testing.T) { "backend": "mysql", "db_url": "username:password@tcp(hostname:1234)/dbname", "tls_ca_file": "/tls/ca.pem", + "client_cert_file": "/tls/cert.pem", + "client_key_file": "/tls/key.pem", "database": "rethinkdbtest" } }`) @@ -230,6 +232,8 @@ func TestParseRethinkStorageDBStoreEmptyDBUrl(t *testing.T) { "storage": { "backend": "rethinkdb", "tls_ca_file": "/tls/ca.pem", + "client_cert_file": "/tls/cert.pem", + "client_key_file": "/tls/key.pem", "database": "rethinkdbtest" } }`) @@ -245,7 +249,9 @@ func TestParseRethinkStorageDBStoreEmptyDBName(t *testing.T) { "storage": { "backend": "rethinkdb", "db_url": "username:password@tcp(hostname:1234)/dbname", - "tls_ca_file": "/tls/ca.pem" + "tls_ca_file": "/tls/ca.pem", + "client_cert_file": "/tls/cert.pem", + "client_key_file": "/tls/key.pem" } }`) @@ -260,7 +266,9 @@ func TestParseRethinkStorageDBStoreEmptyCA(t *testing.T) { "storage": { "backend": "rethinkdb", "db_url": "username:password@tcp(hostname:1234)/dbname", - "database": "rethinkdbtest" + "database": "rethinkdbtest", + "client_cert_file": "/tls/cert.pem", + "client_key_file": "/tls/key.pem" } }`) @@ -269,6 +277,22 @@ func TestParseRethinkStorageDBStoreEmptyCA(t *testing.T) { require.Contains(t, err.Error(), "cowardly refusal to connect to rethinkdb without a CA cert") } +// ParseRethinkDBStorage will require a client cert and key to connect to rethink databases +func TestParseRethinkStorageDBStoreEmptyCertAndKey(t *testing.T) { + config := configure(`{ + "storage": { + "backend": "rethinkdb", + "db_url": "username:password@tcp(hostname:1234)/dbname", + "database": "rethinkdbtest", + "tls_ca_file": "/tls/ca.pem" + } + }`) + + _, err := ParseRethinkDBStorage(config) + require.Error(t, err) + require.Contains(t, err.Error(), "cowardly refusal to connect to rethinkdb without a client cert") +} + func TestParseSQLStorageWithEnvironmentVariables(t *testing.T) { config := configure(`{ "storage": {