From 1ca4975c35e8bf4df1c008a638962cb80102a802 Mon Sep 17 00:00:00 2001 From: Tristan Partin Date: Fri, 18 Apr 2025 09:48:13 -0500 Subject: [PATCH] Add GUCs to control libpq SSL parameters in walproposer and pagestore In preparation for TLS enablement of compute to {pageserver,safekeeper} connections, add GUCs to control the various SSL connection parameters. Start with the Postgres defaults, and change them when we have all the files in place for enabling TLS encryption on the connections. Note that the Neon DBaaS won't make use of all these parameters, but open source users may want to use them. Part-of: https://github.com/neondatabase/cloud/issues/25823 Signed-off-by: Tristan Partin --- pgxn/neon/libpagestore.c | 232 ++++++++++++++++++++++++++++++++++++- pgxn/neon/walproposer_pg.c | 222 ++++++++++++++++++++++++++++++++++- 2 files changed, 445 insertions(+), 9 deletions(-) diff --git a/pgxn/neon/libpagestore.c b/pgxn/neon/libpagestore.c index 64d38e7913..ae22589397 100644 --- a/pgxn/neon/libpagestore.c +++ b/pgxn/neon/libpagestore.c @@ -81,6 +81,23 @@ static int neon_compute_mode = 0; static int max_reconnect_attempts = 60; static int stripe_size; +static char *pageserver_sslcert = NULL; +static char *pageserver_sslcertmode = NULL; +static char *pageserver_sslcompression = NULL; +static char *pageserver_sslcrl = NULL; +static char *pageserver_sslcrldir = NULL; +static char *pageserver_sslkey = NULL; +static char *pageserver_sslmode = NULL; +static char *pageserver_sslpassword = NULL; +static char *pageserver_sslrootcert = NULL; +static char *pageserver_sslsni = NULL; +static char *pageserver_ssl_min_protocol_version = NULL; +static char *pageserver_ssl_max_protocol_version = NULL; + +#if PG_MAJORVERSION_NUM >= 17 +static char *pageserver_sslnegotiation = NULL; +#endif + static int pageserver_response_log_timeout = 10000; /* 2.5 minutes. A bit higher than highest default TCP retransmission timeout */ static int pageserver_response_disconnect_timeout = 150000; @@ -127,7 +144,7 @@ static uint64 pagestore_local_counter = 0; typedef enum PSConnectionState { PS_Disconnected, /* no connection yet */ PS_Connecting_Startup, /* connection starting up */ - PS_Connecting_PageStream, /* negotiating pagestream */ + PS_Connecting_PageStream, /* negotiating pagestream */ PS_Connected, /* connected, pagestream established */ } PSConnectionState; @@ -362,7 +379,7 @@ get_shard_number(BufferTag *tag) } static inline void -CLEANUP_AND_DISCONNECT(PageServer *shard) +CLEANUP_AND_DISCONNECT(PageServer *shard) { if (shard->wes_read) { @@ -384,7 +401,7 @@ CLEANUP_AND_DISCONNECT(PageServer *shard) * complete the connection (e.g. due to receiving an earlier cancellation * during connection start). * Returns true if successfully connected; false if the connection failed. - * + * * Throws errors in unrecoverable situations, or when this backend's query * is canceled. */ @@ -407,8 +424,8 @@ pageserver_connect(shardno_t shard_no, int elevel) { case PS_Disconnected: { - const char *keywords[5]; - const char *values[5]; + const char *keywords[17]; + const char *values[17]; char pid_str[16] = { 0 }; char endpoint_str[36] = { 0 }; int n_pgsql_params; @@ -482,6 +499,92 @@ pageserver_connect(shardno_t shard_no, int elevel) n_pgsql_params++; } + if (pageserver_sslcertmode) + { + keywords[n_pgsql_params] = "sslcertmode"; + values[n_pgsql_params] = pageserver_sslcertmode; + n_pgsql_params++; + } + + if (pageserver_sslcompression) + { + keywords[n_pgsql_params] = "sslcompression"; + values[n_pgsql_params] = pageserver_sslcompression; + n_pgsql_params++; + } + + if (pageserver_sslcrl) + { + keywords[n_pgsql_params] = "sslcrl"; + values[n_pgsql_params] = pageserver_sslcrl; + n_pgsql_params++; + } + + if (pageserver_sslcrldir) + { + keywords[n_pgsql_params] = "sslcrldir"; + values[n_pgsql_params] = pageserver_sslcrldir; + n_pgsql_params++; + } + + if (pageserver_sslkey) + { + keywords[n_pgsql_params] = "sslkey"; + values[n_pgsql_params] = pageserver_sslkey; + n_pgsql_params++; + } + + if (pageserver_sslmode) + { + keywords[n_pgsql_params] = "sslmode"; + values[n_pgsql_params] = pageserver_sslmode; + n_pgsql_params++; + } + +#if PG_MAJORVERSION_NUM >= 17 + if (pageserver_sslnegotiation) + { + keywords[n_pgsql_params] = "sslnegotiation"; + values[n_pgsql_params] = pageserver_sslnegotiation; + n_pgsql_params++; + } +#endif + + if (pageserver_sslpassword) + { + keywords[n_pgsql_params] = "sslpassword"; + values[n_pgsql_params] = pageserver_sslpassword; + n_pgsql_params++; + } + + if (pageserver_sslrootcert) + { + keywords[n_pgsql_params] = "sslrootcert"; + values[n_pgsql_params] = pageserver_sslrootcert; + n_pgsql_params++; + } + + if (pageserver_sslsni) + { + keywords[n_pgsql_params] = "sslsni"; + values[n_pgsql_params] = pageserver_sslsni; + n_pgsql_params++; + } + + if (pageserver_ssl_max_protocol_version) + { + keywords[n_pgsql_params] = "ssl_max_protocol_version"; + values[n_pgsql_params] = pageserver_ssl_max_protocol_version; + n_pgsql_params++; + } + + if (pageserver_ssl_min_protocol_version) + { + keywords[n_pgsql_params] = "ssl_min_protocol_version"; + values[n_pgsql_params] = pageserver_ssl_min_protocol_version; + n_pgsql_params++; + } + { bool param_set = false; switch (neon_compute_mode) @@ -1477,6 +1580,125 @@ pg_init_libpagestore(void) PGC_POSTMASTER, 0, NULL, NULL, NULL); + DefineCustomStringVariable( + "neon.pageserver_sslcert", + "SSL certificate path", + "Refer to the Postgres documentation on libpq's sslcert keyword.", + &pageserver_sslcert, + NULL, + PGC_POSTMASTER, + 0, + NULL, NULL, NULL); + DefineCustomStringVariable( + "neon.pageserver_sslcertmode", + "SSL certificate mode", + "Refer to the Postgres documentation on libpq's sslcertmode keyword.", + &pageserver_sslcertmode, + NULL, + PGC_POSTMASTER, + 0, + NULL, NULL, NULL); + DefineCustomStringVariable( + "neon.pageserver_sslcrl", + "Path to the SSL server certificate revocation list", + "Refer to the Postgres documentation on libpq's sslcrl keyword.", + &pageserver_sslcrl, + NULL, + PGC_POSTMASTER, + 0, + NULL, NULL, NULL); + DefineCustomStringVariable( + "neon.pageserver_sslcrldir", + "Path to the directory of the SSL server certificate revocation list", + "Refer to the Postgres documentation on libpq's sslcrldir keyword.", + &pageserver_sslcrldir, + NULL, + PGC_POSTMASTER, + 0, + NULL, NULL, NULL); + DefineCustomStringVariable( + "neon.pageserver_sslcompression", + "SSL compression", + "Refer to the Postgres documentation on libpq's sslcompression keyword.", + &pageserver_sslcompression, + NULL, + PGC_POSTMASTER, + 0, + NULL, NULL, NULL); + DefineCustomStringVariable( + "neon.pageserver_sslkey", + "SSL key", + "Refer to the Postgres documentation on libpq's sslkey keyword.", + &pageserver_sslkey, + NULL, + PGC_POSTMASTER, + 0, + NULL, NULL, NULL); + DefineCustomStringVariable( + "neon.pageserver_sslmode", + "SSL mode", + "Refer to the Postgres documentation on libpq's sslmode keyword.", + &pageserver_sslmode, + NULL, + PGC_POSTMASTER, + 0, + NULL, NULL, NULL); +#if PG_MAJORVERSION_NUM >= 17 + DefineCustomStringVariable( + "neon.pageserver_sslnegotiation", + "SSL negotiation", + "Refer to the Postgres documentation on libpq's sslnegotiation keyword.", + &pageserver_sslnegotiation, + NULL, + PGC_POSTMASTER, + 0, + NULL, NULL, NULL); +#endif + DefineCustomStringVariable( + "neon.pageserver_sslpassword", + "SSL passphrase", + "Refer to the Postgres documentation on libpq's sslpassword keyword.", + &pageserver_sslpassword, + NULL, + PGC_POSTMASTER, + 0, + NULL, NULL, NULL); + DefineCustomStringVariable( + "neon.pageserver_sslrootcert", + "SSL root certificate", + "Refer to the Postgres documentation on libpq's sslrootcert keyword.", + &pageserver_sslrootcert, + NULL, + PGC_POSTMASTER, + 0, + NULL, NULL, NULL); + DefineCustomStringVariable( + "neon.pageserver_sslsni", + "TLS SNI extension", + "Refer to the Postgres documentation on libpq's sslsni keyword.", + &pageserver_sslsni, + NULL, + PGC_POSTMASTER, + 0, + NULL, NULL, NULL); + DefineCustomStringVariable( + "neon.pageserver_ssl_max_protocol_version", + "SSL maxiumum protocol version", + "Refer to the Postgres documentation on libpq's ssl_max_protocol_version keyword.", + &pageserver_ssl_max_protocol_version, + NULL, + PGC_POSTMASTER, + 0, + NULL, NULL, NULL); + DefineCustomStringVariable( + "neon.pageserver_ssl_min_protocol_version", + "SSL minimum protocol version", + "Refer to the Postgres documentation on libpq's ssl_min_protocol_version keyword.", + &pageserver_ssl_min_protocol_version, + NULL, + PGC_POSTMASTER, + 0, + NULL, NULL, NULL); relsize_hash_init(); diff --git a/pgxn/neon/walproposer_pg.c b/pgxn/neon/walproposer_pg.c index a061639815..d334472013 100644 --- a/pgxn/neon/walproposer_pg.c +++ b/pgxn/neon/walproposer_pg.c @@ -64,6 +64,22 @@ char *wal_acceptors_list = ""; int wal_acceptor_reconnect_timeout = 1000; int wal_acceptor_connection_timeout = 10000; int safekeeper_proto_version = 2; +static char *safekeeper_sslcert = NULL; +static char *safekeeper_sslcertmode = NULL; +static char *safekeeper_sslcompression = NULL; +static char *safekeeper_sslcrl = NULL; +static char *safekeeper_sslcrldir = NULL; +static char *safekeeper_sslkey = NULL; +static char *safekeeper_sslmode = NULL; +static char *safekeeper_sslpassword = NULL; +static char *safekeeper_sslrootcert = NULL; +static char *safekeeper_sslsni = NULL; +static char *safekeeper_ssl_min_protocol_version = NULL; +static char *safekeeper_ssl_max_protocol_version = NULL; + +#if PG_MAJORVERSION_NUM >= 17 +static char *safekeeper_sslnegotiation = NULL; +#endif /* Set to true in the walproposer bgw. */ static bool am_walproposer; @@ -232,6 +248,125 @@ nwp_register_gucs(void) PGC_POSTMASTER, 0, NULL, NULL, NULL); + DefineCustomStringVariable( + "neon.safekeeper_sslcert", + "SSL certificate path", + "Refer to the Postgres documentation on libpq's sslcert keyword.", + &safekeeper_sslcert, + NULL, + PGC_POSTMASTER, + 0, + NULL, NULL, NULL); + DefineCustomStringVariable( + "neon.safekeeper_sslcertmode", + "SSL certificate mode", + "Refer to the Postgres documentation on libpq's sslcertmode keyword.", + &safekeeper_sslcertmode, + NULL, + PGC_POSTMASTER, + 0, + NULL, NULL, NULL); + DefineCustomStringVariable( + "neon.safekeeper_sslcrl", + "Path to the SSL server certificate revocation list", + "Refer to the Postgres documentation on libpq's sslcrl keyword.", + &safekeeper_sslcrl, + NULL, + PGC_POSTMASTER, + 0, + NULL, NULL, NULL); + DefineCustomStringVariable( + "neon.safekeeper_sslcrldir", + "Path to the directory of the SSL server certificate revocation list", + "Refer to the Postgres documentation on libpq's sslcrldir keyword.", + &safekeeper_sslcrldir, + NULL, + PGC_POSTMASTER, + 0, + NULL, NULL, NULL); + DefineCustomStringVariable( + "neon.safekeeper_sslcompression", + "SSL compression", + "Refer to the Postgres documentation on libpq's sslcompression keyword.", + &safekeeper_sslcompression, + NULL, + PGC_POSTMASTER, + 0, + NULL, NULL, NULL); + DefineCustomStringVariable( + "neon.safekeeper_sslkey", + "SSL key", + "Refer to the Postgres documentation on libpq's sslkey keyword.", + &safekeeper_sslkey, + NULL, + PGC_POSTMASTER, + 0, + NULL, NULL, NULL); + DefineCustomStringVariable( + "neon.safekeeper_sslmode", + "SSL mode", + "Refer to the Postgres documentation on libpq's sslmode keyword.", + &safekeeper_sslmode, + NULL, + PGC_POSTMASTER, + 0, + NULL, NULL, NULL); +#if PG_MAJORVERSION_NUM >= 17 + DefineCustomStringVariable( + "neon.safekeeper_sslnegotiation", + "SSL negotiation", + "Refer to the Postgres documentation on libpq's sslnegotiation keyword.", + &safekeeper_sslnegotiation, + NULL, + PGC_POSTMASTER, + 0, + NULL, NULL, NULL); +#endif + DefineCustomStringVariable( + "neon.safekeeper_sslpassword", + "SSL passphrase", + "Refer to the Postgres documentation on libpq's sslpassword keyword.", + &safekeeper_sslpassword, + NULL, + PGC_POSTMASTER, + 0, + NULL, NULL, NULL); + DefineCustomStringVariable( + "neon.safekeeper_sslrootcert", + "SSL root certificate", + "Refer to the Postgres documentation on libpq's sslrootcert keyword.", + &safekeeper_sslrootcert, + NULL, + PGC_POSTMASTER, + 0, + NULL, NULL, NULL); + DefineCustomStringVariable( + "neon.safekeeper_sslsni", + "TLS SNI extension", + "Refer to the Postgres documentation on libpq's sslsni keyword.", + &safekeeper_sslsni, + NULL, + PGC_POSTMASTER, + 0, + NULL, NULL, NULL); + DefineCustomStringVariable( + "neon.safekeeper_ssl_max_protocol_version", + "SSL maxiumum protocol version", + "Refer to the Postgres documentation on libpq's ssl_max_protocol_version keyword.", + &safekeeper_ssl_max_protocol_version, + NULL, + PGC_POSTMASTER, + 0, + NULL, NULL, NULL); + DefineCustomStringVariable( + "neon.safekeeper_ssl_min_protocol_version", + "SSL minimum protocol version", + "Refer to the Postgres documentation on libpq's ssl_min_protocol_version keyword.", + &safekeeper_ssl_min_protocol_version, + NULL, + PGC_POSTMASTER, + 0, + NULL, NULL, NULL); } @@ -843,15 +978,13 @@ walprop_status(Safekeeper *sk) WalProposerConn * libpqwp_connect_start(char *conninfo) { - PGconn *pg_conn; WalProposerConn *conn; - const char *keywords[3]; - const char *values[3]; + const char *keywords[16]; + const char *values[16]; int n; char *password = neon_auth_token; - /* * Connect using the given connection string. If the NEON_AUTH_TOKEN * environment variable was set, use that as the password. @@ -871,9 +1004,90 @@ libpqwp_connect_start(char *conninfo) keywords[n] = "dbname"; values[n] = conninfo; n++; + if (safekeeper_sslcert) + { + keywords[n] = "sslcert"; + values[n] = safekeeper_sslcert; + n++; + } + if (safekeeper_sslcertmode) + { + keywords[n] = "sslcertmode"; + values[n] = safekeeper_sslcertmode; + n++; + } + if (safekeeper_sslcompression) + { + keywords[n] = "sslcompression"; + values[n] = safekeeper_sslcompression; + n++; + } + if (safekeeper_sslcrl) + { + keywords[n] = "sslcrl"; + values[n] = safekeeper_sslcrl; + n++; + } + if (safekeeper_sslcrldir) + { + keywords[n] = "sslcrldir"; + values[n] = safekeeper_sslcrldir; + n++; + } + if (safekeeper_sslkey) + { + keywords[n] = "sslkey"; + values[n] = safekeeper_sslkey; + n++; + } + if (safekeeper_sslmode) + { + keywords[n] = "sslmode"; + values[n] = safekeeper_sslmode; + n++; + } +#if PG_MAJORVERSION_NUM >= 17 + if (safekeeper_sslnegotiation) + { + keywords[n] = "sslnegotiation"; + values[n] = safekeeper_sslnegotiation; + n++; + } +#endif + if (safekeeper_sslpassword) + { + keywords[n] = "sslpassword"; + values[n] = safekeeper_sslpassword; + n++; + } + if (safekeeper_sslrootcert) + { + keywords[n] = "sslrootcert"; + values[n] = safekeeper_sslrootcert; + n++; + } + if (safekeeper_sslsni) + { + keywords[n] = "sslsni"; + values[n] = safekeeper_sslsni; + n++; + } + if (safekeeper_ssl_max_protocol_version) + { + keywords[n] = "ssl_max_protocol_version"; + values[n] = safekeeper_ssl_max_protocol_version; + n++; + } + if (safekeeper_ssl_min_protocol_version) + { + keywords[n] = "ssl_min_protocol_version"; + values[n] = safekeeper_ssl_min_protocol_version; + n++; + } keywords[n] = NULL; values[n] = NULL; n++; + pg_conn = PQconnectStartParams(keywords, values, 1); /*