From 208d4b7ececc75ef93bdb9b948349ba6f0b69363 Mon Sep 17 00:00:00 2001 From: Bruce Momjian Date: Fri, 25 Jun 2021 16:56:53 -0400 Subject: [PATCH 06/12] cfe-06-backend_over_cfe-05-crypto squash commit --- doc/src/sgml/storage.sgml | 5 +++ src/backend/Makefile | 2 +- src/backend/access/transam/xlog.c | 33 +++++++++++++++++++ src/backend/backup/basebackup.c | 5 +++ src/backend/bootstrap/bootstrap.c | 29 +++++++++++++++- src/backend/crypto/kmgr.c | 3 ++ src/backend/libpq/be-secure-common.c | 14 ++++++++ src/backend/main/main.c | 3 ++ src/backend/postmaster/postmaster.c | 13 +++++++- src/backend/storage/ipc/ipci.c | 3 ++ src/backend/storage/lmgr/lwlocknames.txt | 1 + src/backend/tcop/postgres.c | 28 +++++++++++++++- src/backend/utils/activity/wait_event.c | 9 +++++ src/backend/utils/misc/guc_tables.c | 25 ++++++++++++++ src/backend/utils/misc/pg_controldata.c | 11 +++++-- src/backend/utils/misc/postgresql.conf.sample | 5 +++ src/include/access/xlog.h | 5 +++ src/include/catalog/pg_control.h | 5 ++- src/include/postmaster/postmaster.h | 2 ++ src/include/utils/guc_tables.h | 1 + 20 files changed, 194 insertions(+), 8 deletions(-) diff --git a/doc/src/sgml/storage.sgml b/doc/src/sgml/storage.sgml index e5b9f3f1ff..f9dbf04735 100644 --- a/doc/src/sgml/storage.sgml +++ b/doc/src/sgml/storage.sgml @@ -77,6 +77,11 @@ Item Subdirectory containing transaction commit timestamp data + + pg_cryptokeys + Subdirectory containing file encryption keys + + pg_dynshmem Subdirectory containing files used by the dynamic shared memory diff --git a/src/backend/Makefile b/src/backend/Makefile index 1a5239b822..4b8cdd1b44 100644 --- a/src/backend/Makefile +++ b/src/backend/Makefile @@ -22,7 +22,7 @@ SUBDIRS = access backup bootstrap catalog parser commands executor \ main nodes optimizer partitioning port postmaster \ regex replication rewrite \ statistics storage tcop tsearch utils $(top_builddir)/src/timezone \ - jit + jit crypto include $(srcdir)/common.mk diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c index dea978a962..de92e88d81 100644 --- a/src/backend/access/transam/xlog.c +++ b/src/backend/access/transam/xlog.c @@ -69,6 +69,7 @@ #include "catalog/pg_database.h" #include "common/controldata_utils.h" #include "common/file_utils.h" +#include "crypto/kmgr.h" #include "executor/instrument.h" #include "miscadmin.h" #include "pg_trace.h" @@ -76,6 +77,7 @@ #include "port/atomics.h" #include "port/pg_iovec.h" #include "postmaster/bgwriter.h" +#include "postmaster/postmaster.h" #include "postmaster/startup.h" #include "postmaster/walwriter.h" #include "replication/logical.h" @@ -109,6 +111,7 @@ #include "utils/varlena.h" extern uint32 bootstrap_data_checksum_version; +extern int bootstrap_file_encryption_method; /* timeline ID to be used when bootstrapping */ #define BootstrapTimeLineID 1 @@ -3898,6 +3901,7 @@ InitControlFile(uint64 sysidentifier) ControlFile->wal_log_hints = wal_log_hints; ControlFile->track_commit_timestamp = track_commit_timestamp; ControlFile->data_checksum_version = bootstrap_data_checksum_version; + ControlFile->file_encryption_method = bootstrap_file_encryption_method; } static void @@ -4185,6 +4189,12 @@ ReadControlFile(void) /* Make the initdb settings visible as GUC variables, too */ SetConfigOption("data_checksums", DataChecksumsEnabled() ? "yes" : "no", PGC_INTERNAL, PGC_S_DYNAMIC_DEFAULT); + + StaticAssertStmt(lengthof(encryption_methods) == NUM_ENCRYPTION_METHODS, + "encryption_methods[] must match NUM_ENCRYPTION_METHODS"); + SetConfigOption("file_encryption_method", + encryption_methods[ControlFile->file_encryption_method].name, + PGC_INTERNAL, PGC_S_OVERRIDE); } /* @@ -4227,6 +4237,21 @@ DataChecksumsEnabled(void) return (ControlFile->data_checksum_version > 0); } +/* + * Is cluster file encryption enabled? + */ +int +GetFileEncryptionMethod(void) +{ + if (IsBootstrapProcessingMode()) + return bootstrap_file_encryption_method; + else + { + Assert(ControlFile != NULL); + return ControlFile->file_encryption_method; + } +} + /* * Returns a fake LSN for unlogged relations. * @@ -4806,6 +4831,14 @@ BootStrapXLOG(void) /* some additional ControlFile fields are set in WriteControlFile() */ WriteControlFile(); + BootStrapKmgr(); + + if (terminal_fd != -1) + { + close(terminal_fd); + terminal_fd = -1; + } + /* Bootstrap the commit log, too */ BootStrapCLOG(); BootStrapCommitTs(); diff --git a/src/backend/backup/basebackup.c b/src/backend/backup/basebackup.c index 74fb529380..35e955e036 100644 --- a/src/backend/backup/basebackup.c +++ b/src/backend/backup/basebackup.c @@ -24,6 +24,7 @@ #include "backup/basebackup_target.h" #include "commands/defrem.h" #include "common/compression.h" +#include "common/kmgr_utils.h" #include "common/file_perm.h" #include "lib/stringinfo.h" #include "miscadmin.h" @@ -129,6 +130,10 @@ struct exclude_list_item */ static const char *const excludeDirContents[] = { + /* Skip temporary crypto key directories */ + NEW_KMGR_DIR, + OLD_KMGR_DIR, + /* * Skip temporary statistics files. PG_STAT_TMP_DIR must be skipped * because extensions like pg_stat_statements store data there. diff --git a/src/backend/bootstrap/bootstrap.c b/src/backend/bootstrap/bootstrap.c index 58247d826d..9fc09b34de 100644 --- a/src/backend/bootstrap/bootstrap.c +++ b/src/backend/bootstrap/bootstrap.c @@ -33,6 +33,7 @@ #include "miscadmin.h" #include "nodes/makefuncs.h" #include "pg_getopt.h" +#include "postmaster/postmaster.h" /* TODO: verify we need this still */ #include "storage/bufmgr.h" #include "storage/bufpage.h" #include "storage/condition_variable.h" @@ -46,6 +47,8 @@ #include "utils/relmapper.h" uint32 bootstrap_data_checksum_version = 0; /* No checksum */ +int bootstrap_file_encryption_method = DISABLED_ENCRYPTION_METHOD; +char *bootstrap_old_key_datadir = NULL; /* disabled */ static void CheckerModeMain(void); @@ -221,7 +224,7 @@ BootstrapModeMain(int argc, char *argv[], bool check_only) argv++; argc--; - while ((flag = getopt(argc, argv, "B:c:d:D:Fkr:X:-:")) != -1) + while ((flag = getopt(argc, argv, "B:c:d:D:FkK:r:R:u:X:-:")) != -1) { switch (flag) { @@ -250,9 +253,33 @@ BootstrapModeMain(int argc, char *argv[], bool check_only) case 'k': bootstrap_data_checksum_version = PG_DATA_CHECKSUM_VERSION; break; + case 'K': + { + int i; + + /* method 0/disabled cannot be specified */ + for (i = DISABLED_ENCRYPTION_METHOD + 1; + i < NUM_ENCRYPTION_METHODS; i++) + if (pg_strcasecmp(optarg, encryption_methods[i].name) == 0) + { + bootstrap_file_encryption_method = i; + break; + } + if (i == NUM_ENCRYPTION_METHODS) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("invalid encryption method specified"))); + } + break; case 'r': strlcpy(OutputFileName, optarg, MAXPGPATH); break; + case 'R': + terminal_fd = atoi(optarg); + break; + case 'u': + bootstrap_old_key_datadir = pstrdup(optarg); + break; case 'X': { int WalSegSz = strtoul(optarg, NULL, 0); diff --git a/src/backend/crypto/kmgr.c b/src/backend/crypto/kmgr.c index 4b74961323..a83effbca8 100644 --- a/src/backend/crypto/kmgr.c +++ b/src/backend/crypto/kmgr.c @@ -253,6 +253,9 @@ InitializeKmgr(void) char live_path[MAXPGPATH]; unsigned char cluster_key[KMGR_CLUSTER_KEY_LEN]; + if (!FileEncryptionEnabled) + return; + #ifndef USE_OPENSSL ereport(ERROR, (errcode(ERRCODE_CONFIG_FILE_ERROR), diff --git a/src/backend/libpq/be-secure-common.c b/src/backend/libpq/be-secure-common.c index 2719ce1403..9357c80ea6 100644 --- a/src/backend/libpq/be-secure-common.c +++ b/src/backend/libpq/be-secure-common.c @@ -22,6 +22,7 @@ #include #include +#include "postmaster/postmaster.h" #include "common/string.h" #include "libpq/libpq.h" #include "storage/fd.h" @@ -61,6 +62,19 @@ run_ssl_passphrase_command(const char *prompt, bool is_server_start, char *buf, appendStringInfoString(&command, prompt); p++; break; + case 'R': + { + char fd_str[20]; + + if (terminal_fd == -1) + ereport(ERROR, + (errcode(ERRCODE_INTERNAL_ERROR), + errmsg("ssl_passphrase_command referenced %%R, but -R not specified"))); + p++; + snprintf(fd_str, sizeof(fd_str), "%d", terminal_fd); + appendStringInfoString(&command, fd_str); + break; + } case '%': appendStringInfoChar(&command, '%'); p++; diff --git a/src/backend/main/main.c b/src/backend/main/main.c index f5da4260a1..ddf3fcb6fb 100644 --- a/src/backend/main/main.c +++ b/src/backend/main/main.c @@ -343,6 +343,7 @@ help(const char *progname) #endif printf(_(" -N MAX-CONNECT maximum number of allowed connections\n")); printf(_(" -p PORT port number to listen on\n")); + printf(_(" -R fd prompt for the cluster key\n")); printf(_(" -s show statistics after each query\n")); printf(_(" -S WORK-MEM set amount of memory for sorts (in kB)\n")); printf(_(" -V, --version output version information, then exit\n")); @@ -371,7 +372,9 @@ help(const char *progname) printf(_(" --boot selects bootstrapping mode (must be first argument)\n")); printf(_(" --check selects check mode (must be first argument)\n")); printf(_(" DBNAME database name (mandatory argument in bootstrapping mode)\n")); + printf(_(" -K LEN enable cluster file encryption with specified key bit length\n")); printf(_(" -r FILENAME send stdout and stderr to given file\n")); + printf(_(" -u DATADIR copy encryption keys from datadir\n")); printf(_("\nPlease read the documentation for the complete list of run-time\n" "configuration settings and how to set them on the command line or in\n" diff --git a/src/backend/postmaster/postmaster.c b/src/backend/postmaster/postmaster.c index 30fb576ac3..aec9b094bd 100644 --- a/src/backend/postmaster/postmaster.c +++ b/src/backend/postmaster/postmaster.c @@ -98,6 +98,7 @@ #include "common/ip.h" #include "common/pg_prng.h" #include "common/string.h" +#include "crypto/kmgr.h" #include "lib/ilist.h" #include "libpq/auth.h" #include "libpq/libpq.h" @@ -231,6 +232,7 @@ static int SendStop = false; /* still more option variables */ bool EnableSSL = false; +int terminal_fd = -1; int PreAuthDelay = 0; int AuthenticationTimeout = 60; @@ -697,7 +699,7 @@ PostmasterMain(int argc, char *argv[]) * tcop/postgres.c (the option sets should not conflict) and with the * common help() function in main/main.c. */ - while ((opt = getopt(argc, argv, "B:bc:C:D:d:EeFf:h:ijk:lN:nOPp:r:S:sTt:W:-:")) != -1) + while ((opt = getopt(argc, argv, "B:bc:C:D:d:EeFf:h:ijk:lN:nOPp:r:R:S:sTt:W:-:")) != -1) { switch (opt) { @@ -788,6 +790,10 @@ PostmasterMain(int argc, char *argv[]) /* only used by single-user backend */ break; + case 'R': + terminal_fd = atoi(optarg); + break; + case 'S': SetConfigOption("work_mem", optarg, PGC_POSTMASTER, PGC_S_ARGV); break; @@ -1347,6 +1353,11 @@ PostmasterMain(int argc, char *argv[]) pfree(rawstring); } + InitializeKmgr(); + + if (terminal_fd != -1) + close(terminal_fd); + /* * check that we have some socket to listen on */ diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c index b204ecdbc3..061da11a58 100644 --- a/src/backend/storage/ipc/ipci.c +++ b/src/backend/storage/ipc/ipci.c @@ -25,6 +25,7 @@ #include "access/xlogprefetcher.h" #include "access/xlogrecovery.h" #include "commands/async.h" +#include "crypto/kmgr.h" #include "miscadmin.h" #include "pgstat.h" #include "postmaster/autovacuum.h" @@ -142,6 +143,7 @@ CalculateShmemSize(int *num_semaphores) size = add_size(size, SyncScanShmemSize()); size = add_size(size, AsyncShmemSize()); size = add_size(size, StatsShmemSize()); + size = add_size(size, KmgrShmemSize()); #ifdef EXEC_BACKEND size = add_size(size, ShmemBackendArraySize()); #endif @@ -294,6 +296,7 @@ CreateSharedMemoryAndSemaphores(void) SyncScanShmemInit(); AsyncShmemInit(); StatsShmemInit(); + KmgrShmemInit(); #ifdef EXEC_BACKEND diff --git a/src/backend/storage/lmgr/lwlocknames.txt b/src/backend/storage/lmgr/lwlocknames.txt index 6c7cf6c295..56ac32407f 100644 --- a/src/backend/storage/lmgr/lwlocknames.txt +++ b/src/backend/storage/lmgr/lwlocknames.txt @@ -53,3 +53,4 @@ XactTruncationLock 44 # 45 was XactTruncationLock until removal of BackendRandomLock WrapLimitsVacuumLock 46 NotifyQueueTailLock 47 +KmgrFileLock 48 diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c index a9a1851c94..5970ec97a5 100644 --- a/src/backend/tcop/postgres.c +++ b/src/backend/tcop/postgres.c @@ -35,6 +35,7 @@ #include "commands/async.h" #include "commands/prepare.h" #include "common/pg_prng.h" +#include "crypto/kmgr.h" #include "jit/jit.h" #include "libpq/libpq.h" #include "libpq/pqformat.h" @@ -3718,7 +3719,7 @@ process_postgres_switches(int argc, char *argv[], GucContext ctx, * postmaster/postmaster.c (the option sets should not conflict) and with * the common help() function in main/main.c. */ - while ((flag = getopt(argc, argv, "B:bc:C:D:d:EeFf:h:ijk:lN:nOPp:r:S:sTt:v:W:-:")) != -1) + while ((flag = getopt(argc, argv, "B:bc:C:D:d:EeFf:h:ijk:lN:nOPp:r:R:S:sTt:v:W:-:")) != -1) { switch (flag) { @@ -3810,6 +3811,19 @@ process_postgres_switches(int argc, char *argv[], GucContext ctx, strlcpy(OutputFileName, optarg, MAXPGPATH); break; + case 'R': + terminal_fd = atoi(optarg); + if (terminal_fd == -1) + { + /* + * Allow file descriptor closing to be bypassed via -1. + * We just duplicate sterr. This is useful for + * single-user mode. + */ + terminal_fd = dup(2); + } + break; + case 'S': SetConfigOption("work_mem", optarg, ctx, gucsource); break; @@ -4055,6 +4069,18 @@ PostgresMain(const char *dbname, const char *username) SetProcessingMode(InitProcessing); + /* + * Initialize kmgr for cluster encryption. Since kmgr needs to attach to + * shared memory the initialization must be called after BaseInit(). + */ + if (!IsUnderPostmaster) + { + InitializeKmgr(); + + if (terminal_fd != -1) + close(terminal_fd); + } + /* * Set up signal handlers. (InitPostmasterChild or InitStandaloneProcess * has already set up BlockSig and made that the active signal mask.) diff --git a/src/backend/utils/activity/wait_event.c b/src/backend/utils/activity/wait_event.c index 92f24a6c9b..6f7953accb 100644 --- a/src/backend/utils/activity/wait_event.c +++ b/src/backend/utils/activity/wait_event.c @@ -591,6 +591,15 @@ pgstat_get_wait_io(WaitEventIO w) case WAIT_EVENT_DSM_FILL_ZERO_WRITE: event_name = "DSMFillZeroWrite"; break; + case WAIT_EVENT_KEY_FILE_READ: + event_name = "KeyFileRead"; + break; + case WAIT_EVENT_KEY_FILE_WRITE: + event_name = "KeyFileWrite"; + break; + case WAIT_EVENT_KEY_FILE_SYNC: + event_name = "KeyFileSync"; + break; case WAIT_EVENT_LOCK_FILE_ADDTODATADIR_READ: event_name = "LockFileAddToDataDirRead"; break; diff --git a/src/backend/utils/misc/guc_tables.c b/src/backend/utils/misc/guc_tables.c index 05ab087934..402f1aa8f0 100644 --- a/src/backend/utils/misc/guc_tables.c +++ b/src/backend/utils/misc/guc_tables.c @@ -40,6 +40,7 @@ #include "commands/trigger.h" #include "commands/user.h" #include "commands/vacuum.h" +#include "crypto/kmgr.h" #include "jit/jit.h" #include "libpq/auth.h" #include "libpq/libpq.h" @@ -565,6 +566,7 @@ static char *recovery_target_string; static char *recovery_target_xid_string; static char *recovery_target_name_string; static char *recovery_target_lsn_string; +static char *file_encryption_method_str; /* should be static, but commands/variable.c needs to get at this */ char *role_string; @@ -687,6 +689,8 @@ const char *const config_group_names[] = gettext_noop("Statistics / Monitoring"), /* STATS_CUMULATIVE */ gettext_noop("Statistics / Cumulative Query and Index Statistics"), + /* ENCRYPTION */ + gettext_noop("Encryption"), /* AUTOVACUUM */ gettext_noop("Autovacuum"), /* CLIENT_CONN_STATEMENT */ @@ -4405,6 +4409,27 @@ struct config_string ConfigureNamesString[] = NULL, NULL, NULL }, + { + {"cluster_key_command", PGC_SIGHUP, ENCRYPTION, + gettext_noop("Command to obtain cluster key for cluster file encryption."), + NULL + }, + &cluster_key_command, + "", + NULL, NULL, NULL + }, + + { + {"file_encryption_method", PGC_INTERNAL, PRESET_OPTIONS, + gettext_noop("Shows the cluster file encryption method."), + NULL, + GUC_NOT_IN_SAMPLE | GUC_DISALLOW_IN_FILE + }, + &file_encryption_method_str, + "", + NULL, NULL, NULL + }, + { {"application_name", PGC_USERSET, LOGGING_WHAT, gettext_noop("Sets the application name to be reported in statistics and logs."), diff --git a/src/backend/utils/misc/pg_controldata.c b/src/backend/utils/misc/pg_controldata.c index 781f8b8758..d5fbf8f7df 100644 --- a/src/backend/utils/misc/pg_controldata.c +++ b/src/backend/utils/misc/pg_controldata.c @@ -263,8 +263,8 @@ pg_control_recovery(PG_FUNCTION_ARGS) Datum pg_control_init(PG_FUNCTION_ARGS) { - Datum values[11]; - bool nulls[11]; + Datum values[12]; + bool nulls[12]; TupleDesc tupdesc; HeapTuple htup; ControlFileData *ControlFile; @@ -274,7 +274,7 @@ pg_control_init(PG_FUNCTION_ARGS) * Construct a tuple descriptor for the result row. This must match this * function's pg_proc entry! */ - tupdesc = CreateTemplateTupleDesc(11); + tupdesc = CreateTemplateTupleDesc(12); TupleDescInitEntry(tupdesc, (AttrNumber) 1, "max_data_alignment", INT4OID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 2, "database_block_size", @@ -297,6 +297,8 @@ pg_control_init(PG_FUNCTION_ARGS) BOOLOID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 11, "data_page_checksum_version", INT4OID, -1, 0); + TupleDescInitEntry(tupdesc, (AttrNumber) 12, "file_encryption_method", + INT4OID, -1, 0); tupdesc = BlessTupleDesc(tupdesc); /* read the control file */ @@ -338,6 +340,9 @@ pg_control_init(PG_FUNCTION_ARGS) values[10] = Int32GetDatum(ControlFile->data_checksum_version); nulls[10] = false; + values[11] = Int32GetDatum(ControlFile->file_encryption_method); + nulls[11] = false; + htup = heap_form_tuple(tupdesc, values, nulls); PG_RETURN_DATUM(HeapTupleGetDatum(htup)); diff --git a/src/backend/utils/misc/postgresql.conf.sample b/src/backend/utils/misc/postgresql.conf.sample index 868d21c351..96b5c869e1 100644 --- a/src/backend/utils/misc/postgresql.conf.sample +++ b/src/backend/utils/misc/postgresql.conf.sample @@ -659,6 +659,11 @@ # autovacuum, -1 means use # vacuum_cost_limit +#------------------------------------------------------------------------------ +# ENCRYPTION +#------------------------------------------------------------------------------ + +#cluster_key_command = '' #------------------------------------------------------------------------------ # CLIENT CONNECTION DEFAULTS diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h index 1fbd48fbda..8f219fbacc 100644 --- a/src/include/access/xlog.h +++ b/src/include/access/xlog.h @@ -13,6 +13,7 @@ #include "access/xlogbackup.h" #include "access/xlogdefs.h" +#include "common/kmgr_utils.h" #include "datatype/timestamp.h" #include "lib/stringinfo.h" #include "nodes/pg_list.h" @@ -224,6 +225,10 @@ extern XLogRecPtr GetXLogWriteRecPtr(void); extern uint64 GetSystemIdentifier(void); extern char *GetMockAuthenticationNonce(void); extern bool DataChecksumsEnabled(void); + +#define FileEncryptionEnabled (GetFileEncryptionMethod() != DISABLED_ENCRYPTION_METHOD) +extern int GetFileEncryptionMethod(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 06368e2366..db7de77287 100644 --- a/src/include/catalog/pg_control.h +++ b/src/include/catalog/pg_control.h @@ -22,7 +22,7 @@ /* Version identifier for this pg_control format */ -#define PG_CONTROL_VERSION 1300 +#define PG_CONTROL_VERSION 1400 /* Nonce key length, see below */ #define MOCK_AUTH_NONCE_LEN 32 @@ -226,6 +226,9 @@ typedef struct ControlFileData */ char mock_authentication_nonce[MOCK_AUTH_NONCE_LEN]; + /* File encryption method; index into encryption_methods[]. */ + int file_encryption_method; + /* CRC of all above ... MUST BE LAST! */ pg_crc32c crc; } ControlFileData; diff --git a/src/include/postmaster/postmaster.h b/src/include/postmaster/postmaster.h index 90e333ccd2..deac25e115 100644 --- a/src/include/postmaster/postmaster.h +++ b/src/include/postmaster/postmaster.h @@ -31,6 +31,8 @@ extern PGDLLIMPORT char *bonjour_name; extern PGDLLIMPORT bool restart_after_crash; extern PGDLLIMPORT bool remove_temp_files_after_crash; +extern int terminal_fd; + #ifdef WIN32 extern PGDLLIMPORT HANDLE PostmasterHandle; #else diff --git a/src/include/utils/guc_tables.h b/src/include/utils/guc_tables.h index 51f7f09fde..1446b334b1 100644 --- a/src/include/utils/guc_tables.h +++ b/src/include/utils/guc_tables.h @@ -86,6 +86,7 @@ enum config_group PROCESS_TITLE, STATS_MONITORING, STATS_CUMULATIVE, + ENCRYPTION, AUTOVACUUM, CLIENT_CONN_STATEMENT, CLIENT_CONN_LOCALE, -- 2.37.0 (Apple Git-136)