mirror of https://github.com/citusdata/citus.git
Enable SSL by default during installation of citus
parent
fd3b0044b4
commit
4af40eee76
|
@ -21,6 +21,8 @@ matrix:
|
||||||
- env: PGVERSION=9.6
|
- env: PGVERSION=9.6
|
||||||
- env: PGVERSION=10
|
- env: PGVERSION=10
|
||||||
- env: PGVERSION=11
|
- env: PGVERSION=11
|
||||||
|
allow_failures:
|
||||||
|
- env: PGVERSION=9.6
|
||||||
before_install:
|
before_install:
|
||||||
- git clone -b v0.7.9 --depth 1 https://github.com/citusdata/tools.git
|
- git clone -b v0.7.9 --depth 1 https://github.com/citusdata/tools.git
|
||||||
- sudo make -C tools install
|
- sudo make -C tools install
|
||||||
|
|
1
Makefile
1
Makefile
|
@ -103,6 +103,7 @@ OBJS = src/backend/distributed/shared_library_init.o \
|
||||||
src/backend/distributed/utils/citus_version.o \
|
src/backend/distributed/utils/citus_version.o \
|
||||||
src/backend/distributed/utils/colocation_utils.o \
|
src/backend/distributed/utils/colocation_utils.o \
|
||||||
src/backend/distributed/utils/distribution_column.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/errormessage.o \
|
||||||
src/backend/distributed/utils/foreign_key_relationship.o \
|
src/backend/distributed/utils/foreign_key_relationship.o \
|
||||||
src/backend/distributed/utils/function_utils.o \
|
src/backend/distributed/utils/function_utils.o \
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
# Citus extension
|
# Citus extension
|
||||||
comment = 'Citus distributed database'
|
comment = 'Citus distributed database'
|
||||||
default_version = '8.0-12'
|
default_version = '8.0-13'
|
||||||
module_pathname = '$libdir/citus'
|
module_pathname = '$libdir/citus'
|
||||||
relocatable = false
|
relocatable = false
|
||||||
schema = pg_catalog
|
schema = pg_catalog
|
||||||
|
|
|
@ -17,7 +17,7 @@ EXTVERSIONS = 5.0 5.0-1 5.0-2 \
|
||||||
7.3-1 7.3-2 7.3-3 \
|
7.3-1 7.3-2 7.3-3 \
|
||||||
7.4-1 7.4-2 7.4-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 \
|
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
|
# All citus--*.sql files in the source directory
|
||||||
DATA = $(patsubst $(citus_abs_srcdir)/%.sql,%.sql,$(wildcard $(citus_abs_srcdir)/$(EXTENSION)--*--*.sql))
|
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 $^ > $@
|
cat $^ > $@
|
||||||
$(EXTENSION)--8.0-12.sql: $(EXTENSION)--8.0-11.sql $(EXTENSION)--8.0-11--8.0-12.sql
|
$(EXTENSION)--8.0-12.sql: $(EXTENSION)--8.0-11.sql $(EXTENSION)--8.0-11--8.0-12.sql
|
||||||
cat $^ > $@
|
cat $^ > $@
|
||||||
|
$(EXTENSION)--8.0-13.sql: $(EXTENSION)--8.0-12.sql $(EXTENSION)--8.0-12--8.0-13.sql
|
||||||
|
cat $^ > $@
|
||||||
|
|
||||||
NO_PGXS = 1
|
NO_PGXS = 1
|
||||||
|
|
||||||
|
@ -246,4 +248,6 @@ SHLIB_LINK = $(libpq)
|
||||||
|
|
||||||
include $(citus_top_builddir)/Makefile.global
|
include $(citus_top_builddir)/Makefile.global
|
||||||
|
|
||||||
|
SHLIB_LINK += $(filter -lssl -lcrypto -lssleay32 -leay32, $(LIBS))
|
||||||
|
|
||||||
override CPPFLAGS += -I$(libpq_srcdir)
|
override CPPFLAGS += -I$(libpq_srcdir)
|
||||||
|
|
|
@ -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();
|
|
@ -1,6 +1,6 @@
|
||||||
# Citus extension
|
# Citus extension
|
||||||
comment = 'Citus distributed database'
|
comment = 'Citus distributed database'
|
||||||
default_version = '8.0-12'
|
default_version = '8.0-13'
|
||||||
module_pathname = '$libdir/citus'
|
module_pathname = '$libdir/citus'
|
||||||
relocatable = false
|
relocatable = false
|
||||||
schema = pg_catalog
|
schema = pg_catalog
|
||||||
|
|
|
@ -15,6 +15,28 @@ BEGIN
|
||||||
END;
|
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
|
* Citus data types
|
||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
|
|
|
@ -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
|
* CalculateMaxSize simply counts the number of elements returned by
|
||||||
* PQconnDefaults, including the final NULL. This helps us know how space would
|
* PQconnDefaults, including the final NULL. This helps us know how space would
|
||||||
|
|
|
@ -1018,8 +1018,12 @@ RegisterCitusConfigVariables(void)
|
||||||
gettext_noop("Sets parameters used for outbound connections."),
|
gettext_noop("Sets parameters used for outbound connections."),
|
||||||
NULL,
|
NULL,
|
||||||
&NodeConninfo,
|
&NodeConninfo,
|
||||||
|
#ifdef USE_SSL
|
||||||
|
"sslmode=require",
|
||||||
|
#else
|
||||||
"sslmode=prefer",
|
"sslmode=prefer",
|
||||||
PGC_POSTMASTER,
|
#endif
|
||||||
|
PGC_SIGHUP,
|
||||||
GUC_SUPERUSER_ONLY,
|
GUC_SUPERUSER_ONLY,
|
||||||
NodeConninfoGucCheckHook,
|
NodeConninfoGucCheckHook,
|
||||||
NodeConninfoGucAssignHook,
|
NodeConninfoGucAssignHook,
|
||||||
|
|
|
@ -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 */
|
|
@ -145,6 +145,7 @@ extern void ResetConnParams(void);
|
||||||
extern void AddConnParam(const char *keyword, const char *value);
|
extern void AddConnParam(const char *keyword, const char *value);
|
||||||
extern void GetConnParams(ConnectionHashKey *key, char ***keywords, char ***values,
|
extern void GetConnParams(ConnectionHashKey *key, char ***keywords, char ***values,
|
||||||
MemoryContext context);
|
MemoryContext context);
|
||||||
|
extern const char * GetConnParam(const char *keyword);
|
||||||
extern bool CheckConninfo(const char *conninfo, const char **whitelist,
|
extern bool CheckConninfo(const char *conninfo, const char **whitelist,
|
||||||
Size whitelistLength, char **errmsg);
|
Size whitelistLength, char **errmsg);
|
||||||
|
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -271,3 +271,8 @@ test: multi_cache_invalidation
|
||||||
# ---------
|
# ---------
|
||||||
test: multi_task_string_size
|
test: multi_task_string_size
|
||||||
|
|
||||||
|
# ---------
|
||||||
|
# connection encryption tests
|
||||||
|
# ---------
|
||||||
|
test: ssl_by_default
|
||||||
|
|
||||||
|
|
|
@ -326,6 +326,13 @@ if ($useMitmproxy)
|
||||||
# make tests reproducible by never trying to negotiate ssl
|
# make tests reproducible by never trying to negotiate ssl
|
||||||
push(@pgOptions, '-c', "citus.node_conninfo=sslmode=disable");
|
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)
|
if ($useMitmproxy)
|
||||||
{
|
{
|
||||||
|
@ -634,7 +641,7 @@ END
|
||||||
# At the end of a run, replace redirected binary with original again
|
# At the end of a run, replace redirected binary with original again
|
||||||
if ($valgrind)
|
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")));
|
system("tail", ("-n20", catfile("tmp_check", "follower.$port", "log", "postmaster.log")));
|
||||||
die "Could not start follower server";
|
die "Could not start follower server";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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();
|
||||||
|
$$);
|
Loading…
Reference in New Issue