1
0
mirror of https://github.com/postgres/postgres.git synced 2026-10-29 15:58:33 +03:00

ssl: Don't disable SSL for all hosts when one SNI host fails to load

On Windows and in EXEC_BACKEND builds every backend reloads the SSL
configuration, with no previous one to fall back on.  If a single
pg_hosts.conf entry failed to load there, the whole configuration was
discarded and the backend ran without SSL for every host.  Fix by
keeping the failed entry without an SSL context in child processes
and failing the handshake only for connections selecting it.

Also document that SSL_passphrase_cmd_reload must be enabled on
Windows, like commit 0f4f45772c did for the GUC.

Author: Zsolt Parragi <zsolt.parragi@percona.com>
Reviewed-by: Daniel Gustafsson <daniel@yesql.se>
Discussion: https://postgr.es/m/CAN4CZFM-Hn7-bdKC8=xwCQiE128Y1Xao+aft=Grq5QA9OkZ9Kw@mail.gmail.com
Backpatch-through: 19
This commit is contained in:
Daniel Gustafsson
2026-10-26 21:33:22 +01:00
parent cf765d6e14
commit 07be6f384c
3 changed files with 65 additions and 1 deletions

View File

@@ -2649,6 +2649,16 @@ openssl x509 -req -in server.csr -text -days 365 \
<literal>off</literal>.
</para>
<para>
On <systemitem class="osname">Windows</systemitem>, all connections will
perform a configuration reload due to the different process model of that
platform, so <replaceable>SSL_passphrase_cmd_reload</replaceable> must be
set to <literal>on</literal> for every entry which has an
<replaceable>SSL_passphrase_cmd</replaceable>. If an entry fails to load
at backend start, SSL connections for the hosts of that entry will fail,
while the other entries remain usable.
</para>
<para>
<replaceable>hostname</replaceable> can be either the literal
hostname for the connection, <literal>/no_sni/</literal>, or <literal>*</literal>.

View File

@@ -167,6 +167,7 @@ be_tls_init(bool isServerStart)
int ssl_ver_max = -1;
host_cache_hash *host_cache = NULL;
bool hasWarned = false;
int nloaded = 0;
/*
* Since we don't know which host we're using until the ClientHello is
@@ -331,10 +332,20 @@ be_tls_init(bool isServerStart)
* Create the SSL context only once the entry has been accepted
* and added to new_hosts, as the cleanup callback can only free
* contexts of entries it can reach from there.
*
* An EXEC_BACKEND child has no previous configuration to fall
* back on, so rather than leaving the backend without SSL for all
* hosts we keep a failed entry without a context. Connections
* selecting it will fail the handshake.
*/
if (!init_host_context(host, isServerStart, &hasWarned))
if (init_host_context(host, isServerStart, &hasWarned))
nloaded++;
else if (!IsUnderPostmaster)
goto error;
}
if (nloaded == 0)
goto error;
}
/*
@@ -2098,6 +2109,17 @@ sni_clienthello_cb(SSL *ssl, int *al, void *arg)
}
found:
if (install_config->ssl_ctx == NULL)
{
*al = SSL_AD_INTERNAL_ERROR;
ereport(COMMERROR,
errcode(ERRCODE_CONFIG_FILE_ERROR),
errmsg("SSL configuration for host could not be loaded, terminating connection"),
errcontext("line %d of configuration file \"%s\"",
install_config->linenumber, install_config->sourcefile));
return SSL_CLIENT_HELLO_ERROR;
}
if (!ssl_update_ssl(ssl, install_config))
{
*al = SSL_AD_INTERNAL_ERROR;

View File

@@ -504,6 +504,38 @@ SKIP:
'log reload failure due to passphrase command reloading');
}
# A host whose key cannot be loaded in an EXEC_BACKEND child must only affect
# connections to that host.
ok(unlink($node->data_dir . '/pg_hosts.conf'));
$node->append_conf('pg_hosts.conf',
'localhost server-cn-only.crt server-cn-only.key');
$node->append_conf('pg_hosts.conf',
'example.org server-cn-only.crt server-password.key root+client_ca.crt "echo secret1" off'
);
$node->restart;
$node_loglocation = -s $node->logfile;
$node->connect_ok(
"$connstr sslrootcert=ssl/root+server_ca.crt sslmode=require host=localhost",
"pg_hosts.conf: connect to host next to a passphrase host without reload"
);
if ($windows_os || $exec_backend =~ /on/)
{
$node->connect_fails(
"$connstr sslrootcert=ssl/root+server_ca.crt sslmode=require host=example.org",
"pg_hosts.conf: connect to passphrase host without reload fails",
expected_stderr => qr/internal error/);
$node->wait_for_log(qr/SSL configuration for host could not be loaded/,
$node_loglocation);
}
else
{
$node->connect_ok(
"$connstr sslrootcert=ssl/root+server_ca.crt sslmode=require host=example.org",
"pg_hosts.conf: connect to passphrase host without reload");
}
# Configure with only non-SNI connections allowed
ok(unlink($node->data_dir . '/pg_hosts.conf'));
$node->append_conf('pg_hosts.conf',