From 1b275fec48762c34cdf9ce158c88956a3ebb0372 Mon Sep 17 00:00:00 2001 From: Bob Ross Date: Wed, 25 Mar 2026 05:55:29 -0400 Subject: [PATCH] feat: reload SSL certificates on SIGHUP without restart MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Allow Pgpool-II to pick up rotated TLS certificates (and any change to SSL-related configuration) when receiving SIGHUP (i.e. systemctl reload pgpool2), matching the behavior PostgreSQL has had since PostgreSQL 12. Problem ------- All SSL configuration parameters (ssl_cert, ssl_key, ssl_ca_cert, ssl_ciphers, etc.) were declared CFGCXT_INIT, meaning they were silently ignored when pool_get_config() was called under CFGCXT_RELOAD. Furthermore, SSL_ServerSide_init() was only called once at startup in main.c and never again, so the in-memory SSL_CTX was never refreshed. Fix --- 1. src/main/pgpool_main.c - Include utils/pool_ssl.h. - In reload_config(), call SSL_ServerSide_init() (guarded by #ifdef USE_SSL) *before* kill_all_children(SIGHUP). The function already replaces SSL_frontend_context atomically: it frees the old SSL_CTX only after a new one has been created successfully, so a failed reload leaves the existing context intact. 2. src/protocol/child.c - In check_config_reload(), call SSL_ServerSide_init() (guarded by #ifdef USE_SSL) so each worker child also refreshes its own copy of the SSL context for subsequent new connections. In-flight TLS sessions are unaffected because they hold a direct reference to the SSL object, not to SSL_frontend_context. 3. src/config/pool_config_variables.c - Change CFGCXT_INIT -> CFGCXT_RELOAD for: ssl_prefer_server_ciphers, ssl_cert, ssl_key, ssl_ca_cert, ssl_ca_cert_dir, ssl_crl_file, ssl_ciphers, ssl_ecdh_curve, ssl_dh_params_file, ssl_passphrase_command. - The 'ssl' boolean (master enable flag) is intentionally left as CFGCXT_INIT because dynamically enabling SSL at runtime is a larger, separate concern. Usage after this change ----------------------- Standard in-place certificate rotation (cert-manager, ACME, manual openssl refresh at the same path): # replace /etc/pgpool/server.{crt,key} with new files systemctl reload pgpool2 # or: pgpool -f /etc/pgpool/pgpool.conf reload New connections will use the new certificates after workers process the reload signal. Existing connections are not interrupted. Switching to a different certificate path also works: update pgpool.conf then reload — the new paths are now accepted in CFGCXT_RELOAD context. --- src/config/pool_config_variables.c | 20 ++++++++++---------- src/main/pgpool_main.c | 17 +++++++++++++++++ src/protocol/child.c | 11 +++++++++++ 3 files changed, 38 insertions(+), 10 deletions(-) diff --git a/src/config/pool_config_variables.c b/src/config/pool_config_variables.c index ce13c42f6..ca40c341a 100644 --- a/src/config/pool_config_variables.c +++ b/src/config/pool_config_variables.c @@ -705,7 +705,7 @@ static struct config_bool ConfigureNamesBool[] = }, { - {"ssl_prefer_server_ciphers", CFGCXT_INIT, SSL_CONFIG, + {"ssl_prefer_server_ciphers", CFGCXT_RELOAD, SSL_CONFIG, "Use server's SSL cipher preferences, rather than the client's", CONFIG_VAR_TYPE_BOOL, false, 0 }, @@ -1271,7 +1271,7 @@ static struct config_string ConfigureNamesString[] = }, { - {"ssl_cert", CFGCXT_INIT, SSL_CONFIG, + {"ssl_cert", CFGCXT_RELOAD, SSL_CONFIG, "SSL public certificate file.", CONFIG_VAR_TYPE_STRING, false, 0 }, @@ -1281,7 +1281,7 @@ static struct config_string ConfigureNamesString[] = }, { - {"ssl_key", CFGCXT_INIT, SSL_CONFIG, + {"ssl_key", CFGCXT_RELOAD, SSL_CONFIG, "SSL private key file.", CONFIG_VAR_TYPE_STRING, false, 0 }, @@ -1291,7 +1291,7 @@ static struct config_string ConfigureNamesString[] = }, { - {"ssl_ca_cert", CFGCXT_INIT, SSL_CONFIG, + {"ssl_ca_cert", CFGCXT_RELOAD, SSL_CONFIG, "Single PEM format file containing CA root certificate(s).", CONFIG_VAR_TYPE_STRING, false, 0 }, @@ -1301,7 +1301,7 @@ static struct config_string ConfigureNamesString[] = }, { - {"ssl_ca_cert_dir", CFGCXT_INIT, SSL_CONFIG, + {"ssl_ca_cert_dir", CFGCXT_RELOAD, SSL_CONFIG, "Directory containing CA root certificate(s).", CONFIG_VAR_TYPE_STRING, false, 0 }, @@ -1311,7 +1311,7 @@ static struct config_string ConfigureNamesString[] = }, { - {"ssl_crl_file", CFGCXT_INIT, SSL_CONFIG, + {"ssl_crl_file", CFGCXT_RELOAD, SSL_CONFIG, "SSL certificate revocation list file", CONFIG_VAR_TYPE_STRING, false, 0 }, @@ -1321,7 +1321,7 @@ static struct config_string ConfigureNamesString[] = }, { - {"ssl_ciphers", CFGCXT_INIT, SSL_CONFIG, + {"ssl_ciphers", CFGCXT_RELOAD, SSL_CONFIG, "Allowed SSL ciphers.", CONFIG_VAR_TYPE_STRING, false, 0 }, @@ -1331,7 +1331,7 @@ static struct config_string ConfigureNamesString[] = }, { - {"ssl_ecdh_curve", CFGCXT_INIT, SSL_CONFIG, + {"ssl_ecdh_curve", CFGCXT_RELOAD, SSL_CONFIG, "The curve to use in ECDH key exchange.", CONFIG_VAR_TYPE_STRING, false, 0 }, @@ -1341,7 +1341,7 @@ static struct config_string ConfigureNamesString[] = }, { - {"ssl_dh_params_file", CFGCXT_INIT, SSL_CONFIG, + {"ssl_dh_params_file", CFGCXT_RELOAD, SSL_CONFIG, "Path to the Diffie-Hellman parameters contained file", CONFIG_VAR_TYPE_STRING, false, 0 }, @@ -1351,7 +1351,7 @@ static struct config_string ConfigureNamesString[] = }, { - {"ssl_passphrase_command", CFGCXT_INIT, SSL_CONFIG, + {"ssl_passphrase_command", CFGCXT_RELOAD, SSL_CONFIG, "Path to the Diffie-Hellman parameters contained file", CONFIG_VAR_TYPE_STRING, false, 0 }, diff --git a/src/main/pgpool_main.c b/src/main/pgpool_main.c index bf7c452e2..0a9c92826 100644 --- a/src/main/pgpool_main.c +++ b/src/main/pgpool_main.c @@ -61,6 +61,7 @@ #include "watchdog/wd_lifecheck.h" #include "watchdog/watchdog.h" #include "pcp/pcp_worker.h" +#include "utils/pool_ssl.h" #include /* @@ -3489,6 +3490,22 @@ reload_config(void) if (pool_config->enable_pool_hba) load_hba(hba_file); +#ifdef USE_SSL + /* + * If SSL is enabled, re-initialize the SSL context so that new + * connections pick up rotated certificates without requiring a restart. + * SSL_ServerSide_init() is safe to call repeatedly: it frees and replaces + * the existing SSL_CTX only on success, leaving the old context intact on + * failure. + */ + if (pool_config->ssl) + { + ereport(LOG, + (errmsg("reload SSL certificates."))); + SSL_ServerSide_init(); + } +#endif /* USE_SSL */ + kill_all_children(SIGHUP); } diff --git a/src/protocol/child.c b/src/protocol/child.c index c34f05728..713bfe28f 100644 --- a/src/protocol/child.c +++ b/src/protocol/child.c @@ -1796,6 +1796,17 @@ check_config_reload(void) if (strcmp("", pool_config->pool_passwd)) pool_reopen_passwd_file(); +#ifdef USE_SSL + /* + * Re-initialize the frontend SSL context so this child process + * serves new connections with any rotated certificates without a + * restart. In-flight TLS sessions are unaffected; they hold a + * direct reference to the old SSL object. + */ + if (pool_config->ssl) + SSL_ServerSide_init(); +#endif /* USE_SSL */ + got_sighup = 0; } } -- 2.52.0