Skip to content

Commit

Permalink
Fix SNI tests for LibreSSL
Browse files Browse the repository at this point in the history
Since beginning of 2022, LibreSSL dropped support for IP addresses in
SNI. This is aligned to RFC 6066, section 3:

> Literal IPv4 and IPv6 addresses are not permitted in "HostName".

In order to deal with it, clients connect to "localhost" instead of
"127.0.0.1" and adjust "server.crt" to use "localhost" as Common Name
instead of "127.0.0.1".

rollback localhost => 127.0.0.1 change for servers
  • Loading branch information
lgv5 committed May 11, 2023
1 parent e87091a commit 36e1551
Show file tree
Hide file tree
Showing 5 changed files with 60 additions and 52 deletions.
14 changes: 7 additions & 7 deletions t/mojo/certs/server.crt
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
-----BEGIN CERTIFICATE-----
MIIBsjCCARsCCQCptEBZlSnk3jANBgkqhkiG9w0BAQUFADAaMQswCQYDVQQGEwJV
UzELMAkGA1UEAxMCY2EwHhcNMTQxMjEyMDUwMzI1WhcNMzQxMjA3MDUwMzI1WjAh
MQswCQYDVQQGEwJVUzESMBAGA1UEAxMJMTI3LjAuMC4xMIGfMA0GCSqGSIb3DQEB
MIIBsjCCARsCCQCM8WLoRPCPATANBgkqhkiG9w0BAQsFADAaMQswCQYDVQQGEwJV
UzELMAkGA1UEAxMCY2EwHhcNMjMwMjI1MTc1NjAwWhcNNDMwMjIwMTc1NjAwWjAh
MQswCQYDVQQGEwJVUzESMBAGA1UEAwwJbG9jYWxob3N0MIGfMA0GCSqGSIb3DQEB
AQUAA4GNADCBiQKBgQDDhbj7nsfzahPilwn6pGdo6nKYCR21WZ73CuwPN86DmsZi
5LIRYRfKA0unape2BQBnMnSmInaXvHHBdVsTyt3XSFZj5+iCF9RcorXAqcDygScj
8MTWYAZxCu3lGAjtw0bGGYutlLg5jtEXvZwfe61XfJj9xDUPNQrP7mf/HTBmgQID
AQABMA0GCSqGSIb3DQEBBQUAA4GBACRIx9fB4x8UO44C9TGj3bKb1NX3bkuHMz0m
WdhCkzUUiANtRMxp2oLA3KHY4yOusZLZIUNyP10Ri5q/U1mR0poYCMm7AYee2OV7
NdQIyppeDLoWQ9uPISPjp1d+zjpGOrLrSkpD1rYLVw4R56A9ZQks/LNs6TSceZjZ
c5QST/9i
AQABMA0GCSqGSIb3DQEBCwUAA4GBABjuNiXMWmGIr4LU7hypd4QKFZDfHyFFw21h
dRhFp4cBq+A/9cDW7CBmKuVvBwYtkLSzQf0Y2/55mx1hz85NjRiSdDENWLncW8sA
qt0mS9eX6s9HMeYNcT9ngPoAAUmkGT3/tAXwmejvu2XKBt8UBcnpJdt40YYq1wIH
9lcg5Hni
-----END CERTIFICATE-----
6 changes: 3 additions & 3 deletions t/mojo/daemon_ipv6_tls.t
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,8 @@ subtest 'IPv6, TLS, SNI and a proxy' => sub {
$daemon = Mojo::Server::Daemon->new(app => app, silent => 1);
my $listen
= 'https://[::1]'
. '?127.0.0.1_cert=t/mojo/certs/server.crt'
. '&127.0.0.1_key=t/mojo/certs/server.key'
. '?localhost_cert=t/mojo/certs/server.crt'
. '&localhost_key=t/mojo/certs/server.key'
. '&example.com_cert=t/mojo/certs/domain.crt'
. '&example.com_key=t/mojo/certs/domain.key';
my $forward = $daemon->listen([$listen])->start->ports->[0];
Expand All @@ -54,7 +54,7 @@ subtest 'IPv6, TLS, SNI and a proxy' => sub {
is $tx->res->code, 200, 'right status';
is $tx->res->body, 'works!', 'right content';
ok !$tx->error, 'no error';
$tx = $ua->get("https://127.0.0.1/");
$tx = $ua->get("https://localhost/");
is $tx->res->code, 200, 'right status';
is $tx->res->body, 'works!', 'right content';
ok !$tx->error, 'no error';
Expand Down
30 changes: 19 additions & 11 deletions t/mojo/ioloop_tls.t
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ plan skip_all => 'IO::Socket::SSL 2.009+ required for this test!' unless Moj
# openssl req -x509 -days 7300 -key ca.key -in ca.csr -out ca.crt
#
# openssl genrsa -out server.key 1024
# openssl req -new -key server.key -out server.csr -subj "/C=US/CN=127.0.0.1"
# openssl req -new -key server.key -out server.csr -subj "/C=US/CN=localhost"
# openssl x509 -req -days 7300 -in server.csr -out server.crt -CA ca.crt \
# -CAkey ca.key -CAcreateserial
#
Expand Down Expand Up @@ -46,7 +46,7 @@ my $id = $loop->server(
my $port = $loop->acceptor($id)->port;
my $promise2 = Mojo::Promise->new->ioloop($loop);
$loop->client(
{port => $port, tls => 1, tls_options => {SSL_verify_mode => 0x00}} => sub {
{address => 'localhost', port => $port, tls => 1, tls_options => {SSL_verify_mode => 0x00}} => sub {
my ($loop, $err, $stream) = @_;
$stream->write('tset' => sub { shift->write('123') });
$stream->on(close => sub { $promise2->resolve });
Expand Down Expand Up @@ -88,6 +88,7 @@ $id = Mojo::IOLoop->server(
$port = Mojo::IOLoop->acceptor($id)->port;
$promise2 = Mojo::Promise->new;
Mojo::IOLoop->client(
address => 'localhost',
port => $port,
tls => 1,
tls_cert => 't/mojo/certs/client.crt',
Expand Down Expand Up @@ -118,6 +119,7 @@ ok !$server_err, 'no error';
# Invalid client certificate
my $client_err;
Mojo::IOLoop->client(
address => 'localhost',
port => $port,
tls => 1,
tls_cert => 't/mojo/certs/bad.crt',
Expand All @@ -133,7 +135,7 @@ ok $client_err, 'has error';
# Missing client certificate
($server_err, $client_err) = ();
Mojo::IOLoop->client(
{port => $port, tls => 1} => sub {
{address => 'localhost', port => $port, tls => 1} => sub {
shift->stop;
$client_err = shift;
}
Expand All @@ -155,6 +157,7 @@ $id = $loop->server(
);
$port = $loop->acceptor($id)->port;
$loop->client(
address => 'localhost',
port => $port,
tls => 1,
tls_cert => 't/mojo/certs/client.crt',
Expand Down Expand Up @@ -195,6 +198,7 @@ $id = Mojo::IOLoop->server(
$port = Mojo::IOLoop->acceptor($id)->port;
$promise2 = Mojo::Promise->new;
Mojo::IOLoop->client(
address => 'localhost',
port => $port,
tls => 1,
tls_ca => 't/mojo/certs/ca.crt',
Expand Down Expand Up @@ -235,9 +239,10 @@ $id = $loop->server(
);
$port = $loop->acceptor($id)->port;
$loop->client(
port => $port,
tls => 1,
tls_ca => 't/mojo/certs/ca.crt',
address => 'localhost',
port => $port,
tls => 1,
tls_ca => 't/mojo/certs/ca.crt',
sub {
shift->stop;
$client_err = shift;
Expand All @@ -259,7 +264,7 @@ $id = $loop->server(
);
$port = $loop->acceptor($id)->port;
$loop->client(
address => '127.0.0.1',
address => 'localhost',
port => $port,
tls => 1,
tls_ca => 't/mojo/certs/ca.crt',
Expand All @@ -284,9 +289,10 @@ $id = $loop->server(
);
$port = $loop->acceptor($id)->port;
$loop->client(
port => $port,
tls => 1,
tls_ca => 'no cert',
address => 'localhost',
port => $port,
tls => 1,
tls_ca => 'no cert',
sub {
shift->stop;
$client_err = shift;
Expand Down Expand Up @@ -315,6 +321,7 @@ $id = $loop->server(
);
$port = $loop->acceptor($id)->port;
$loop->client(
address => 'localhost',
port => $port,
tls => 1,
tls_cert => 't/mojo/certs/bad.crt',
Expand Down Expand Up @@ -350,7 +357,7 @@ $id = Mojo::IOLoop->server(
);
$port = Mojo::IOLoop->acceptor($id)->port;
Mojo::IOLoop->client(
{port => $port, tls => 1, tls_options => {SSL_verify_mode => 0x00}} => sub {
{address => 'localhost', port => $port, tls => 1, tls_options => {SSL_verify_mode => 0x00}} => sub {
shift->stop;
$client = 'connected';
$client_err = shift;
Expand All @@ -376,6 +383,7 @@ subtest 'ALPN' => sub {
);
$port = Mojo::IOLoop->acceptor($id)->port;
Mojo::IOLoop->client(
address => 'localhost',
port => $port,
tls => 1,
tls_options => {SSL_alpn_protocols => ['baz', 'bar'], SSL_verify_mode => 0x00},
Expand Down
24 changes: 12 additions & 12 deletions t/mojo/user_agent_tls.t
Original file line number Diff line number Diff line change
Expand Up @@ -29,20 +29,20 @@ subtest 'Web server with valid certificates' => sub {

subtest 'No certificate' => sub {
my $ua = Mojo::UserAgent->new(ioloop => Mojo::IOLoop->singleton);
my $tx = $ua->get("https://127.0.0.1:$port");
my $tx = $ua->get("https://localhost:$port");
ok $tx->error, 'has error';
$tx = $ua->get("https://127.0.0.1:$port");
$tx = $ua->get("https://localhost:$port");
ok $tx->error, 'has error';
$tx = $ua->ca('t/mojo/certs/ca.crt')->get("https://127.0.0.1:$port");
$tx = $ua->ca('t/mojo/certs/ca.crt')->get("https://localhost:$port");
ok $tx->error, 'has error';
$tx = $ua->get("https://127.0.0.1:$port");
$tx = $ua->get("https://localhost:$port");
ok $tx->error, 'has error';
};

subtest 'Valid certificates' => sub {
my $ua = Mojo::UserAgent->new(ioloop => Mojo::IOLoop->singleton);
$ua->ca('t/mojo/certs/ca.crt')->cert('t/mojo/certs/client.crt')->key('t/mojo/certs/client.key');
my $tx = $ua->get("https://127.0.0.1:$port");
my $tx = $ua->get("https://localhost:$port");
ok !$tx->error, 'no error';
is $tx->res->code, 200, 'right status';
is $tx->res->body, 'works!', 'right content';
Expand All @@ -54,7 +54,7 @@ subtest 'Web server with valid certificates' => sub {
local $ENV{MOJO_CERT_FILE} = 't/mojo/certs/client.crt';
local $ENV{MOJO_KEY_FILE} = 't/mojo/certs/client.key';
local $ENV{MOJO_INSECURE} = 0;
my $tx = $ua->get("https://127.0.0.1:$port");
my $tx = $ua->get("https://localhost:$port");
is $ua->ca, 't/mojo/certs/ca.crt', 'right path';
is $ua->cert, 't/mojo/certs/client.crt', 'right path';
is $ua->key, 't/mojo/certs/client.key', 'right path';
Expand All @@ -67,7 +67,7 @@ subtest 'Web server with valid certificates' => sub {
subtest 'Invalid certificate' => sub {
my $ua = Mojo::UserAgent->new(ioloop => Mojo::IOLoop->singleton);
$ua->cert('t/mojo/certs/bad.crt')->key('t/mojo/certs/bad.key');
my $tx = $ua->get("https://127.0.0.1:$port");
my $tx = $ua->get("https://localhost:$port");
ok $tx->error, 'has error';
};
};
Expand All @@ -88,11 +88,11 @@ subtest 'Web server with valid certificates and no verification' => sub {
# Invalid certificate
my $ua = Mojo::UserAgent->new(ioloop => Mojo::IOLoop->singleton);
$ua->cert('t/mojo/certs/bad.crt')->key('t/mojo/certs/bad.key');
my $tx = $ua->get("https://127.0.0.1:$port");
my $tx = $ua->get("https://localhost:$port");
ok $tx->error, 'has error';
$ua = Mojo::UserAgent->new(ioloop => $ua->ioloop, insecure => 1);
$ua->cert('t/mojo/certs/bad.crt')->key('t/mojo/certs/bad.key');
$tx = $ua->get("https://127.0.0.1:$port");
$tx = $ua->get("https://localhost:$port");
ok !$tx->error, 'no error';
is $ua->ioloop->stream($tx->connection)->handle->get_cipher, 'AES256-SHA', 'AES256-SHA has been negotiatied';
is $ua->ioloop->stream($tx->connection)->handle->get_sslversion, 'TLSv1', 'TLSv1 has been negotiatied';
Expand All @@ -105,19 +105,19 @@ subtest 'Client side TLS options' => sub {

subtest '(Not) setting verification mode' => sub {
my $ua = Mojo::UserAgent->new(ioloop => Mojo::IOLoop->singleton);
my $tx = $ua->get("https://127.0.0.1:$port");
my $tx = $ua->get("https://localhost:$port");
like $tx->error->{message}, qr/certificate verify failed/, 'has error';

$ua = Mojo::UserAgent->new(ioloop => Mojo::IOLoop->singleton);
$ua->tls_options({SSL_verify_mode => 0x00});
$tx = $ua->get("https://127.0.0.1:$port");
$tx = $ua->get("https://localhost:$port");
ok !$tx->error, 'no error';
};

subtest 'Setting acceptable protocol version' => sub {
my $ua = Mojo::UserAgent->new(ioloop => Mojo::IOLoop->singleton);
$ua->tls_options({SSL_version => 'TLSv1_2'});
my $tx = $ua->get("https://127.0.0.1:$port");
my $tx = $ua->get("https://localhost:$port");
like $tx->error->{message}, qr/wrong ssl version/, 'has error';
};
};
Expand Down
38 changes: 19 additions & 19 deletions t/mojo/websocket_proxy_tls.t
Original file line number Diff line number Diff line change
Expand Up @@ -66,38 +66,38 @@ my $ua = Mojo::UserAgent->new(
# Normal non-blocking request
my $result;
$ua->get(
"https://127.0.0.1:$port/" => sub {
"https://localhost:$port/" => sub {
my ($ua, $tx) = @_;
$result = $tx->res->body;
Mojo::IOLoop->stop;
}
);
Mojo::IOLoop->start;
is $result, "Hello World! / https://127.0.0.1:$port/", 'right content';
is $result, "Hello World! / https://localhost:$port/", 'right content';

# Broken redirect
my $start;
$ua->on(start => sub { $start++; pop->req->headers->header('X-Works', 'it does!') });
$result = undef;
my $works;
$ua->max_redirects(3)->get(
"https://127.0.0.1:$port/broken_redirect" => sub {
"https://localhost:$port/broken_redirect" => sub {
my ($ua, $tx) = @_;
$result = $tx->res->body;
$works = $tx->res->headers->header('X-Works');
Mojo::IOLoop->stop;
}
);
Mojo::IOLoop->start;
is $result, "Hello World! / https://127.0.0.1:$port/", 'right content';
is $result, "Hello World! / https://localhost:$port/", 'right content';
is $works, 'it does!', 'right header';
is $start, 2, 'redirected once';
$ua->unsubscribe('start');

# Normal WebSocket
$result = undef;
$ua->websocket(
"wss://127.0.0.1:$port/test" => sub {
"wss://localhost:$port/test" => sub {
my ($ua, $tx) = @_;
$tx->on(finish => sub { Mojo::IOLoop->stop });
$tx->on(message => sub { shift->finish; $result = shift });
Expand All @@ -112,7 +112,7 @@ $ua->proxy->https("http://sri:secr3t\@127.0.0.1:$proxy");
$result = undef;
my ($auth, $kept_alive);
$ua->get(
"https://127.0.0.1:$port/proxy" => sub {
"https://localhost:$port/proxy" => sub {
my ($ua, $tx) = @_;
$result = $tx->res->body;
$auth = $tx->req->headers->proxy_authorization;
Expand All @@ -123,27 +123,27 @@ $ua->get(
Mojo::IOLoop->start;
ok !$auth, 'no "Proxy-Authorization" header';
ok !$kept_alive, 'connection was not kept alive';
is $result, "https://127.0.0.1:$port/proxy", 'right content';
is $result, "https://localhost:$port/proxy", 'right content';

# Non-blocking kept alive proxy request
($kept_alive, $result) = ();
$ua->get(
"https://127.0.0.1:$port/proxy" => sub {
"https://localhost:$port/proxy" => sub {
my ($ua, $tx) = @_;
$kept_alive = $tx->kept_alive;
$result = $tx->res->body;
Mojo::IOLoop->stop;
}
);
Mojo::IOLoop->start;
is $result, "https://127.0.0.1:$port/proxy", 'right content';
is $result, "https://localhost:$port/proxy", 'right content';
ok $kept_alive, 'connection was kept alive';

# Kept alive proxy WebSocket
$ua->proxy->https("http://127.0.0.1:$proxy");
($kept_alive, $result) = ();
$ua->websocket(
"wss://127.0.0.1:$port/test" => sub {
"wss://localhost:$port/test" => sub {
my ($ua, $tx) = @_;
$kept_alive = $tx->kept_alive;
$tx->on(finish => sub { Mojo::IOLoop->stop });
Expand All @@ -157,23 +157,23 @@ is $result, 'test1test2', 'right result';

# Blocking proxy requests
$ua->proxy->https("http://sri:secr3t\@127.0.0.1:$proxy");
my $tx = $ua->max_connections(0)->get("https://127.0.0.1:$port/proxy");
my $tx = $ua->max_connections(0)->get("https://localhost:$port/proxy");
is $tx->res->code, 200, 'right status';
is $tx->res->body, "https://127.0.0.1:$port/proxy", 'right content';
is $tx->res->body, "https://localhost:$port/proxy", 'right content';
is $tx->req->method, 'GET', 'right method';
is $tx->previous->req->method, 'CONNECT', 'right method';
$tx = $ua->max_connections(5)->get("https://127.0.0.1:$port/proxy");
$tx = $ua->max_connections(5)->get("https://localhost:$port/proxy");
ok !$tx->kept_alive, 'connection was not kept alive';
is $tx->res->code, 200, 'right status';
is $tx->res->body, "https://127.0.0.1:$port/proxy", 'right content';
is $tx->res->body, "https://localhost:$port/proxy", 'right content';
is $tx->req->method, 'GET', 'right method';
is $tx->previous->req->method, 'CONNECT', 'right method';

# Proxy WebSocket with bad target
$ua->proxy->https("http://127.0.0.1:$proxy");
my ($leak, $err);
$ua->websocket(
"wss://127.0.0.1:0/test" => sub {
"wss://localhost:0/test" => sub {
my ($ua, $tx) = @_;
$leak = !!Mojo::IOLoop->stream($tx->previous->connection);
$err = $tx->error;
Expand All @@ -185,9 +185,9 @@ ok !$leak, 'connection has been removed';
is $err->{message}, 'Proxy connection failed', 'right error';

# Blocking proxy request again
$tx = $ua->get("https://127.0.0.1:$port/proxy");
$tx = $ua->get("https://localhost:$port/proxy");
is $tx->res->code, 200, 'right status';
is $tx->res->body, "https://127.0.0.1:$port/proxy", 'right content';
is $tx->res->body, "https://localhost:$port/proxy", 'right content';

# Failed TLS handshake through proxy
my $close = Mojo::IOLoop->acceptor(Mojo::IOLoop->server(sub {
Expand All @@ -214,13 +214,13 @@ $ua->connect_timeout(10);
$ua = Mojo::UserAgent->new;
$proxy = Mojo::IOLoop::Server->generate_port;
$ua->proxy->https("http://127.0.0.1:$proxy");
$tx = $ua->get("https://127.0.0.1:$port/proxy");
$tx = $ua->get("https://localhost:$port/proxy");
is $tx->error->{message}, 'Proxy connection failed', 'right error';

# Non-blocking request to bad proxy
$err = undef;
$ua->get(
"https://127.0.0.1:$port/proxy" => sub {
"https://localhost:$port/proxy" => sub {
my ($ua, $tx) = @_;
$err = $tx->error;
Mojo::IOLoop->stop;
Expand Down

0 comments on commit 36e1551

Please sign in to comment.