diff options
Diffstat (limited to 'src/test/ssl/t/SSLServer.pm')
-rw-r--r-- | src/test/ssl/t/SSLServer.pm | 205 |
1 files changed, 205 insertions, 0 deletions
diff --git a/src/test/ssl/t/SSLServer.pm b/src/test/ssl/t/SSLServer.pm new file mode 100644 index 0000000..804d008 --- /dev/null +++ b/src/test/ssl/t/SSLServer.pm @@ -0,0 +1,205 @@ + +# Copyright (c) 2021, PostgreSQL Global Development Group + +# This module sets up a test server, for the SSL regression tests. +# +# The server is configured as follows: +# +# - SSL enabled, with the server certificate specified by argument to +# switch_server_cert function. +# - ssl/root+client_ca.crt as the CA root for validating client certs. +# - reject non-SSL connections +# - a database called trustdb that lets anyone in +# - another database called certdb that uses certificate authentication, ie. +# the client must present a valid certificate signed by the client CA +# +# The server is configured to only accept connections from localhost. If you +# want to run the client from another host, you'll have to configure that +# manually. +# +# Note: Someone running these test could have key or certificate files +# in their ~/.postgresql/, which would interfere with the tests. The +# way to override that is to specify sslcert=invalid and/or +# sslrootcert=invalid if no actual certificate is used for a +# particular test. libpq will ignore specifications that name +# nonexisting files. (sslkey and sslcrl do not need to specified +# explicitly because an invalid sslcert or sslrootcert, respectively, +# causes those to be ignored.) + +package SSLServer; + +use strict; +use warnings; +use PostgresNode; +use TestLib; +use File::Basename; +use File::Copy; +use Test::More; + +use Exporter 'import'; +our @EXPORT = qw( + configure_test_server_for_ssl + switch_server_cert +); + +# Copy a set of files, taking into account wildcards +sub copy_files +{ + my $orig = shift; + my $dest = shift; + + my @orig_files = glob $orig; + foreach my $orig_file (@orig_files) + { + my $base_file = basename($orig_file); + copy($orig_file, "$dest/$base_file") + or die "Could not copy $orig_file to $dest"; + } + return; +} + +# 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 +{ + my ($node, $serverhost, $servercidr, $authmethod, $password, + $password_enc) = @_; + + my $pgdata = $node->data_dir; + + # Create test users and databases + $node->psql('postgres', "CREATE USER ssltestuser"); + $node->psql('postgres', "CREATE USER md5testuser"); + $node->psql('postgres', "CREATE USER anotheruser"); + $node->psql('postgres', "CREATE USER yetanotheruser"); + $node->psql('postgres', "CREATE DATABASE trustdb"); + $node->psql('postgres', "CREATE DATABASE certdb"); + $node->psql('postgres', "CREATE DATABASE certdb_dn"); + $node->psql('postgres', "CREATE DATABASE certdb_dn_re"); + $node->psql('postgres', "CREATE DATABASE certdb_cn"); + $node->psql('postgres', "CREATE DATABASE verifydb"); + + # Update password of each user as needed. + if (defined($password)) + { + $node->psql('postgres', + "SET password_encryption='$password_enc'; ALTER USER ssltestuser PASSWORD '$password';" + ); + # A special user that always has an md5-encrypted password + $node->psql('postgres', + "SET password_encryption='md5'; ALTER USER md5testuser PASSWORD '$password';" + ); + $node->psql('postgres', + "SET password_encryption='$password_enc'; ALTER USER anotheruser PASSWORD '$password';" + ); + } + + # enable logging etc. + open my $conf, '>>', "$pgdata/postgresql.conf"; + print $conf "fsync=off\n"; + print $conf "log_connections=on\n"; + print $conf "log_hostname=on\n"; + print $conf "listen_addresses='$serverhost'\n"; + print $conf "log_statement=all\n"; + + # enable SSL and set up server key + print $conf "include 'sslconfig.conf'\n"; + + close $conf; + + # ssl configuration will be placed here + open my $sslconf, '>', "$pgdata/sslconfig.conf"; + 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/"); + + # Stop and restart server to load new listen_addresses. + $node->restart; + + # Change pg_hba after restart because hostssl requires ssl=on + configure_hba_for_ssl($node, $servercidr, $authmethod); + + return; +} + +# Change the configuration to use given server cert file, and reload +# the server so that the configuration takes effect. +sub switch_server_cert +{ + my $node = $_[0]; + my $certfile = $_[1]; + my $cafile = $_[2] || "root+client_ca"; + my $crlfile = "root+client.crl"; + my $crldir; + my $pgdata = $node->data_dir; + + # defaults to use crl file + if (defined $_[3] || defined $_[4]) + { + $crlfile = $_[3]; + $crldir = $_[4]; + } + + 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 "ssl_crl_file='$crlfile'\n" if defined $crlfile; + print $sslconf "ssl_crl_dir='$crldir'\n" if defined $crldir; + close $sslconf; + + $node->restart; + return; +} + +sub configure_hba_for_ssl +{ + my ($node, $servercidr, $authmethod) = @_; + my $pgdata = $node->data_dir; + + # Only accept SSL connections from $servercidr. Our tests don't depend on this + # but seems best to keep it as narrow as possible for security reasons. + # + # When connecting to certdb, also check the client certificate. + open my $hba, '>', "$pgdata/pg_hba.conf"; + print $hba + "# TYPE DATABASE USER ADDRESS METHOD OPTIONS\n"; + print $hba + "hostssl trustdb md5testuser $servercidr md5\n"; + print $hba + "hostssl trustdb all $servercidr $authmethod\n"; + print $hba + "hostssl verifydb ssltestuser $servercidr $authmethod clientcert=verify-full\n"; + print $hba + "hostssl verifydb anotheruser $servercidr $authmethod clientcert=verify-full\n"; + print $hba + "hostssl verifydb yetanotheruser $servercidr $authmethod clientcert=verify-ca\n"; + print $hba + "hostssl certdb all $servercidr cert\n"; + print $hba + "hostssl certdb_dn all $servercidr cert clientname=DN map=dn\n", + "hostssl certdb_dn_re all $servercidr cert clientname=DN map=dnre\n", + "hostssl certdb_cn all $servercidr cert clientname=CN map=cn\n"; + close $hba; + + # Also set the ident maps. Note: fields with commas must be quoted + open my $map, ">", "$pgdata/pg_ident.conf"; + print $map + "# MAPNAME SYSTEM-USERNAME PG-USERNAME\n", + "dn \"CN=ssltestuser-dn,OU=Testing,OU=Engineering,O=PGDG\" ssltestuser\n", + "dnre \"/^.*OU=Testing,.*\$\" ssltestuser\n", + "cn ssltestuser-dn ssltestuser\n"; + + return; +} + +1; |