From 5735872edf3f6d3e12f09c4c61c3d8511c33ef82 Mon Sep 17 00:00:00 2001 From: Daniel Gustafsson Date: Mon, 8 Feb 2021 23:52:34 +0100 Subject: [PATCH v53 02/11] Refactor SSL testharness for multiple library The SSL testharness was fully tied to OpenSSL in the way the server was set up and reconfigured. This refactors the SSLServer module into a SSL library agnostic SSL/Server module which in turn use SSL/Backend/ modules for the implementation details. No changes are done to the actual tests, this only change how setup and teardown is performed. --- src/test/ssl/t/001_ssltests.pl | 57 +-------- src/test/ssl/t/002_scram.pl | 4 +- src/test/ssl/t/003_sslinfo.pl | 2 +- src/test/ssl/t/SSL/Backend/OpenSSL.pm | 110 ++++++++++++++++++ .../ssl/t/{SSLServer.pm => SSL/Server.pm} | 92 ++++++++++++--- 5 files changed, 196 insertions(+), 69 deletions(-) create mode 100644 src/test/ssl/t/SSL/Backend/OpenSSL.pm rename src/test/ssl/t/{SSLServer.pm => SSL/Server.pm} (77%) diff --git a/src/test/ssl/t/001_ssltests.pl b/src/test/ssl/t/001_ssltests.pl index b1fb15ce80..8d6b9dfbdc 100644 --- a/src/test/ssl/t/001_ssltests.pl +++ b/src/test/ssl/t/001_ssltests.pl @@ -8,12 +8,10 @@ use PostgreSQL::Test::Cluster; use PostgreSQL::Test::Utils; use Test::More; -use File::Copy; - use FindBin; use lib $FindBin::RealBin; -use SSLServer; +use SSL::Server; if ($ENV{with_ssl} ne 'openssl') { @@ -36,39 +34,6 @@ my $SERVERHOSTCIDR = '127.0.0.1/32'; # Allocation of base connection string shared among multiple tests. my $common_connstr; -# The client's private key must not be world-readable, so take a copy -# of the key stored in the code tree and update its permissions. -# -# This changes to using keys stored in a temporary path for the rest of -# the tests. To get the full path for inclusion in connection strings, the -# %key hash can be interrogated. -my $cert_tempdir = PostgreSQL::Test::Utils::tempdir(); -my %key; -my @keys = ( - "client.key", "client-revoked.key", - "client-der.key", "client-encrypted-pem.key", - "client-encrypted-der.key", "client-dn.key"); -foreach my $keyfile (@keys) -{ - copy("ssl/$keyfile", "$cert_tempdir/$keyfile") - or die - "couldn't copy ssl/$keyfile to $cert_tempdir/$keyfile for permissions change: $!"; - chmod 0600, "$cert_tempdir/$keyfile" - or die "failed to change permissions on $cert_tempdir/$keyfile: $!"; - $key{$keyfile} = PostgreSQL::Test::Utils::perl2host("$cert_tempdir/$keyfile"); - $key{$keyfile} =~ s!\\!/!g if $PostgreSQL::Test::Utils::windows_os; -} - -# Also make a copy of that explicitly world-readable. We can't -# necessarily rely on the file in the source tree having those -# permissions. -copy("ssl/client.key", "$cert_tempdir/client_wrongperms.key") - or die - "couldn't copy ssl/client_key to $cert_tempdir/client_wrongperms.key for permission change: $!"; -chmod 0644, "$cert_tempdir/client_wrongperms.key" - or die "failed to change permissions on $cert_tempdir/client_wrongperms.key: $!"; -$key{'client_wrongperms.key'} = PostgreSQL::Test::Utils::perl2host("$cert_tempdir/client_wrongperms.key"); -$key{'client_wrongperms.key'} =~ s!\\!/!g if $PostgreSQL::Test::Utils::windows_os; #### Set up the server. note "setting up data directory"; @@ -83,32 +48,22 @@ $node->start; # Run this before we lock down access below. my $result = $node->safe_psql('postgres', "SHOW ssl_library"); -is($result, 'OpenSSL', 'ssl_library parameter'); +is($result, SSL::Server::ssl_library(), 'ssl_library parameter'); configure_test_server_for_ssl($node, $SERVERHOSTADDR, $SERVERHOSTCIDR, 'trust'); note "testing password-protected keys"; -open my $sslconf, '>', $node->data_dir . "/sslconfig.conf"; -print $sslconf "ssl=on\n"; -print $sslconf "ssl_cert_file='server-cn-only.crt'\n"; -print $sslconf "ssl_key_file='server-password.key'\n"; -print $sslconf "ssl_passphrase_command='echo wrongpassword'\n"; -close $sslconf; - +set_server_cert($node, 'server-cn-only', 'root+client_ca', + 'server-password', 'echo wrongpassword'); command_fails( [ 'pg_ctl', '-D', $node->data_dir, '-l', $node->logfile, 'restart' ], 'restart fails with password-protected key file with wrong password'); $node->_update_pid(0); -open $sslconf, '>', $node->data_dir . "/sslconfig.conf"; -print $sslconf "ssl=on\n"; -print $sslconf "ssl_cert_file='server-cn-only.crt'\n"; -print $sslconf "ssl_key_file='server-password.key'\n"; -print $sslconf "ssl_passphrase_command='echo secret1'\n"; -close $sslconf; - +set_server_cert($node, 'server-cn-only', 'root+client_ca', + 'server-password', 'echo secret1'); command_ok( [ 'pg_ctl', '-D', $node->data_dir, '-l', $node->logfile, 'restart' ], 'restart succeeds with password-protected key file'); diff --git a/src/test/ssl/t/002_scram.pl b/src/test/ssl/t/002_scram.pl index 86312be88c..56c5f88d5f 100644 --- a/src/test/ssl/t/002_scram.pl +++ b/src/test/ssl/t/002_scram.pl @@ -14,11 +14,11 @@ use File::Copy; use FindBin; use lib $FindBin::RealBin; -use SSLServer; +use SSL::Server; if ($ENV{with_ssl} ne 'openssl') { - plan skip_all => 'OpenSSL not supported by this build'; + plan skip_all => 'SSL not supported by this build'; } # This is the hostname used to connect to the server. diff --git a/src/test/ssl/t/003_sslinfo.pl b/src/test/ssl/t/003_sslinfo.pl index 8c760b39db..6c733c8a75 100644 --- a/src/test/ssl/t/003_sslinfo.pl +++ b/src/test/ssl/t/003_sslinfo.pl @@ -12,7 +12,7 @@ use File::Copy; use FindBin; use lib $FindBin::RealBin; -use SSLServer; +use SSL::Server; if ($ENV{with_ssl} ne 'openssl') { diff --git a/src/test/ssl/t/SSL/Backend/OpenSSL.pm b/src/test/ssl/t/SSL/Backend/OpenSSL.pm new file mode 100644 index 0000000000..991953bf2c --- /dev/null +++ b/src/test/ssl/t/SSL/Backend/OpenSSL.pm @@ -0,0 +1,110 @@ +package SSL::Backend::OpenSSL; + +use strict; +use warnings; +use Exporter; +use File::Copy; + +our @ISA = qw(Exporter); +our @EXPORT_OK = qw(get_new_openssl_backend get_openssl_key); + +our (%key); + +INIT +{ +} + +sub new +{ + my ($class) = @_; + + my $self = { _library => 'OpenSSL' }; + + bless $self, $class; + + return $self; +} + +sub get_new_openssl_backend +{ + my $class = 'SSL::Backend::OpenSSL'; + + my $backend = $class->new(); + + return $backend; +} + +sub init +{ + # The client's private key must not be world-readable, so take a copy + # of the key stored in the code tree and update its permissions. + # + # This changes to using keys stored in a temporary path for the rest of + # the tests. To get the full path for inclusion in connection strings, the + # %key hash can be interrogated. + my $cert_tempdir = PostgreSQL::Test::Utils::tempdir(); + my @keys = ( + "client.key", "client-revoked.key", + "client-der.key", "client-encrypted-pem.key", + "client-encrypted-der.key", "client-dn.key"); + foreach my $keyfile (@keys) + { + copy("ssl/$keyfile", "$cert_tempdir/$keyfile") + or die + "couldn't copy ssl/$keyfile to $cert_tempdir/$keyfile for permissions change: $!"; + chmod 0600, "$cert_tempdir/$keyfile" + or die "failed to change permissions on $cert_tempdir/$keyfile: $!"; + $key{$keyfile} = PostgreSQL::Test::Utils::perl2host("$cert_tempdir/$keyfile"); + $key{$keyfile} =~ s!\\!/!g if $PostgreSQL::Test::Utils::windows_os; + } + + # Also make a copy of that explicitly world-readable. We can't + # necessarily rely on the file in the source tree having those + # permissions. + copy("ssl/client.key", "$cert_tempdir/client_wrongperms.key") + or die + "couldn't copy ssl/client_key to $cert_tempdir/client_wrongperms.key for permission change: $!"; + chmod 0644, "$cert_tempdir/client_wrongperms.key" + or die "failed to change permissions on $cert_tempdir/client_wrongperms.key: $!"; + $key{'client_wrongperms.key'} = PostgreSQL::Test::Utils::perl2host("$cert_tempdir/client_wrongperms.key"); + $key{'client_wrongperms.key'} =~ s!\\!/!g if $PostgreSQL::Test::Utils::windows_os; +} + +sub get_openssl_key +{ + my $self = $_[0]; + my $keyfile = $_[1]; + + return $key{$keyfile}; +} + +# Change the configuration to use given server cert file, and reload +# the server so that the configuration takes effect. +sub set_server_cert +{ + my $self = $_[0]; + my $certfile = $_[1]; + my $cafile = $_[2] || "root+client_ca"; + my $keyfile = $_[3] || $certfile; + + my $sslconf = + "ssl_ca_file='$cafile.crt'\n" + . "ssl_cert_file='$certfile.crt'\n" + . "ssl_key_file='$keyfile.key'\n" + . "ssl_crl_file='root+client.crl'\n"; + + return $sslconf; +} + +sub get_library +{ + my ($self) = @_; + + return $self->{_library}; +} + +sub cleanup +{ +} + +1; diff --git a/src/test/ssl/t/SSLServer.pm b/src/test/ssl/t/SSL/Server.pm similarity index 77% rename from src/test/ssl/t/SSLServer.pm rename to src/test/ssl/t/SSL/Server.pm index c85c6fd997..9275876250 100644 --- a/src/test/ssl/t/SSLServer.pm +++ b/src/test/ssl/t/SSL/Server.pm @@ -26,20 +26,41 @@ # explicitly because an invalid sslcert or sslrootcert, respectively, # causes those to be ignored.) -package SSLServer; +package SSL::Server; use strict; use warnings; use PostgreSQL::Test::Cluster; use PostgreSQL::Test::Utils; +use PostgreSQL::Test::RecursiveCopy; use File::Basename; use File::Copy; use Test::More; +use SSL::Backend::OpenSSL qw(get_new_openssl_backend get_openssl_key); +use SSL::Backend::NSS qw(get_new_nss_backend); + +our ($openssl, $nss, $backend); + +# The TLS backend which the server is using should be mostly transparent for +# the user, apart from individual configuration settings, so keep the backend +# specific things abstracted behind SSL::Server. +if ($ENV{with_ssl} eq 'openssl') +{ + $backend = get_new_openssl_backend(); + $openssl = 1; +} +elsif ($ENV{with_ssl} eq 'nss') +{ + $backend = get_new_nss_backend(); + $nss = 1; +} use Exporter 'import'; our @EXPORT = qw( configure_test_server_for_ssl + set_server_cert switch_server_cert + key ); # Copy a set of files, taking into account wildcards @@ -58,6 +79,14 @@ sub copy_files return; } +sub key +{ + my ($node, $keyfile) = @_; + + return get_openssl_key($keyfile) if (defined($openssl); + return get_nss_key($keyfile) if (defined($nss)); +} + # serverhost: what to put in listen_addresses, e.g. '127.0.0.1' # servercidr: what to put in pg_hba.conf, e.g. '127.0.0.1/32' sub configure_test_server_for_ssl @@ -126,14 +155,21 @@ sub configure_test_server_for_ssl close $sslconf; # Copy all server certificates and keys, and client root cert, to the data dir - copy_files("ssl/server-*.crt", $pgdata); - copy_files("ssl/server-*.key", $pgdata); - chmod(0600, glob "$pgdata/server-*.key") or die $!; - copy_files("ssl/root+client_ca.crt", $pgdata); - copy_files("ssl/root_ca.crt", $pgdata); - copy_files("ssl/root+client.crl", $pgdata); - mkdir("$pgdata/root+client-crldir"); - copy_files("ssl/root+client-crldir/*", "$pgdata/root+client-crldir/"); + if (defined($openssl)) + { + copy_files("ssl/server-*.crt", $pgdata); + copy_files("ssl/server-*.key", $pgdata); + chmod(0600, glob "$pgdata/server-*.key") or die $!; + copy_files("ssl/root+client_ca.crt", $pgdata); + copy_files("ssl/root_ca.crt", $pgdata); + copy_files("ssl/root+client.crl", $pgdata); + mkdir("$pgdata/root+client-crldir"); + copy_files("ssl/root+client-crldir/*", "$pgdata/root+client-crldir/"); + } + elsif (defined($nss)) + { + RecursiveCopy::copypath("ssl/nss", $pgdata . "/nss") if -e "ssl/nss"; + } # Stop and restart server to load new listen_addresses. $node->restart; @@ -141,20 +177,36 @@ sub configure_test_server_for_ssl # Change pg_hba after restart because hostssl requires ssl=on configure_hba_for_ssl($node, $servercidr, $authmethod); + # Finally, perform backend specific configuration + $backend->init(); + return; } -# Change the configuration to use given server cert file, and reload -# the server so that the configuration takes effect. -sub switch_server_cert +sub ssl_library +{ + return $backend->get_library(); +} + +sub cleanup +{ + $backend->cleanup(); +} + +# Change the configuration to use given server cert file, +sub set_server_cert { my $node = $_[0]; my $certfile = $_[1]; my $cafile = $_[2] || "root+client_ca"; + my $keyfile = $_[3] || ''; + my $pwcmd = $_[4] || ''; my $crlfile = "root+client.crl"; my $crldir; my $pgdata = $node->data_dir; + $keyfile = $certfile if $keyfile eq ''; + # defaults to use crl file if (defined $_[3] || defined $_[4]) { @@ -164,13 +216,23 @@ sub switch_server_cert open my $sslconf, '>', "$pgdata/sslconfig.conf"; print $sslconf "ssl=on\n"; - print $sslconf "ssl_ca_file='$cafile.crt'\n"; - print $sslconf "ssl_cert_file='$certfile.crt'\n"; - print $sslconf "ssl_key_file='$certfile.key'\n"; + print $sslconf $backend->set_server_cert($certfile, $cafile, $keyfile); print $sslconf "ssl_crl_file='$crlfile'\n" if defined $crlfile; print $sslconf "ssl_crl_dir='$crldir'\n" if defined $crldir; + print $sslconf "ssl_passphrase_command='$pwcmd'\n" + unless $pwcmd eq ''; close $sslconf; + return; +} +# Change the configuration to use given server cert file, and reload +# the server so that the configuration takes effect. +# Takes the same arguments as set_server_cert, which it calls to do that +# piece of the work. +sub switch_server_cert +{ + my $node = $_[0]; + set_server_cert(@_); $node->restart; return; } -- 2.24.3 (Apple Git-128)