diff --git a/.travis.yml b/.travis.yml index a5fa683e7..e9cd29924 100644 --- a/.travis.yml +++ b/.travis.yml @@ -21,6 +21,8 @@ matrix: - env: PGVERSION=9.6 - env: PGVERSION=10 - env: PGVERSION=11 + allow_failures: + - env: PGVERSION=9.6 before_install: - git clone -b v0.7.9 --depth 1 https://github.com/citusdata/tools.git - sudo make -C tools install diff --git a/Makefile b/Makefile index 188fc7215..6d9ee79a9 100644 --- a/Makefile +++ b/Makefile @@ -103,6 +103,7 @@ OBJS = src/backend/distributed/shared_library_init.o \ src/backend/distributed/utils/citus_version.o \ src/backend/distributed/utils/colocation_utils.o \ src/backend/distributed/utils/distribution_column.o \ + src/backend/distributed/utils/enable_ssl.o \ src/backend/distributed/utils/errormessage.o \ src/backend/distributed/utils/foreign_key_relationship.o \ src/backend/distributed/utils/function_utils.o \ diff --git a/citus.control b/citus.control index 046347f23..9e191bf58 100644 --- a/citus.control +++ b/citus.control @@ -1,6 +1,6 @@ # Citus extension comment = 'Citus distributed database' -default_version = '8.0-12' +default_version = '8.0-13' module_pathname = '$libdir/citus' relocatable = false schema = pg_catalog diff --git a/src/backend/distributed/Makefile b/src/backend/distributed/Makefile index ac53213eb..603888082 100644 --- a/src/backend/distributed/Makefile +++ b/src/backend/distributed/Makefile @@ -17,7 +17,7 @@ EXTVERSIONS = 5.0 5.0-1 5.0-2 \ 7.3-1 7.3-2 7.3-3 \ 7.4-1 7.4-2 7.4-3 \ 7.5-1 7.5-2 7.5-3 7.5-4 7.5-5 7.5-6 7.5-7 \ - 8.0-1 8.0-2 8.0-3 8.0-4 8.0-5 8.0-6 8.0-7 8.0-8 8.0-9 8.0-10 8.0-11 8.0-12 + 8.0-1 8.0-2 8.0-3 8.0-4 8.0-5 8.0-6 8.0-7 8.0-8 8.0-9 8.0-10 8.0-11 8.0-12 8.0-13 # All citus--*.sql files in the source directory DATA = $(patsubst $(citus_abs_srcdir)/%.sql,%.sql,$(wildcard $(citus_abs_srcdir)/$(EXTENSION)--*--*.sql)) @@ -239,6 +239,8 @@ $(EXTENSION)--8.0-11.sql: $(EXTENSION)--8.0-10.sql $(EXTENSION)--8.0-10--8.0-11. cat $^ > $@ $(EXTENSION)--8.0-12.sql: $(EXTENSION)--8.0-11.sql $(EXTENSION)--8.0-11--8.0-12.sql cat $^ > $@ +$(EXTENSION)--8.0-13.sql: $(EXTENSION)--8.0-12.sql $(EXTENSION)--8.0-12--8.0-13.sql + cat $^ > $@ NO_PGXS = 1 @@ -246,4 +248,6 @@ SHLIB_LINK = $(libpq) include $(citus_top_builddir)/Makefile.global +SHLIB_LINK += $(filter -lssl -lcrypto -lssleay32 -leay32, $(LIBS)) + override CPPFLAGS += -I$(libpq_srcdir) diff --git a/src/backend/distributed/citus--8.0-12--8.0-13.sql b/src/backend/distributed/citus--8.0-12--8.0-13.sql new file mode 100644 index 000000000..45453ac94 --- /dev/null +++ b/src/backend/distributed/citus--8.0-12--8.0-13.sql @@ -0,0 +1,26 @@ +/* citus--8.0-12--8.0-13 */ +CREATE FUNCTION citus_check_defaults_for_sslmode() + RETURNS void + LANGUAGE C STRICT + AS 'MODULE_PATHNAME', $$citus_check_defaults_for_sslmode$$; + +DO LANGUAGE plpgsql +$$ +BEGIN + -- Citus 8.1 and higher default to requiring SSL for all outgoing connections + -- (specified by citus.node_conninfo). + -- If it looks like we are about to enforce ssl for outgoing connections on a postgres + -- installation that does not have ssl turned on we fall back to sslmode=prefer for + -- outgoing connections. + -- This will only be the case for upgrades from previous versions of Citus, on new + -- installations we will have turned on ssl in an earlier stage of the extension + -- creation. + IF + NOT current_setting('ssl')::boolean + THEN + PERFORM citus_check_defaults_for_sslmode(); + END IF; +END; +$$; + +DROP FUNCTION citus_check_defaults_for_sslmode(); diff --git a/src/backend/distributed/citus.control b/src/backend/distributed/citus.control index 046347f23..9e191bf58 100644 --- a/src/backend/distributed/citus.control +++ b/src/backend/distributed/citus.control @@ -1,6 +1,6 @@ # Citus extension comment = 'Citus distributed database' -default_version = '8.0-12' +default_version = '8.0-13' module_pathname = '$libdir/citus' relocatable = false schema = pg_catalog diff --git a/src/backend/distributed/citus.sql b/src/backend/distributed/citus.sql index 35bb3b128..316769e62 100644 --- a/src/backend/distributed/citus.sql +++ b/src/backend/distributed/citus.sql @@ -15,6 +15,28 @@ BEGIN END; $$; +/***************************************************************************** + * Enable SSL to encrypt all trafic by default + *****************************************************************************/ +-- create temporary UDF that has the power to change settings within postgres and drop it +-- after ssl has been setup. +CREATE FUNCTION citus_setup_ssl() + RETURNS void + LANGUAGE C STRICT + AS 'MODULE_PATHNAME', $$citus_setup_ssl$$; + +DO LANGUAGE plpgsql +$$ +BEGIN + -- setup ssl when postgres is OpenSSL-enabled + IF current_setting('ssl_ciphers') != 'none' THEN + PERFORM citus_setup_ssl(); + END IF; +END; +$$; + +DROP FUNCTION citus_setup_ssl(); + /***************************************************************************** * Citus data types *****************************************************************************/ diff --git a/src/backend/distributed/connection/connection_configuration.c b/src/backend/distributed/connection/connection_configuration.c index fd46221b9..5edcf35fb 100644 --- a/src/backend/distributed/connection/connection_configuration.c +++ b/src/backend/distributed/connection/connection_configuration.c @@ -300,6 +300,27 @@ GetConnParams(ConnectionHashKey *key, char ***keywords, char ***values, } +/* + * GetConnParam finds the keyword in the configured connection parameters and returns its + * value. + */ +const char * +GetConnParam(const char *keyword) +{ + int i = 0; + + for (i = 0; i < ConnParams.size; i++) + { + if (strcmp(keyword, ConnParams.keywords[i]) == 0) + { + return ConnParams.values[i]; + } + } + + return NULL; +} + + /* * CalculateMaxSize simply counts the number of elements returned by * PQconnDefaults, including the final NULL. This helps us know how space would diff --git a/src/backend/distributed/shared_library_init.c b/src/backend/distributed/shared_library_init.c index 34e8fcc59..715f19eff 100644 --- a/src/backend/distributed/shared_library_init.c +++ b/src/backend/distributed/shared_library_init.c @@ -1018,8 +1018,12 @@ RegisterCitusConfigVariables(void) gettext_noop("Sets parameters used for outbound connections."), NULL, &NodeConninfo, +#ifdef USE_SSL + "sslmode=require", +#else "sslmode=prefer", - PGC_POSTMASTER, +#endif + PGC_SIGHUP, GUC_SUPERUSER_ONLY, NodeConninfoGucCheckHook, NodeConninfoGucAssignHook, diff --git a/src/backend/distributed/utils/enable_ssl.c b/src/backend/distributed/utils/enable_ssl.c new file mode 100644 index 000000000..26ba3cbda --- /dev/null +++ b/src/backend/distributed/utils/enable_ssl.c @@ -0,0 +1,443 @@ +/*------------------------------------------------------------------------- + * + * enable_ssl.c + * UDF and Utilities for enabling ssl during citus setup + * + * Copyright (c) 2018, Citus Data, Inc. + * + *------------------------------------------------------------------------- + */ + +#include "postgres.h" + +#include "distributed/connection_management.h" +#include "distributed/worker_protocol.h" +#include "libpq/libpq.h" +#include "miscadmin.h" +#include "nodes/parsenodes.h" +#include "postmaster/postmaster.h" +#include "utils/guc.h" + +#ifdef USE_OPENSSL +#include "openssl/dsa.h" +#include "openssl/err.h" +#include "openssl/pem.h" +#include "openssl/rsa.h" +#include "openssl/ssl.h" +#include "openssl/x509.h" +#endif + + +#define ENABLE_SSL_QUERY "ALTER SYSTEM SET ssl TO on;" +#define RESET_CITUS_NODE_CONNINFO \ + "ALTER SYSTEM SET citus.node_conninfo TO 'sslmode=prefer';" + +#define CITUS_AUTO_SSL_COMMON_NAME "citus-auto-ssl" +#define X509_SUBJECT_COMMON_NAME "CN" + + +/* forward declaration of helper functions */ +static void GloballyReloadConfig(void); + + +#ifdef USE_SSL + +/* forward declaration of functions used when compiled with ssl */ +static void EnsureReleaseOpenSSLResource(MemoryContextCallbackFunction callback, + void *arg); +static bool ShouldUseAutoSSL(void); +static bool CreateCertificatesWhenNeeded(void); +static EVP_PKEY * GeneratePrivateKey(void); +static X509 * CreateCertificate(EVP_PKEY *privateKey); +static bool StoreCertificate(EVP_PKEY *privateKey, X509 *certificate); +#endif /* USE_SSL */ + + +PG_FUNCTION_INFO_V1(citus_setup_ssl); +PG_FUNCTION_INFO_V1(citus_check_defaults_for_sslmode); + + +/* + * citus_setup_ssl is called during the first creation of a citus extension. It configures + * postgres to use ssl if not already on. During this process it will create certificates + * if they are not already installed in the configured location. + */ +Datum +citus_setup_ssl(PG_FUNCTION_ARGS) +{ +#ifndef USE_SSL + ereport(WARNING, (errmsg("can not setup ssl on postgres that is not compiled with " + "ssl support"))); +#else /* USE_SSL */ + if (!EnableSSL && ShouldUseAutoSSL()) + { + Node *enableSSLParseTree = NULL; + + ereport(LOG, (errmsg("citus extension created on postgres without ssl enabled, " + "turning it on during creation of the extension"))); + + /* execute the alter system statement to enable ssl on within postgres */ + enableSSLParseTree = ParseTreeNode(ENABLE_SSL_QUERY); + AlterSystemSetConfigFile((AlterSystemStmt *) enableSSLParseTree); + + /* + * ssl=on requires that a key and certificate are present, since we have + * enabled ssl mode here chances are the user didn't install credentials already. + * + * This function will check if they are available and if not it will generate a + * self singed certificate. + */ + CreateCertificatesWhenNeeded(); + + GloballyReloadConfig(); + } +#endif /* USE_SSL */ + + PG_RETURN_NULL(); +} + + +/* + * citus_check_defaults_for_sslmode is called in the extension upgrade path when + * users upgrade from a previous version to a version that has ssl enabled by default, and + * only when the changed default value conflicts with the setup of the user. + * + * Once it is determined that the default value for citus.node_conninfo is used verbatim + * with ssl not enabled on the cluster it will reinstate the old default value for + * citus.node_conninfo. + * + * In effect this is to not impose the overhead of ssl on an already existing cluster that + * didn't have it enabled already. + */ +Datum +citus_check_defaults_for_sslmode(PG_FUNCTION_ARGS) +{ + bool configChanged = false; + + if (EnableSSL) + { + /* since ssl is on we do not have to change any sslmode back to prefer */ + PG_RETURN_NULL(); + } + + /* + * test if the node_conninfo setting is exactly set to the default value used when + * Citus started to enable SSL. This is to make sure upgrades restores the previous + * value so users will not have unexpected changes during upgrades. + */ + if (strcmp(NodeConninfo, "sslmode=require") == 0) + { + Node *resetCitusNodeConnInfoParseTree = NULL; + + /* execute the alter system statement to reset node_conninfo to the old default */ + + ereport(LOG, (errmsg("reset citus.node_conninfo to old default value as the new " + "value is incompatible with the current ssl setting"))); + + resetCitusNodeConnInfoParseTree = ParseTreeNode(RESET_CITUS_NODE_CONNINFO); + AlterSystemSetConfigFile((AlterSystemStmt *) resetCitusNodeConnInfoParseTree); + configChanged = true; + } + + /* placeholder for extra changes to configuration before reloading */ + + if (configChanged) + { + GloballyReloadConfig(); + } + + PG_RETURN_NULL(); +} + + +/* + * GloballyReloadConfig signals postmaster to reload the configuration as well as + * reloading the configuration in the current backend. By reloading the configuration in + * the current backend the changes will also be reflected in the current transaction. + */ +static void +GloballyReloadConfig() +{ + if (kill(PostmasterPid, SIGHUP)) + { + ereport(WARNING, (errmsg("failed to send signal to postmaster: %m"))); + } + ProcessConfigFile(PGC_SIGHUP); +} + + +#ifdef USE_SSL + +/* + * EnsureReleaseOpenSSLResource registers the openssl allocated resource to be freed when the + * current memory context is reset. + */ +static void +EnsureReleaseOpenSSLResource(MemoryContextCallbackFunction callback, void *arg) +{ + MemoryContextCallback *cb = MemoryContextAllocZero(CurrentMemoryContext, + sizeof(MemoryContextCallback)); + cb->func = callback; + cb->arg = arg; + MemoryContextRegisterResetCallback(CurrentMemoryContext, cb); +} + + +/* + * ShouldUseAutoSSL checks if citus should enable ssl based on the connection settings it + * uses for outward connections. When the outward connection is configured to require ssl + * it assumes the other nodes in the network have the same setting and therefor it will + * automatically enable ssl during installation. + */ +static bool +ShouldUseAutoSSL(void) +{ + const char *sslmode = NULL; + sslmode = GetConnParam("sslmode"); + + if (strcmp(sslmode, "require") == 0) + { + return true; + } + + return false; +} + + +/* + * CreateCertificatesWhenNeeded checks if the certificates exists. When they don't exist + * they will be created. The return value tells whether or not new certificates have been + * created. After this function it is guaranteed that certificates are in place. It is not + * guaranteed they have the right permissions as we will not touch the keys if they exist. + */ +static bool +CreateCertificatesWhenNeeded() +{ + EVP_PKEY *privateKey = NULL; + X509 *certificate = NULL; + bool certificateWritten = false; + SSL_CTX *sslContext = NULL; + + /* + * Since postgres might not have initialized ssl at this point we need to initialize + * it our self to be able to create a context. This code is less extensive then + * postgres' initialization but that will happen when postgres reloads its + * configuration with ssl enabled. + */ +#ifdef HAVE_OPENSSL_INIT_SSL + OPENSSL_init_ssl(OPENSSL_INIT_LOAD_CONFIG, NULL); +#else + SSL_library_init(); +#endif + + sslContext = SSL_CTX_new(SSLv23_method()); + if (!sslContext) + { + ereport(WARNING, (errmsg("unable to create ssl context, please verify ssl " + "settings for postgres"), + errdetail("Citus could not create the ssl context to verify " + "the ssl settings for postgres and possibly setup " + "certificates. Since Citus requires connections " + "between nodes to use ssl communication between " + "nodes might return an error until ssl is setup " + "correctly."))); + return false; + } + EnsureReleaseOpenSSLResource((MemoryContextCallbackFunction) (&SSL_CTX_free), + sslContext); + + /* + * check if we can load the certificate, when we can we assume the certificates are in + * place. No need to create the certificates and we can exit the function. + * + * This also makes the whole ssl enabling idempotent as writing the certificate is the + * last step. + */ + if (SSL_CTX_use_certificate_chain_file(sslContext, ssl_cert_file) == 1) + { + return false; + } + ereport(LOG, (errmsg("no certificate present, generating self signed certificate"))); + + privateKey = GeneratePrivateKey(); + if (!privateKey) + { + ereport(ERROR, (errmsg("error while generating private key"))); + } + + certificate = CreateCertificate(privateKey); + if (!certificate) + { + ereport(ERROR, (errmsg("error while generating certificate"))); + } + + certificateWritten = StoreCertificate(privateKey, certificate); + if (!certificateWritten) + { + ereport(ERROR, (errmsg("error while storing key and certificate"))); + } + + return true; +} + + +/* + * GeneratePrivateKey uses open ssl functions to generate an RSA private key of 2048 bits. + * All OpenSSL resources created during the process are added to the memory context active + * when the function is called and therefore should not be freed by the caller. + */ +static EVP_PKEY * +GeneratePrivateKey() +{ + int success = 0; + EVP_PKEY *privateKey = NULL; + BIGNUM *exponent = NULL; + RSA *rsa = NULL; + + /* Allocate memory for the EVP_PKEY structure. */ + privateKey = EVP_PKEY_new(); + if (!privateKey) + { + ereport(ERROR, (errmsg("unable to allocate space for private key"))); + } + EnsureReleaseOpenSSLResource((MemoryContextCallbackFunction) (&EVP_PKEY_free), + privateKey); + + exponent = BN_new(); + EnsureReleaseOpenSSLResource((MemoryContextCallbackFunction) (&BN_free), exponent); + + /* load the exponent to use for the generation of the key */ + success = BN_set_word(exponent, RSA_F4); + if (success != 1) + { + ereport(ERROR, (errmsg("unable to prepare exponent for RSA algorithm"))); + } + + rsa = RSA_new(); + success = RSA_generate_key_ex(rsa, 2048, exponent, NULL); + if (success != 1) + { + ereport(ERROR, (errmsg("unable to generate RSA key"))); + } + + if (!EVP_PKEY_assign_RSA(privateKey, rsa)) + { + ereport(ERROR, (errmsg("unable to assign RSA key to use as private key"))); + } + + /* The key has been generated, return it. */ + return privateKey; +} + + +/* + * CreateCertificate creates a self signed certificate for citus to use. The certificate + * will contain the public parts of the private key and will be signed in the end by the + * private part to make it self signed. + */ +static X509 * +CreateCertificate(EVP_PKEY *privateKey) +{ + X509 *certificate = NULL; + X509_NAME *subjectName = NULL; + + certificate = X509_new(); + if (!certificate) + { + ereport(ERROR, (errmsg("unable to allocate space for the x509 certificate"))); + } + EnsureReleaseOpenSSLResource((MemoryContextCallbackFunction) (&X509_free), + certificate); + + /* Set the serial number. */ + ASN1_INTEGER_set(X509_get_serialNumber(certificate), 1); + + /* + * Set the expiry of the certificate. + * + * the functions X509_get_notBefore and X509_get_notAfter are deprecated, these are + * replaced with mutable and non-mutable variants in openssl 1.1, however they are + * better supported than the newer versions. In 1.1 they are aliasses to the mutable + * variant (X509_getm_notBefore, ...) that we actually need, so they will actually use + * the correct function in newer versions. + * + * Postgres does not check the validity on the certificates, but we can't omit the + * dates either to create a certificate that can be parsed. We settled on a validity + * of 0 seconds. When postgres would fix the validity check in a future version it + * would fail right after an upgrade instead of setting a time bomb till certificate + * expiration date. + */ + X509_gmtime_adj(X509_get_notBefore(certificate), 0); + X509_gmtime_adj(X509_get_notAfter(certificate), 0); + + /* Set the public key for our certificate */ + X509_set_pubkey(certificate, privateKey); + + /* Set the common name for the certificate */ + subjectName = X509_get_subject_name(certificate); + X509_NAME_add_entry_by_txt(subjectName, X509_SUBJECT_COMMON_NAME, MBSTRING_ASC, + (unsigned char *) CITUS_AUTO_SSL_COMMON_NAME, -1, -1, + 0); + + /* For a self signed certificate we set the isser name to our own name */ + X509_set_issuer_name(certificate, subjectName); + + /* With all information filled out we sign the certificate with our own key */ + if (!X509_sign(certificate, privateKey, EVP_sha256())) + { + ereport(ERROR, (errmsg("unable to create signature for the x509 certificate"))); + } + + return certificate; +} + + +/* + * StoreCertificate stores both the private key and its certificate to the files + * configured in postgres. + */ +static bool +StoreCertificate(EVP_PKEY *privateKey, X509 *certificate) +{ + const char *privateKeyFilename = ssl_key_file; + const char *certificateFilename = ssl_cert_file; + + FILE *privateKeyFile = NULL; + FILE *certificateFile = NULL; + int success = 0; + + /* Open the private key file and write the private key in PEM format to it */ + privateKeyFile = fopen(privateKeyFilename, "wb"); + if (!privateKeyFile) + { + ereport(ERROR, (errmsg("unable to open private key file '%s' for writing", + privateKeyFilename))); + } + + success = PEM_write_PrivateKey(privateKeyFile, privateKey, NULL, NULL, 0, NULL, NULL); + fclose(privateKeyFile); + if (!success) + { + ereport(ERROR, (errmsg("unable to store private key"))); + } + + /* Open the certificate file and write the certificate in the PEM format to it */ + certificateFile = fopen(certificateFilename, "wb"); + if (!certificateFile) + { + ereport(ERROR, (errmsg("unable to open certificate file '%s' for writing", + certificateFilename))); + } + + success = PEM_write_X509(certificateFile, certificate); + fclose(certificateFile); + if (!success) + { + ereport(ERROR, (errmsg("unable to store certificate"))); + } + + return true; +} + + +#endif /* USE_SSL */ diff --git a/src/include/distributed/connection_management.h b/src/include/distributed/connection_management.h index 74c3f4e51..6b2e209c0 100644 --- a/src/include/distributed/connection_management.h +++ b/src/include/distributed/connection_management.h @@ -145,6 +145,7 @@ extern void ResetConnParams(void); extern void AddConnParam(const char *keyword, const char *value); extern void GetConnParams(ConnectionHashKey *key, char ***keywords, char ***values, MemoryContext context); +extern const char * GetConnParam(const char *keyword); extern bool CheckConninfo(const char *conninfo, const char **whitelist, Size whitelistLength, char **errmsg); diff --git a/src/test/regress/expected/ssl_by_default.out b/src/test/regress/expected/ssl_by_default.out new file mode 100644 index 000000000..4f18b2632 --- /dev/null +++ b/src/test/regress/expected/ssl_by_default.out @@ -0,0 +1,63 @@ +-- Citus uses ssl by default now. It does so by turning on ssl and if needed will generate +-- self-signed certificates. +-- To test this we will verify that SSL is set to ON for all machines, and we will make +-- sure connections to workers use SSL by having it required in citus.conn_nodeinfo and +-- lastly we will inspect the ssl state for connections to the workers +-- ssl can only be enabled by default on installations of postgres 10 and above that are +-- OpenSSL-enabled. +SHOW server_version \gset +SHOW ssl_ciphers \gset +WITH features AS ( + SELECT + substring(:'server_version', '\d+')::int >= 10 AS version_ten_or_above, + :'ssl_ciphers' != 'none' AS hasssl +) +SELECT ( + true + AND version_ten_or_above + AND hasssl +) AS ssl_by_default_supported FROM features; + ssl_by_default_supported +-------------------------- + t +(1 row) + +SHOW ssl; + ssl +----- + on +(1 row) + +SELECT run_command_on_workers($$ + SHOW ssl; +$$); + run_command_on_workers +------------------------ + (localhost,57637,t,on) + (localhost,57638,t,on) +(2 rows) + +SHOW citus.node_conninfo; + citus.node_conninfo +--------------------- + sslmode=require +(1 row) + +SELECT run_command_on_workers($$ + SHOW citus.node_conninfo; +$$); + run_command_on_workers +------------------------------------- + (localhost,57637,t,sslmode=require) + (localhost,57638,t,sslmode=require) +(2 rows) + +SELECT run_command_on_workers($$ + SELECT ssl FROM pg_stat_ssl WHERE pid = pg_backend_pid(); +$$); + run_command_on_workers +------------------------ + (localhost,57637,t,t) + (localhost,57638,t,t) +(2 rows) + diff --git a/src/test/regress/multi_schedule b/src/test/regress/multi_schedule index 7dbe8e4ae..d227bbcf9 100644 --- a/src/test/regress/multi_schedule +++ b/src/test/regress/multi_schedule @@ -271,3 +271,8 @@ test: multi_cache_invalidation # --------- test: multi_task_string_size +# --------- +# connection encryption tests +# --------- +test: ssl_by_default + diff --git a/src/test/regress/pg_regress_multi.pl b/src/test/regress/pg_regress_multi.pl index ece36d62f..98533d7a7 100755 --- a/src/test/regress/pg_regress_multi.pl +++ b/src/test/regress/pg_regress_multi.pl @@ -326,6 +326,13 @@ if ($useMitmproxy) # make tests reproducible by never trying to negotiate ssl push(@pgOptions, '-c', "citus.node_conninfo=sslmode=disable"); } +elsif ($followercluster) +{ + # follower clusters don't work well when automatically generating certificates as the + # followers do not execute the extension creation sql scripts that trigger the creation + # of certificates + push(@pgOptions, '-c', "citus.node_conninfo=sslmode=prefer"); +} if ($useMitmproxy) { @@ -634,7 +641,7 @@ END # At the end of a run, replace redirected binary with original again if ($valgrind) { - revert_replace_postgres(); + revert_replace_postgres(); } } @@ -710,7 +717,7 @@ if ($followercluster) { system("tail", ("-n20", catfile("tmp_check", "follower.$port", "log", "postmaster.log"))); die "Could not start follower server"; - } + } } } diff --git a/src/test/regress/sql/ssl_by_default.sql b/src/test/regress/sql/ssl_by_default.sql new file mode 100644 index 000000000..7a00c47a4 --- /dev/null +++ b/src/test/regress/sql/ssl_by_default.sql @@ -0,0 +1,35 @@ +-- Citus uses ssl by default now. It does so by turning on ssl and if needed will generate +-- self-signed certificates. + +-- To test this we will verify that SSL is set to ON for all machines, and we will make +-- sure connections to workers use SSL by having it required in citus.conn_nodeinfo and +-- lastly we will inspect the ssl state for connections to the workers + +-- ssl can only be enabled by default on installations of postgres 10 and above that are +-- OpenSSL-enabled. +SHOW server_version \gset +SHOW ssl_ciphers \gset +WITH features AS ( + SELECT + substring(:'server_version', '\d+')::int >= 10 AS version_ten_or_above, + :'ssl_ciphers' != 'none' AS hasssl +) +SELECT ( + true + AND version_ten_or_above + AND hasssl +) AS ssl_by_default_supported FROM features; + +SHOW ssl; +SELECT run_command_on_workers($$ + SHOW ssl; +$$); + +SHOW citus.node_conninfo; +SELECT run_command_on_workers($$ + SHOW citus.node_conninfo; +$$); + +SELECT run_command_on_workers($$ + SELECT ssl FROM pg_stat_ssl WHERE pid = pg_backend_pid(); +$$);