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:
@@ -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>.
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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',
|
||||
|
||||
Reference in New Issue
Block a user