From ab9351f83a711acb33656962214dd14d05ee7307 Mon Sep 17 00:00:00 2001 From: Masahiko Sawada Date: Thu, 15 Aug 2019 13:11:30 +0900 Subject: [PATCH 3/5] Enable cluster encryption during initdb. --- src/backend/access/transam/xlog.c | 14 +++++ src/backend/bootstrap/bootstrap.c | 9 +++- src/backend/utils/misc/guc.c | 34 +++++++++++- src/backend/utils/misc/postgresql.conf.sample | 5 ++ src/bin/initdb/initdb.c | 75 +++++++++++++++++++++++++-- src/bin/pg_controldata/pg_controldata.c | 18 +++++++ src/include/access/xlog.h | 1 + src/include/catalog/pg_control.h | 3 ++ src/include/utils/guc_tables.h | 1 + 9 files changed, 155 insertions(+), 5 deletions(-) diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c index 5cc2a40..0040e77 100644 --- a/src/backend/access/transam/xlog.c +++ b/src/backend/access/transam/xlog.c @@ -55,6 +55,7 @@ #include "replication/walreceiver.h" #include "replication/walsender.h" #include "storage/bufmgr.h" +#include "storage/encryption.h" #include "storage/fd.h" #include "storage/ipc.h" #include "storage/kmgr.h" @@ -78,6 +79,7 @@ #include "pg_trace.h" extern uint32 bootstrap_data_checksum_version; +extern uint32 bootstrap_data_encryption_cipher; /* Unsupported old recovery command file names (relative to $PGDATA) */ #define RECOVERY_COMMAND_FILE "recovery.conf" @@ -4784,6 +4786,10 @@ ReadControlFile(void) /* Make the initdb settings visible as GUC variables, too */ SetConfigOption("data_checksums", DataChecksumsEnabled() ? "yes" : "no", PGC_INTERNAL, PGC_S_OVERRIDE); + + SetConfigOption("data_encryption_cipher", + EncryptionCipherString(GetDataEncryptionCipher()), + PGC_INTERNAL, PGC_S_OVERRIDE); } /* @@ -4826,6 +4832,13 @@ DataChecksumsEnabled(void) return (ControlFile->data_checksum_version > 0); } +int +GetDataEncryptionCipher(void) +{ + Assert(ControlFile != NULL); + return ControlFile->data_encryption_cipher; +} + /* * Returns a fake LSN for unlogged relations. * @@ -5255,6 +5268,7 @@ BootStrapXLOG(void) ControlFile->wal_log_hints = wal_log_hints; ControlFile->track_commit_timestamp = track_commit_timestamp; ControlFile->data_checksum_version = bootstrap_data_checksum_version; + ControlFile->data_encryption_cipher = bootstrap_data_encryption_cipher; /* some additional ControlFile fields are set in WriteControlFile() */ diff --git a/src/backend/bootstrap/bootstrap.c b/src/backend/bootstrap/bootstrap.c index 9238fbe..3882228 100644 --- a/src/backend/bootstrap/bootstrap.c +++ b/src/backend/bootstrap/bootstrap.c @@ -40,6 +40,8 @@ #include "storage/bufmgr.h" #include "storage/bufpage.h" #include "storage/condition_variable.h" +#include "storage/encryption.h" +#include "storage/kmgr.h" #include "storage/ipc.h" #include "storage/proc.h" #include "tcop/tcopprot.h" @@ -51,6 +53,7 @@ #include "utils/relmapper.h" uint32 bootstrap_data_checksum_version = 0; /* No checksum */ +uint32 bootstrap_data_encryption_cipher = 0; /* No encryption */ #define ALLOC(t, c) \ @@ -226,7 +229,7 @@ AuxiliaryProcessMain(int argc, char *argv[]) /* If no -x argument, we are a CheckerProcess */ MyAuxProcType = CheckerProcess; - while ((flag = getopt(argc, argv, "B:c:d:D:Fkr:x:X:-:")) != -1) + while ((flag = getopt(argc, argv, "B:c:d:D:e:Fkr:x:X:-:")) != -1) { switch (flag) { @@ -249,6 +252,10 @@ AuxiliaryProcessMain(int argc, char *argv[]) pfree(debugstr); } break; + + case 'e': + bootstrap_data_encryption_cipher = EncryptionCipherValue(optarg); + break; case 'F': SetConfigOption("fsync", "false", PGC_POSTMASTER, PGC_S_ARGV); break; diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c index eb78522..53e056e 100644 --- a/src/backend/utils/misc/guc.c +++ b/src/backend/utils/misc/guc.c @@ -71,7 +71,9 @@ #include "replication/walreceiver.h" #include "replication/walsender.h" #include "storage/bufmgr.h" +#include "storage/encryption.h" #include "storage/dsm_impl.h" +#include "storage/kmgr.h" #include "storage/standby.h" #include "storage/fd.h" #include "storage/large_object.h" @@ -457,6 +459,13 @@ const struct config_enum_entry ssl_protocol_versions_info[] = { {NULL, 0, false} }; +const struct config_enum_entry data_encryption_cipher_options[] = { + {"off", TDE_ENCRYPTION_OFF, false}, + {"aes-128", TDE_ENCRYPTION_AES_128, false}, + {"aes-256", TDE_ENCRYPTION_AES_256, false}, + {NULL, 0, false} +}; + static struct config_enum_entry shared_memory_options[] = { #ifndef WIN32 {"sysv", SHMEM_TYPE_SYSV, false}, @@ -4108,7 +4117,7 @@ static struct config_string ConfigureNamesString[] = {"ssl_ciphers", PGC_SIGHUP, CONN_AUTH_SSL, gettext_noop("Sets the list of allowed SSL ciphers."), NULL, - GUC_SUPERUSER_ONLY + GUC_NOT_IN_SAMPLE | GUC_DISALLOW_IN_FILE }, &SSLCipherSuites, #ifdef USE_OPENSSL @@ -4156,6 +4165,16 @@ static struct config_string ConfigureNamesString[] = }, { + {"cluster_passphrase_command", PGC_POSTMASTER, ENCRYPTION, + gettext_noop("Command to obtain passphrases for database encryption."), + NULL + }, + &cluster_passphrase_command, + "", + NULL, NULL, NULL + }, + + { {"application_name", PGC_USERSET, LOGGING_WHAT, gettext_noop("Sets the application name to be reported in statistics and logs."), NULL, @@ -4537,6 +4556,19 @@ static struct config_enum ConfigureNamesEnum[] = NULL, NULL, NULL }, + { + {"data_encryption_cipher", PGC_INTERNAL, PRESET_OPTIONS, + gettext_noop("Specify encryption algorithms to use."), + NULL, + GUC_NOT_IN_SAMPLE | GUC_DISALLOW_IN_FILE, + GUC_SUPERUSER_ONLY + }, + &data_encryption_cipher, + TDE_ENCRYPTION_AES_128, + data_encryption_cipher_options, + NULL, NULL, NULL + }, + /* End-of-list marker */ { {NULL, 0, 0, NULL, NULL}, NULL, 0, NULL, NULL, NULL, NULL diff --git a/src/backend/utils/misc/postgresql.conf.sample b/src/backend/utils/misc/postgresql.conf.sample index 65a6da1..723c984 100644 --- a/src/backend/utils/misc/postgresql.conf.sample +++ b/src/backend/utils/misc/postgresql.conf.sample @@ -600,6 +600,11 @@ # autovacuum, -1 means use # vacuum_cost_limit +#------------------------------------------------------------------------------ +# ENCRYPTION +#------------------------------------------------------------------------------ + +#cluster_passphrase_command = '' #------------------------------------------------------------------------------ # CLIENT CONNECTION DEFAULTS diff --git a/src/bin/initdb/initdb.c b/src/bin/initdb/initdb.c index 88a261d..613e050 100644 --- a/src/bin/initdb/initdb.c +++ b/src/bin/initdb/initdb.c @@ -114,6 +114,12 @@ static const char *const auth_methods_local[] = { NULL }; +static const char *const encryption_ciphers[] = { + "aes-128", + "aes-256", + NULL +}; + /* * these values are passed in by makefile defines */ @@ -145,6 +151,8 @@ static bool data_checksums = false; static char *xlog_dir = NULL; static char *str_wal_segment_size_mb = NULL; static int wal_segment_size_mb; +static char *enc_cipher = NULL; +static char *cluster_passphrase = NULL; /* internal vars */ @@ -1208,6 +1216,13 @@ setup_config(void) "password_encryption = scram-sha-256"); } + if (cluster_passphrase) + { + snprintf(repltok, sizeof(repltok), "cluster_passphrase_command = '%s'", + escape_quotes(cluster_passphrase)); + conflines = replace_token(conflines, "#cluster_passphrase_command = ''", repltok); + } + /* * If group access has been enabled for the cluster then it makes sense to * ensure that the log files also allow group access. Otherwise a backup @@ -1421,14 +1436,15 @@ bootstrap_template1(void) unsetenv("PGCLIENTENCODING"); snprintf(cmd, sizeof(cmd), - "\"%s\" --boot -x1 -X %u %s %s %s", + "\"%s\" --boot -x1 -X %u %s %s %s %s %s", backend_exec, wal_segment_size_mb * (1024 * 1024), data_checksums ? "-k" : "", + enc_cipher ? "-e" : "", + enc_cipher ? enc_cipher : "", boot_options, debug ? "-d 5" : ""); - PG_CMD_OPEN; for (line = bki_lines; *line != NULL; line++) @@ -2351,6 +2367,9 @@ usage(const char *progname) printf(_(" --wal-segsize=SIZE size of WAL segments, in megabytes\n")); printf(_("\nLess commonly used options:\n")); printf(_(" -d, --debug generate lots of debugging output\n")); + printf(_(" -e --enc-cipher=MODE set encryption cipher for data encryption\n")); + printf(_(" -c --cluster-passphrase-command=COMMAND\n" + " set command to obtain passphrase for data encryption key\n")); printf(_(" -k, --data-checksums use data page checksums\n")); printf(_(" -L DIRECTORY where to find the input files\n")); printf(_(" -n, --no-clean do not clean up after errors\n")); @@ -2416,6 +2435,41 @@ check_need_password(const char *authmethodlocal, const char *authmethodhost) } } +static void +check_encryption_cipher(const char *cipher, const char *passphrase, + const char *const *valid_ciphers) +{ + const char *const *p; + + if (!cipher && !passphrase) + return; + +#ifndef USE_OPENSSL + pg_log_error("cluster encryption is not supported because OpenSSL is not supported by this build"); +#endif + + /* Check both options must be specified at the same time */ + if (cipher && !passphrase) + { + pg_log_error("encryption passphrase command must be specified when encryption cipher is specified"); + exit(1); + } + + if (!cipher && passphrase) + { + pg_log_error("encryption cipher must be specified when encryption passphrase command is specified"); + exit(1); + } + + for (p = valid_ciphers; *p; p++) + { + if (strcmp(cipher, *p) == 0) + return; + } + + pg_log_error("invalid encryption cipher \"%s\"", cipher); + exit(1); +} void setup_pgdata(void) @@ -3029,6 +3083,8 @@ main(int argc, char *argv[]) {"wal-segsize", required_argument, NULL, 12}, {"data-checksums", no_argument, NULL, 'k'}, {"allow-group-access", no_argument, NULL, 'g'}, + {"enc-cipher", required_argument, NULL, 'e'}, + {"cluster-passphrase-command", required_argument, NULL, 'c'}, {NULL, 0, NULL, 0} }; @@ -3070,7 +3126,7 @@ main(int argc, char *argv[]) /* process command-line options */ - while ((c = getopt_long(argc, argv, "dD:E:kL:nNU:WA:sST:X:g", long_options, &option_index)) != -1) + while ((c = getopt_long(argc, argv, "c:dD:E:e:kL:nNU:WA:sST:X:g", long_options, &option_index)) != -1) { switch (c) { @@ -3152,6 +3208,12 @@ main(int argc, char *argv[]) case 9: pwfilename = pg_strdup(optarg); break; + case 'e': + enc_cipher = pg_strdup(optarg); + break; + case 'c': + cluster_passphrase = pg_strdup(optarg); + break; case 's': show_setting = true; break; @@ -3230,6 +3292,8 @@ main(int argc, char *argv[]) check_need_password(authmethodlocal, authmethodhost); + check_encryption_cipher(enc_cipher, cluster_passphrase, encryption_ciphers); + /* set wal segment size */ if (str_wal_segment_size_mb == NULL) wal_segment_size_mb = (DEFAULT_XLOG_SEG_SIZE) / (1024 * 1024); @@ -3289,6 +3353,11 @@ main(int argc, char *argv[]) else printf(_("Data page checksums are disabled.\n")); + if (enc_cipher) + printf(_("Data encryption using %s is enabled.\n"), enc_cipher); + else + printf(_("Data encryption is disabled.\n")); + if (pwprompt || pwfilename) get_su_pwd(); diff --git a/src/bin/pg_controldata/pg_controldata.c b/src/bin/pg_controldata/pg_controldata.c index ff17804..a1cd638 100644 --- a/src/bin/pg_controldata/pg_controldata.c +++ b/src/bin/pg_controldata/pg_controldata.c @@ -26,6 +26,7 @@ #include "catalog/pg_control.h" #include "common/controldata_utils.h" #include "common/logging.h" +#include "storage/encryption.h" #include "pg_getopt.h" #include "getopt_long.h" @@ -84,6 +85,21 @@ wal_level_str(WalLevel wal_level) return _("unrecognized wal_level"); } +static const char * +encryption_cipher_str(int val) +{ + switch (val) + { + case TDE_ENCRYPTION_OFF: + return "off"; + case TDE_ENCRYPTION_AES_128: + return "aes-128"; + case TDE_ENCRYPTION_AES_256: + return "aes-256"; + } + + return _("unrecognized encryption cipher"); +} int main(int argc, char *argv[]) @@ -336,5 +352,7 @@ main(int argc, char *argv[]) ControlFile->data_checksum_version); printf(_("Mock authentication nonce: %s\n"), mock_auth_nonce_str); + printf(_("Data encryption cipher: %s\n"), + encryption_cipher_str(ControlFile->data_encryption_cipher)); return 0; } diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h index d519252..2c260db 100644 --- a/src/include/access/xlog.h +++ b/src/include/access/xlog.h @@ -294,6 +294,7 @@ extern void UpdateControlFile(void); extern uint64 GetSystemIdentifier(void); extern char *GetMockAuthenticationNonce(void); extern bool DataChecksumsEnabled(void); +extern int GetDataEncryptionCipher(void); extern XLogRecPtr GetFakeLSNForUnloggedRel(void); extern Size XLOGShmemSize(void); extern void XLOGShmemInit(void); diff --git a/src/include/catalog/pg_control.h b/src/include/catalog/pg_control.h index ff98d9e..9d05d45 100644 --- a/src/include/catalog/pg_control.h +++ b/src/include/catalog/pg_control.h @@ -221,6 +221,9 @@ typedef struct ControlFileData /* Are data pages protected by checksums? Zero if no checksum version */ uint32 data_checksum_version; + /* Are data pages and WAL encrypted? Zero if encryption is disabled */ + uint32 data_encryption_cipher; + /* * Random nonce, used in authentication requests that need to proceed * based on values that are cluster-unique, like a SASL exchange that diff --git a/src/include/utils/guc_tables.h b/src/include/utils/guc_tables.h index d68976f..c1ae129 100644 --- a/src/include/utils/guc_tables.h +++ b/src/include/utils/guc_tables.h @@ -89,6 +89,7 @@ enum config_group STATS, STATS_MONITORING, STATS_COLLECTOR, + ENCRYPTION, AUTOVACUUM, CLIENT_CONN, CLIENT_CONN_STATEMENT, -- 2.10.5