1# This module sets up a test server, for the SSL regression tests. 2# 3# The server is configured as follows: 4# 5# - SSL enabled, with the server certificate specified by argument to 6# switch_server_cert function. 7# - ssl/root+client_ca.crt as the CA root for validating client certs. 8# - reject non-SSL connections 9# - a database called trustdb that lets anyone in 10# - another database called certdb that uses certificate authentication, ie. 11# the client must present a valid certificate signed by the client CA 12# - two users, called ssltestuser and anotheruser. 13# 14# The server is configured to only accept connections from localhost. If you 15# want to run the client from another host, you'll have to configure that 16# manually. 17package SSLServer; 18 19use strict; 20use warnings; 21use PostgresNode; 22use TestLib; 23use File::Basename; 24use File::Copy; 25use Test::More; 26 27use Exporter 'import'; 28our @EXPORT = qw( 29 configure_test_server_for_ssl 30 switch_server_cert 31 test_connect_fails 32 test_connect_ok 33); 34 35# Define a couple of helper functions to test connecting to the server. 36 37# The first argument is a base connection string to use for connection. 38# The second argument is a complementary connection string. 39sub test_connect_ok 40{ 41 my ($common_connstr, $connstr, $test_name) = @_; 42 43 my $cmd = [ 44 'psql', '-X', '-A', '-t', '-c', 45 "SELECT \$\$connected with $connstr\$\$", 46 '-d', "$common_connstr $connstr" 47 ]; 48 49 command_ok($cmd, $test_name); 50 return; 51} 52 53sub test_connect_fails 54{ 55 my ($common_connstr, $connstr, $expected_stderr, $test_name) = @_; 56 57 my $cmd = [ 58 'psql', '-X', '-A', '-t', '-c', 59 "SELECT \$\$connected with $connstr\$\$", 60 '-d', "$common_connstr $connstr" 61 ]; 62 63 command_fails_like($cmd, $expected_stderr, $test_name); 64 return; 65} 66 67# Copy a set of files, taking into account wildcards 68sub copy_files 69{ 70 my $orig = shift; 71 my $dest = shift; 72 73 my @orig_files = glob $orig; 74 foreach my $orig_file (@orig_files) 75 { 76 my $base_file = basename($orig_file); 77 copy($orig_file, "$dest/$base_file") 78 or die "Could not copy $orig_file to $dest"; 79 } 80 return; 81} 82 83sub configure_test_server_for_ssl 84{ 85 my ($node, $serverhost, $authmethod, $password, $password_enc) = @_; 86 87 my $pgdata = $node->data_dir; 88 89 # Create test users and databases 90 $node->psql('postgres', "CREATE USER ssltestuser"); 91 $node->psql('postgres', "CREATE USER anotheruser"); 92 $node->psql('postgres', "CREATE DATABASE trustdb"); 93 $node->psql('postgres', "CREATE DATABASE certdb"); 94 95 # Update password of each user as needed. 96 if (defined($password)) 97 { 98 $node->psql('postgres', 99 "SET password_encryption='$password_enc'; ALTER USER ssltestuser PASSWORD '$password';" 100 ); 101 $node->psql('postgres', 102 "SET password_encryption='$password_enc'; ALTER USER anotheruser PASSWORD '$password';" 103 ); 104 } 105 106 # enable logging etc. 107 open my $conf, '>>', "$pgdata/postgresql.conf"; 108 print $conf "fsync=off\n"; 109 print $conf "log_connections=on\n"; 110 print $conf "log_hostname=on\n"; 111 print $conf "listen_addresses='$serverhost'\n"; 112 print $conf "log_statement=all\n"; 113 114 # enable SSL and set up server key 115 print $conf "include 'sslconfig.conf'"; 116 117 close $conf; 118 119 # ssl configuration will be placed here 120 open my $sslconf, '>', "$pgdata/sslconfig.conf"; 121 close $sslconf; 122 123 # Copy all server certificates and keys, and client root cert, to the data dir 124 copy_files("ssl/server-*.crt", $pgdata); 125 copy_files("ssl/server-*.key", $pgdata); 126 chmod(0600, glob "$pgdata/server-*.key") or die $!; 127 copy_files("ssl/root+client_ca.crt", $pgdata); 128 copy_files("ssl/root_ca.crt", $pgdata); 129 copy_files("ssl/root+client.crl", $pgdata); 130 131 # Stop and restart server to load new listen_addresses. 132 $node->restart; 133 134 # Change pg_hba after restart because hostssl requires ssl=on 135 configure_hba_for_ssl($node, $serverhost, $authmethod); 136 137 return; 138} 139 140# Change the configuration to use given server cert file, and reload 141# the server so that the configuration takes effect. 142sub switch_server_cert 143{ 144 my $node = $_[0]; 145 my $certfile = $_[1]; 146 my $cafile = $_[2] || "root+client_ca"; 147 my $pgdata = $node->data_dir; 148 149 open my $sslconf, '>', "$pgdata/sslconfig.conf"; 150 print $sslconf "ssl=on\n"; 151 print $sslconf "ssl_ca_file='$cafile.crt'\n"; 152 print $sslconf "ssl_cert_file='$certfile.crt'\n"; 153 print $sslconf "ssl_key_file='$certfile.key'\n"; 154 print $sslconf "ssl_crl_file='root+client.crl'\n"; 155 close $sslconf; 156 157 $node->restart; 158 return; 159} 160 161sub configure_hba_for_ssl 162{ 163 my ($node, $serverhost, $authmethod) = @_; 164 my $pgdata = $node->data_dir; 165 166 # Only accept SSL connections from localhost. Our tests don't depend on this 167 # but seems best to keep it as narrow as possible for security reasons. 168 # 169 # When connecting to certdb, also check the client certificate. 170 open my $hba, '>', "$pgdata/pg_hba.conf"; 171 print $hba 172 "# TYPE DATABASE USER ADDRESS METHOD\n"; 173 print $hba 174 "hostssl trustdb all $serverhost/32 $authmethod\n"; 175 print $hba 176 "hostssl trustdb all ::1/128 $authmethod\n"; 177 print $hba 178 "hostssl certdb all $serverhost/32 cert\n"; 179 print $hba 180 "hostssl certdb all ::1/128 cert\n"; 181 close $hba; 182 return; 183} 184 1851; 186