ssl: Add tests for client CA

These tests were originally written to test the SSL SNI patchset
but they have merit on their own since we lack coverage for these
scenarios in the non SNI case as well.

Author: Jacob Champion <jacob.champion@enterprisedb.com>
Co-authored-by: Daniel Gustafsson <daniel@yesql.se>
Discussion: https://postgr.es/m/1C81CD0D-407E-44F9-833A-DD0331C202E5@yesql.se
This commit is contained in:
Daniel Gustafsson 2026-03-18 12:36:53 +01:00
parent e82fc27e09
commit 25e568ba7c
2 changed files with 69 additions and 3 deletions

View file

@ -1004,4 +1004,60 @@ $node->connect_fails(
qr{Failed certificate data \(unverified\): subject "/CN=\\xce\\x9f\\xce\\xb4\\xcf\\x85\\xcf\\x83\\xcf\\x83\\xce\\xad\\xce\\xb1\\xcf\\x82", serial number \d+, issuer "/CN=Test CA for PostgreSQL SSL regression test client certs"},
]);
SKIP:
{
skip "sslmode require not supported in this build", 4
unless ($supports_sslcertmode_require);
# Test client CAs
my $connstr =
"user=ssltestuser dbname=certdb hostaddr=$SERVERHOSTADDR sslmode=require sslsni=1";
switch_server_cert($node, certfile => 'server-cn-only', cafile => '');
# example.org is unconfigured and should fail.
$node->connect_fails(
"$connstr host=example.org sslcertmode=require sslcert=ssl/client.crt"
. sslkey('client.key'),
"host: 'example.org', ca: '': connect with sslcert, no client CA configured",
expected_stderr =>
qr/client certificates can only be checked if a root certificate store is available/
);
# example.com uses the client CA.
switch_server_cert(
$node,
certfile => 'server-cn-only',
cafile => 'root+client_ca');
# example.com is configured and should require a valid client cert.
$node->connect_fails(
"$connstr host=example.com sslcertmode=disable",
"host: 'example.com', ca: 'root+client_ca.crt': connect fails if no client certificate sent",
expected_stderr => qr/connection requires a valid client certificate/
);
$node->connect_ok(
"$connstr host=example.com sslcertmode=require sslcert=ssl/client.crt "
. sslkey('client.key'),
"host: 'example.com', ca: 'root+client_ca.crt': connect with sslcert, client certificate sent"
);
# example.net uses the server CA (which is wrong).
switch_server_cert(
$node,
certfile => 'server-cn-only',
cafile => 'root+server_ca');
# example.net is configured and should require a client cert, but will
# always fail verification.
$node->connect_fails(
"$connstr host=example.net sslcertmode=disable",
"host: 'example.net', ca: 'root+server_ca.crt': connect fails if no client certificate sent",
expected_stderr => qr/connection requires a valid client certificate/
);
$node->connect_fails(
"$connstr host=example.net sslcertmode=require sslcert=ssl/client.crt "
. sslkey('client.key'),
"host: 'example.net', ca: 'root+server_ca.crt': connect with sslcert, client certificate sent",
expected_stderr => qr/unknown ca/);
}
done_testing();

View file

@ -72,6 +72,7 @@ sub init
chmod(0600, glob "$pgdata/server-*.key")
or die "failed to change permissions on server keys: $!";
_copy_files("ssl/root+client_ca.crt", $pgdata);
_copy_files("ssl/root+server_ca.crt", $pgdata);
_copy_files("ssl/root_ca.crt", $pgdata);
_copy_files("ssl/root+client.crl", $pgdata);
mkdir("$pgdata/root+client-crldir")
@ -146,7 +147,8 @@ following parameters are supported:
=item cafile => B<value>
The CA certificate file to use for the C<ssl_ca_file> GUC. If omitted it will
default to 'root+client_ca.crt'.
default to 'root+client_ca.crt'. If empty, no C<ssl_ca_file> configuration
parameter will be set.
=item certfile => B<value>
@ -181,10 +183,18 @@ sub set_server_cert
unless defined $params->{keyfile};
my $sslconf =
"ssl_ca_file='$params->{cafile}.crt'\n"
. "ssl_cert_file='$params->{certfile}.crt'\n"
"ssl_cert_file='$params->{certfile}.crt'\n"
. "ssl_key_file='$params->{keyfile}.key'\n"
. "ssl_crl_file='$params->{crlfile}'\n";
if ($params->{cafile} ne "")
{
$sslconf .= "ssl_ca_file='$params->{cafile}.crt'\n";
}
else
{
$sslconf .= "ssl_ca_file=''\n";
}
$sslconf .= "ssl_crl_dir='$params->{crldir}'\n"
if defined $params->{crldir};