Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SSL cache part 2 #9

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
228 changes: 228 additions & 0 deletions proxy_ssl_certificate_cache.t
Original file line number Diff line number Diff line change
@@ -0,0 +1,228 @@
#!/usr/bin/perl

# (C) Sergey Kandaurov
# (C) Nginx, Inc.

# Tests for proxy to ssl backend, proxy_ssl_certificate_cache directive.

###############################################################################

use warnings;
use strict;

use Test::More;

BEGIN { use FindBin; chdir($FindBin::Bin); }

use lib 'lib';
use Test::Nginx;

###############################################################################

select STDERR; $| = 1;
select STDOUT; $| = 1;

my $t = Test::Nginx->new()
->has(qw/http http_ssl proxy openssl:1.0.2/)
->has_daemon('openssl');

$t->write_file_expand('nginx.conf', <<'EOF');

%%TEST_GLOBALS%%

daemon off;

events {
}

http {
%%TEST_GLOBALS_HTTP%%

server {
listen 127.0.0.1:8080;
server_name localhost;

proxy_ssl_session_reuse off;
proxy_ssl_certificate $arg_cert.example.com.crt;
proxy_ssl_certificate_key $arg_cert.example.com.key;

proxy_ssl_certificate_cache max=4 valid=2s;

location / {
proxy_pass https://127.0.0.1:8081/;
}

location /enc {
proxy_pass https://127.0.0.1:8081/;
proxy_ssl_password_file password;
}

location /nocache {
proxy_pass https://127.0.0.1:8081/;
proxy_ssl_certificate_cache off;
}

location /inactive {
proxy_pass https://127.0.0.1:8081/;
proxy_ssl_certificate_cache max=4 inactive=1s;
}
}

server {
listen 127.0.0.1:8081 ssl;
server_name localhost;

ssl_certificate localhost.crt;
ssl_certificate_key localhost.key;

ssl_verify_client optional_no_ca;
ssl_trusted_certificate root.crt;

location / {
add_header X-Name $ssl_client_s_dn;
}
}
}

EOF

my $d = $t->testdir();

$t->write_file('openssl.conf', <<EOF);
[ req ]
default_bits = 2048
encrypt_key = no
distinguished_name = req_distinguished_name
x509_extensions = myca_extensions
[ req_distinguished_name ]
[ myca_extensions ]
basicConstraints = critical,CA:TRUE
EOF

$t->write_file('ca.conf', <<EOF);
[ ca ]
default_ca = myca

[ myca ]
new_certs_dir = $d
database = $d/certindex
default_md = sha256
policy = myca_policy
serial = $d/certserial
default_days = 1

[ myca_policy ]
commonName = supplied
EOF

foreach my $name ('root', 'localhost') {
system('openssl req -x509 -new '
. "-config $d/openssl.conf -subj /CN=$name/ "
. "-out $d/$name.crt -keyout $d/$name.key "
. ">>$d/openssl.out 2>&1") == 0
or die "Can't create certificate for $name: $!\n";
}

$t->write_file('certserial', '1000');
$t->write_file('certindex', '');

foreach my $name ('1.example.com', '2.example.com', '3.example.com',
'4.example.com', '5.example.com', 'dummy')
{
system('openssl req -new '
. "-config $d/openssl.conf -subj /CN=$name/ "
. "-out $d/$name.csr -keyout $d/$name.key "
. ">>$d/openssl.out 2>&1") == 0
or die "Can't create certificate for $name: $!\n";
system("openssl ca -batch -config $d/ca.conf "
. "-keyfile $d/root.key -cert $d/root.crt "
. "-subj /CN=$name/ -in $d/$name.csr -out $d/$name.crt "
. ">>$d/openssl.out 2>&1") == 0
or die "Can't sign certificate for $name: $!\n";
}

foreach my $name ('e.example.com') {
system("openssl genrsa -out $d/$name.key -passout pass:$name "
. "-aes128 2048 >>$d/openssl.out 2>&1") == 0
or die "Can't create private key: $!\n";
system('openssl req -new '
. "-config $d/openssl.conf -subj /CN=$name/ "
. "-out $d/$name.csr "
. "-key $d/$name.key -passin pass:$name"
. ">>$d/openssl.out 2>&1") == 0
or die "Can't create certificate for $name: $!\n";
system("openssl ca -batch -config $d/ca.conf "
. "-keyfile $d/root.key -cert $d/root.crt "
. "-subj /CN=$name/ -in $d/$name.csr -out $d/$name.crt "
. ">>$d/openssl.out 2>&1") == 0
or die "Can't sign certificate for $name: $!\n";
}

$t->write_file('password', 'e.example.com');
$t->write_file('index.html', '');

$t->try_run('no proxy_ssl_certificate_cache')->plan(16);

###############################################################################

like(http_get('/?cert=1'), qr/CN=1.example.com/, 'certificate 1');
update($t, '1.example.com');

like(http_get('/?cert=1'), qr/CN=1.example.com/, 'certificate 1 cached');

like(http_get('/?cert=2'), qr/CN=2.example.com/, 'certificate 2');
like(http_get('/?cert=3'), qr/CN=3.example.com/, 'certificate 3');

like(http_get('/?cert=1'), qr/500 Internal/, 'certificate 1 evicted');

