From eb38c36f809fd78530878eb9f9cb0ca379ffe5d4 Mon Sep 17 00:00:00 2001 From: Michael Finley Date: Wed, 7 Nov 2018 14:54:51 -0600 Subject: [PATCH] MF-448 - Option for Postgres SSL Mode (#449) * MF-448 - Option for Postgres SSL Mode Adds an option to choose the ssl mode when connecting to postgres. Only supporting disable or require for now with verify-ca and verify-full to come after more discussion. Signed-off-by: Michael Finley * Changing package name back Accidentally changed the package name so reverting that! Signed-off-by: Michael Finley * Adding section in getting-started for securing pgsql connections Signed-off-by: Michael Finley --- cmd/things/main.go | 6 +++++- cmd/users/main.go | 6 +++++- docs/getting-started.md | 13 +++++++++++- things/README.md | 38 ++++++++++++++++++----------------- things/postgres/init.go | 4 ++-- things/postgres/setup_test.go | 2 +- users/README.md | 30 ++++++++++++++------------- users/postgres/init.go | 5 ++--- users/postgres/setup_test.go | 2 +- 9 files changed, 64 insertions(+), 42 deletions(-) diff --git a/cmd/things/main.go b/cmd/things/main.go index 8f44febf1..a5f2270a3 100644 --- a/cmd/things/main.go +++ b/cmd/things/main.go @@ -43,6 +43,7 @@ const ( defDBUser = "mainflux" defDBPass = "mainflux" defDBName = "things" + defDBSSLMode = "disable" defCACerts = "" defCacheURL = "localhost:6379" defCachePass = "" @@ -58,6 +59,7 @@ const ( envDBUser = "MF_THINGS_DB_USER" envDBPass = "MF_THINGS_DB_PASS" envDBName = "MF_THINGS_DB" + envDBSSLMode = "MF_THINGS_DB_SSL_MODE" envCACerts = "MF_THINGS_CA_CERTS" envCacheURL = "MF_THINGS_CACHE_URL" envCachePass = "MF_THINGS_CACHE_PASS" @@ -76,6 +78,7 @@ type config struct { DBUser string DBPass string DBName string + DBSSLMode string CACerts string CacheURL string CachePass string @@ -126,6 +129,7 @@ func loadConfig() config { DBUser: mainflux.Env(envDBUser, defDBUser), DBPass: mainflux.Env(envDBPass, defDBPass), DBName: mainflux.Env(envDBName, defDBName), + DBSSLMode: mainflux.Env(envDBSSLMode, defDBSSLMode), CACerts: mainflux.Env(envCACerts, defCACerts), CacheURL: mainflux.Env(envCacheURL, defCacheURL), CachePass: mainflux.Env(envCachePass, defCachePass), @@ -154,7 +158,7 @@ func connectToCache(cacheURL, cachePass string, cacheDB string, logger logger.Lo } func connectToDB(cfg config, logger logger.Logger) *sql.DB { - db, err := postgres.Connect(cfg.DBHost, cfg.DBPort, cfg.DBName, cfg.DBUser, cfg.DBPass) + db, err := postgres.Connect(cfg.DBHost, cfg.DBPort, cfg.DBName, cfg.DBUser, cfg.DBPass, cfg.DBSSLMode) if err != nil { logger.Error(fmt.Sprintf("Failed to connect to postgres: %s", err)) os.Exit(1) diff --git a/cmd/users/main.go b/cmd/users/main.go index d6a833c3e..e0220f41c 100644 --- a/cmd/users/main.go +++ b/cmd/users/main.go @@ -40,6 +40,7 @@ const ( defDBUser = "mainflux" defDBPass = "mainflux" defDBName = "users" + defDBSSLMode = "disable" defHTTPPort = "8180" defGRPCPort = "8181" defSecret = "users" @@ -51,6 +52,7 @@ const ( envDBUser = "MF_USERS_DB_USER" envDBPass = "MF_USERS_DB_PASS" envDBName = "MF_USERS_DB" + envDBSSLMode = "MF_USERS_DB_SSL_MODE" envHTTPPort = "MF_USERS_HTTP_PORT" envGRPCPort = "MF_USERS_GRPC_PORT" envSecret = "MF_USERS_SECRET" @@ -65,6 +67,7 @@ type config struct { DBUser string DBPass string DBName string + DBSSLMode string HTTPPort string GRPCPort string Secret string @@ -106,6 +109,7 @@ func loadConfig() config { DBUser: mainflux.Env(envDBUser, defDBUser), DBPass: mainflux.Env(envDBPass, defDBPass), DBName: mainflux.Env(envDBName, defDBName), + DBSSLMode: mainflux.Env(envDBSSLMode, defDBSSLMode), HTTPPort: mainflux.Env(envHTTPPort, defHTTPPort), GRPCPort: mainflux.Env(envGRPCPort, defGRPCPort), Secret: mainflux.Env(envSecret, defSecret), @@ -115,7 +119,7 @@ func loadConfig() config { } func connectToDB(cfg config, logger logger.Logger) *sql.DB { - db, err := postgres.Connect(cfg.DBHost, cfg.DBPort, cfg.DBName, cfg.DBUser, cfg.DBPass) + db, err := postgres.Connect(cfg.DBHost, cfg.DBPort, cfg.DBName, cfg.DBUser, cfg.DBPass, cfg.DBSSLMode) if err != nil { logger.Error(fmt.Sprintf("Failed to connect to postgres: %s", err)) os.Exit(1) diff --git a/docs/getting-started.md b/docs/getting-started.md index 9172a3b8c..e3f208c52 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -465,6 +465,17 @@ By default gRPC communication is not secure. ### Server configuration +### Securing PostgreSQL connections + +By default, Mainflux will connect to Postgres using insecure transport. +If a secured connection is required, you can select the SSL mode. + +`MF_USERS_DB_SSL_MODE` the SSL connection mode for Users. + +`MF_THINGS_DB_SSL_MODE` the SSL connection mode for Things. + +Currently supported modes are: `disabled` and `required` + #### Users If either the cert or key is not set, the server will use insecure transport. @@ -491,4 +502,4 @@ If you wish to secure the gRPC connection to `things` and `users` services you m #### Things -`MF_THINGS_CA_CERTS` - the path to a file that contains the CAs in PEM format. If not set, the default connection will be insecure. If it fails to read the file, the service will fail to start up. \ No newline at end of file +`MF_THINGS_CA_CERTS` - the path to a file that contains the CAs in PEM format. If not set, the default connection will be insecure. If it fails to read the file, the service will fail to start up. diff --git a/things/README.md b/things/README.md index 8d304cd80..afd4bba45 100644 --- a/things/README.md +++ b/things/README.md @@ -17,23 +17,24 @@ The service is configured using the environment variables presented in the following table. Note that any unset variables will be replaced with their default values. -| Variable | Description | Default | -|-----------------------|-------------------------------------------------|----------------| -| MF_THINGS_LOG_LEVEL | Log level for Things (debug, info, warn, error) | error | -| MF_THINGS_DB_HOST | Database host address | localhost | -| MF_THINGS_DB_PORT | Database host port | 5432 | -| MF_THINGS_DB_USER | Database user | mainflux | -| MF_THINGS_DB_PASS | Database password | mainflux | -| MF_THINGS_DB | Name of the database used by the service | things | -| MF_THINGS_CA_CERTS | Path to trusted CAs in PEM format | | -| MF_THINGS_CACHE_URL | Cache database URL | localhost:6379 | -| MF_THINGS_CACHE_PASS | Cache database password | | -| MF_THINGS_CACHE_DB | Cache instance that should be used | 0 | -| MF_THINGS_HTTP_PORT | Things service HTTP port | 8180 | -| MF_THINGS_GRPC_PORT | Things service gRPC port | 8181 | -| MF_THINGS_SERVER_CERT | Path to server certificate in pem format | 8181 | -| MF_THINGS_SERVER_KEY | Path to server key in pem format | 8181 | -| MF_USERS_URL | Users service URL | localhost:8181 | +| Variable | Description | Default | +|-----------------------|--------------------------------------------------|----------------| +| MF_THINGS_LOG_LEVEL | Log level for Things (debug, info, warn, error) | error | +| MF_THINGS_DB_HOST | Database host address | localhost | +| MF_THINGS_DB_PORT | Database host port | 5432 | +| MF_THINGS_DB_USER | Database user | mainflux | +| MF_THINGS_DB_PASS | Database password | mainflux | +| MF_THINGS_DB | Name of the database used by the service | things | +| MF_THINGS_DB_SSL_MODE | Database connection SSL mode (disable or require)| disable | +| MF_THINGS_CA_CERTS | Path to trusted CAs in PEM format | | +| MF_THINGS_CACHE_URL | Cache database URL | localhost:6379 | +| MF_THINGS_CACHE_PASS | Cache database password | | +| MF_THINGS_CACHE_DB | Cache instance that should be used | 0 | +| MF_THINGS_HTTP_PORT | Things service HTTP port | 8180 | +| MF_THINGS_GRPC_PORT | Things service gRPC port | 8181 | +| MF_THINGS_SERVER_CERT | Path to server certificate in pem format | 8181 | +| MF_THINGS_SERVER_KEY | Path to server key in pem format | 8181 | +| MF_USERS_URL | Users service URL | localhost:8181 | ## Deployment @@ -56,6 +57,7 @@ services: MF_THINGS_DB_USER: [Database user] MF_THINGS_DB_PASS: [Database password] MF_THINGS_DB: [Name of the database used by the service] + MF_THINGS_DB_SSL_MODE: [SSL mode to connect to the database with] MF_THINGS_CA_CERTS: [Path to trusted CAs in PEM format] MF_THINGS_CACHE_URL: [Cache database URL] MF_THINGS_CACHE_PASS: [Cache database password] @@ -83,7 +85,7 @@ make things make install # set the environment variables and run the service -MF_THINGS_LOG_LEVEL=[Things log level] MF_THINGS_DB_HOST=[Database host address] MF_THINGS_DB_PORT=[Database host port] MF_THINGS_DB_USER=[Database user] MF_THINGS_DB_PASS=[Database password] MF_THINGS_DB=[Name of the database used by the service] MF_HTTP_ADAPTER_CA_CERTS=[Path to trusted CAs in PEM format] MF_THINGS_CACHE_URL=[Cache database URL] MF_THINGS_CACHE_PASS=[Cache database password] MF_THINGS_CACHE_DB=[Cache instance that should be used] MF_THINGS_HTTP_PORT=[Service HTTP port] MF_THINGS_GRPC_PORT=[Service gRPC port] MF_USERS_URL=[Users service URL] MF_THINGS_SERVER_CERT=[Path to server certificate] MF_THINGS_SERVER_KEY=[Path to server key] $GOBIN/mainflux-things +MF_THINGS_LOG_LEVEL=[Things log level] MF_THINGS_DB_HOST=[Database host address] MF_THINGS_DB_PORT=[Database host port] MF_THINGS_DB_USER=[Database user] MF_THINGS_DB_PASS=[Database password] MF_THINGS_DB=[Name of the database used by the service] MF_THINGS_DB_SSL_MODE=[SSL mode to connect to the database with] MF_HTTP_ADAPTER_CA_CERTS=[Path to trusted CAs in PEM format] MF_THINGS_CACHE_URL=[Cache database URL] MF_THINGS_CACHE_PASS=[Cache database password] MF_THINGS_CACHE_DB=[Cache instance that should be used] MF_THINGS_HTTP_PORT=[Service HTTP port] MF_THINGS_GRPC_PORT=[Service gRPC port] MF_USERS_URL=[Users service URL] MF_THINGS_SERVER_CERT=[Path to server certificate] MF_THINGS_SERVER_KEY=[Path to server key] $GOBIN/mainflux-things ``` Setting `MF_THINGS_CA_CERTS` expects a file in PEM format of trusted CAs. This will enable TLS against the Users gRPC endpoint trusting only those CAs that are provided. diff --git a/things/postgres/init.go b/things/postgres/init.go index a874130bb..ea846c74b 100644 --- a/things/postgres/init.go +++ b/things/postgres/init.go @@ -18,8 +18,8 @@ import ( // Connect creates a connection to the PostgreSQL instance and applies any // unapplied database migrations. A non-nil error is returned to indicate // failure. -func Connect(host, port, name, user, pass string) (*sql.DB, error) { - url := fmt.Sprintf("host=%s port=%s user=%s dbname=%s password=%s sslmode=disable", host, port, user, name, pass) +func Connect(host, port, name, user, pass, sslMode string) (*sql.DB, error) { + url := fmt.Sprintf("host=%s port=%s user=%s dbname=%s password=%s sslmode=%s", host, port, user, name, pass, sslMode) db, err := sql.Open("postgres", url) if err != nil { diff --git a/things/postgres/setup_test.go b/things/postgres/setup_test.go index 40a2d0c32..33e3b3471 100644 --- a/things/postgres/setup_test.go +++ b/things/postgres/setup_test.go @@ -60,7 +60,7 @@ func TestMain(m *testing.M) { log.Fatalf("Could not connect to docker: %s", err) } - if db, err = postgres.Connect("localhost", port, "test", "test", "test"); err != nil { + if db, err = postgres.Connect("localhost", port, "test", "test", "test", "disable"); err != nil { log.Fatalf("Could not setup test DB connection: %s", err) } defer db.Close() diff --git a/users/README.md b/users/README.md index dffdabab9..fccc4d233 100644 --- a/users/README.md +++ b/users/README.md @@ -16,19 +16,20 @@ The service is configured using the environment variables presented in the following table. Note that any unset variables will be replaced with their default values. -| Variable | Description | Default | -|----------------------|-------------------------------------------------|--------------| -| MF_USERS_LOG_LEVEL | Log level for Users (debug, info, warn, error) | error | -| MF_USERS_DB_HOST | Database host address | localhost | -| MF_USERS_DB_PORT | Database host port | 5432 | -| MF_USERS_DB_USER | Database user | mainflux | -| MF_USERS_DB_PASSWORD | Database password | mainflux | -| MF_USERS_DB | Name of the database used by the service | users | -| MF_USERS_HTTP_PORT | Users service HTTP port | 8180 | -| MF_USERS_GRPC_PORT | Users service gRPC port | 8181 | -| MF_USERS_SERVER_CERT | Path to server certificate in pem format | | -| MF_USERS_SERVER_KEY | Path to server key in pem format | | -| MF_USERS_SECRET | String used for signing tokens | users | +| Variable | Description | Default | +|----------------------|--------------------------------------------------|--------------| +| MF_USERS_LOG_LEVEL | Log level for Users (debug, info, warn, error) | error | +| MF_USERS_DB_HOST | Database host address | localhost | +| MF_USERS_DB_PORT | Database host port | 5432 | +| MF_USERS_DB_USER | Database user | mainflux | +| MF_USERS_DB_PASSWORD | Database password | mainflux | +| MF_USERS_DB | Name of the database used by the service | users | +| MF_USERS_DB_SSL_MODE | Database connection SSL mode (disable or require)| disable | +| MF_USERS_HTTP_PORT | Users service HTTP port | 8180 | +| MF_USERS_GRPC_PORT | Users service gRPC port | 8181 | +| MF_USERS_SERVER_CERT | Path to server certificate in pem format | | +| MF_USERS_SERVER_KEY | Path to server key in pem format | | +| MF_USERS_SECRET | String used for signing tokens | users | ## Deployment @@ -51,6 +52,7 @@ services: MF_USERS_DB_USER: [Database user] MF_USERS_DB_PASS: [Database password] MF_USERS_DB: [Name of the database used by the service] + MF_USERS_DB_SSL_MODE: [SSL mode to connect to the database with] MF_USERS_HTTP_PORT: [Service HTTP port] MF_USERS_GRPC_PORT: [Service gRPC port] MF_USERS_SECRET: [String used for signing tokens] @@ -73,7 +75,7 @@ make users make install # set the environment variables and run the service -MF_USERS_LOG_LEVEL=[Users log level] MF_USERS_DB_HOST=[Database host address] MF_USERS_DB_PORT=[Database host port] MF_USERS_DB_USER=[Database user] MF_USERS_DB_PASS=[Database password] MF_USERS_DB=[Name of the database used by the service] MF_USERS_HTTP_PORT=[Service HTTP port] MF_USERS_GRPC_PORT=[Service gRPC port] MF_USERS_SECRET=[String used for signing tokens] MF_USERS_SERVER_CERT=[Path to server certificate] MF_USERS_SERVER_KEY=[Path to server key] $GOBIN/mainflux-users +MF_USERS_LOG_LEVEL=[Users log level] MF_USERS_DB_HOST=[Database host address] MF_USERS_DB_PORT=[Database host port] MF_USERS_DB_USER=[Database user] MF_USERS_DB_PASS=[Database password] MF_USERS_DB=[Name of the database used by the service] MF_USERS_DB_SSL_MODE=[SSL mode to connect to the database with] MF_USERS_HTTP_PORT=[Service HTTP port] MF_USERS_GRPC_PORT=[Service gRPC port] MF_USERS_SECRET=[String used for signing tokens] MF_USERS_SERVER_CERT=[Path to server certificate] MF_USERS_SERVER_KEY=[Path to server key] $GOBIN/mainflux-users ``` ## Usage diff --git a/users/postgres/init.go b/users/postgres/init.go index bd54b47dd..d5b0074ea 100644 --- a/users/postgres/init.go +++ b/users/postgres/init.go @@ -18,9 +18,8 @@ import ( // Connect creates a connection to the PostgreSQL instance and applies any // unapplied database migrations. A non-nil error is returned to indicate // failure. -func Connect(host, port, name, user, pass string) (*sql.DB, error) { - t := "host=%s port=%s user=%s dbname=%s password=%s sslmode=disable" - url := fmt.Sprintf(t, host, port, user, name, pass) +func Connect(host, port, name, user, pass, sslMode string) (*sql.DB, error) { + url := fmt.Sprintf("host=%s port=%s user=%s dbname=%s password=%s sslmode=%s", host, port, user, name, pass, sslMode) db, err := sql.Open("postgres", url) if err != nil { diff --git a/users/postgres/setup_test.go b/users/postgres/setup_test.go index 5bf890f52..15ee2664f 100644 --- a/users/postgres/setup_test.go +++ b/users/postgres/setup_test.go @@ -53,7 +53,7 @@ func TestMain(m *testing.M) { log.Fatalf("Could not connect to docker: %s", err) } - if db, err = postgres.Connect("localhost", port, "test", "test", "test"); err != nil { + if db, err = postgres.Connect("localhost", port, "test", "test", "test", "disable"); err != nil { log.Fatalf("Could not setup test DB connection: %s", err) } defer db.Close()