update($t, '2.example.com', 'dummy');
update($t, '3.example.com');

like(http_get('/?cert=2'), qr/CN=2.example.com/, 'certificate 2 cached');
like(http_get('/?cert=3'), qr/CN=3.example.com/, 'certificate 3 cached');

like(http_get('/enc/?cert=e'), qr/CN=e.example.com/, 'encrypted');

# encrypted certificate keys should not spill to the parent cache context
like(http_get('/?cert=e'), qr/500 Internal/, 'encrypted no password');

update($t, 'e.example.com', 'dummy');

like(http_get('/enc/?cert=e'), qr/500 Internal/, 'encrypted not cached');

like(http_get('/nocache/?cert=4'), qr/CN=4.example.com/, 'no cache');
update($t, '4.example.com', 'dummy');

like(http_get('/nocache/?cert=4'), qr/CN=dummy/, 'no cache updated');

like(http_get('/inactive/?cert=5'), qr/CN=5.example.com/, 'inactive');

select undef, undef, undef, 3.1;

like(http_get('/?cert=2'), qr/CN=dummy/, 'certificate 2 expired');
like(http_get('/?cert=3'), qr/500 Internal/, 'certificate 3 expired');

# eviction after inactive time

update($t, '5.example.com', 'dummy');

like(http_get('/inactive/?cert=5'), qr/CN=dummy/, 'inactive expired');

###############################################################################

sub get {
http_get('/?cert=' . shift);
}

sub update {
my ($t, $old, $new) = @_;

for my $ext ("crt", "key") {
unlink "$d/$old.$ext";
next if !defined $new;

$t->write_file("$old.$ext", $t->read_file("$new.$ext"));
}
}

###############################################################################
138 changes: 138 additions & 0 deletions ssl_cache_reload.t
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
#!/usr/bin/perl

# (C) Sergey Kandaurov
# (C) Nginx, Inc.

# Tests for SSL object cache inheritance on configuration reload.

###############################################################################

use warnings;
use strict;

use Test::More;

BEGIN { use FindBin; chdir($FindBin::Bin); }

use lib 'lib';
use Test::Nginx;

###############################################################################

select STDERR; $| = 1;
select STDOUT; $| = 1;

my $t = Test::Nginx->new()->has(qw/http http_ssl socket_ssl/)
->has_daemon('openssl');

$t->write_file_expand('nginx.conf', << 'EOF');

%%TEST_GLOBALS%%

daemon off;

ssl_object_cache_inherit on;

events {
}

http {
%%TEST_GLOBALS_HTTP%%

server {
listen 127.0.0.1:8443 ssl;
server_name localhost;

ssl_certificate 1.example.com.crt;
ssl_certificate_key 1.example.com.key;
}

server {
listen 127.0.0.1:8444 ssl;
server_name localhost;

ssl_certificate 2.example.com.crt;
ssl_certificate_key 2.example.com.key;
}
}

EOF

my $d = $t->testdir();

$t->write_file('openssl.conf', <<EOF);
[ req ]
default_bits = 2048
encrypt_key = no
distinguished_name = req_distinguished_name
[ req_distinguished_name ]
EOF

foreach my $name ('1.example.com', '2.example.com', '3.example.com') {
system('openssl req -x509 -new '
. "-config $d/openssl.conf -subj /CN=$name/ "
. "-out $d/$name.crt -keyout $d/$name.key "
. ">>$d/openssl.out 2>&1") == 0
or die "Can't create certificate for $name: $!\n";
}

$t->try_run('no ssl_object_cache_inherit')->plan(5);

###############################################################################

# make sure SSL certificates are properly cached on configuration reload by:
#
# - updating backing storage
# - keeping inode and mtime metadata

like(get_cert_cn(8443), qr!/CN=1.example.com!, 'certificate 1');
like(get_cert_cn(8444), qr!/CN=2.example.com!, 'certificate 2');

update($t, "1.example.com", "3.example.com");
update($t, "2.example.com", "3.example.com", update_metadata => 1);

ok(reload($t), 'reload');

like(get_cert_cn(8443), qr!/CN=1.example.com!, 'certificate cached');
like(get_cert_cn(8444), qr!/CN=3.example.com!, 'certificate updated');

###############################################################################

sub get_cert_cn {
my ($port) = @_;
my $s = http('',
start => 1,
PeerAddr => '127.0.0.1:' . port($port),
SSL => 1);
return $s->dump_peer_certificate();
}

sub update {
my ($t, $old, $new, %extra) = @_;

for my $ext ("crt", "key") {
if ($extra{update_metadata}) {
$t->write_file("$old.$ext.tmp",
$t->read_file("$new.$ext"));
rename("$d/$old.$ext.tmp", "$d/$old.$ext");

} else {
my $mtime = -e "$d/$old.$ext" && (stat(_))[9];
$t->write_file("$old.$ext", $t->read_file("$new.$ext"));
utime(time(), $mtime, "$d/$old.$ext");
}
}
}

sub reload {
my ($t) = @_;

$t->reload();

for (1 .. 10) {
return 1 if $t->read_file('error.log') =~ /exited with code/;
select undef, undef, undef, 0.2;
}
}

###############################################################################
Loading