From b62118dc0a6684d02803e05b9b4c7e7e006e1a92 Mon Sep 17 00:00:00 2001 From: ellie timoney Date: Wed, 22 Jan 2025 14:38:07 +1100 Subject: [PATCH 1/7] WIP TestCase: pass SSL_options to Net::DAVTalk --- cassandane/Cassandane/Cyrus/TestCase.pm | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/cassandane/Cassandane/Cyrus/TestCase.pm b/cassandane/Cassandane/Cyrus/TestCase.pm index b2da82fe7f..6dc66765b7 100644 --- a/cassandane/Cassandane/Cyrus/TestCase.pm +++ b/cassandane/Cassandane/Cyrus/TestCase.pm @@ -41,12 +41,13 @@ package Cassandane::Cyrus::TestCase; use strict; use warnings; use attributes; +use Cwd qw(abs_path); use Data::Dumper; -use Scalar::Util qw(refaddr); -use List::Util qw(uniq); use Digest::file qw(digest_file_hex); -use File::Temp qw(tempfile); use File::Path qw(rmtree); +use File::Temp qw(tempfile); +use List::Util qw(uniq); +use Scalar::Util qw(refaddr); use lib '.'; use base qw(Cassandane::Unit::TestCase); @@ -800,21 +801,20 @@ sub _setup_http_service_objects $service ||= $self->{instance}->get_service("https"); return if !$service; + my $ca_file = abs_path("data/certs/cacert.pem"); + my %common_args = ( user => 'cassandane', password => 'pass', host => $service->host(), port => $service->port(), scheme => ($service->is_ssl() ? 'https' : 'http'), + SSL_options => { + SSL_ca_file => $ca_file, + SSL_verifycn_scheme => 'none', + }, ); - # XXX HTTP::Tiny 0.8.3 and later have SSL_verify enabled by default, but - # XXX Net::DAVTalk doesn't provide any way for us to supply our CA file, - # XXX making setup fail with certificate verify errors. - # XXX HTTP::Tiny 0.86 and later lets us set this environment variable - # XXX to restore the old default - local $ENV{PERL_HTTP_TINY_SSL_INSECURE_BY_DEFAULT} = 1; - if ($self->{instance}->{config}->get_bit('httpmodules', 'carddav')) { require Net::CardDAVTalk; $self->{carddav} = Net::CardDAVTalk->new( @@ -834,6 +834,8 @@ sub _setup_http_service_objects "cassandane\@example.com"); } if ($self->{instance}->{config}->get_bit('httpmodules', 'jmap')) { + # XXX would be nice if Mail::JMAPTalk would pass through SSL_options + # XXX to its HTTP::Tiny constructor too... require Mail::JMAPTalk; $ENV{DEBUGJMAP} = 1; $self->{jmap} = Mail::JMAPTalk->new( From 2719871992e5b2ea7aa3d913e4561531cc871ac4 Mon Sep 17 00:00:00 2001 From: ellie timoney Date: Fri, 24 Jan 2025 12:28:52 +1100 Subject: [PATCH 2/7] conditionally set http tiny env --- cassandane/Cassandane/Cyrus/TestCase.pm | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/cassandane/Cassandane/Cyrus/TestCase.pm b/cassandane/Cassandane/Cyrus/TestCase.pm index 6dc66765b7..e0c3df176e 100644 --- a/cassandane/Cassandane/Cyrus/TestCase.pm +++ b/cassandane/Cassandane/Cyrus/TestCase.pm @@ -41,6 +41,7 @@ package Cassandane::Cyrus::TestCase; use strict; use warnings; use attributes; +use version 0.77; use Cwd qw(abs_path); use Data::Dumper; use Digest::file qw(digest_file_hex); @@ -815,6 +816,26 @@ sub _setup_http_service_objects }, ); + my $need_http_tiny_env; + eval { + require Net::DAVTalk; + require HTTP::Tiny; + + if (version->parse($Net::DAVTalk::VERSION) < version->parse('0.23') + && version->parse($HTTP::Tiny::VERSION) >= version->parse('0.083')) + { + # Net::DAVTalk < 0.23 doesn't pass through SSL_options, but + # HTTP::Tiny >= 0.083 enables SSL certificate verification, + # which will fail without our SSL_options. + # For HTTP::Tiny >= 0.086, we can set an environment variable + # to turn off SSL certificate verifications. + # For HTTP::Tiny in 0.083 .. 0.085, ¯\_(ツ)_/¯ + HTTP::Tiny->VERSION('0.086'); + $need_http_tiny_env = 1; + } + }; + local $ENV{PERL_HTTP_TINY_SSL_INSECURE_BY_DEFAULT} = $need_http_tiny_env; + if ($self->{instance}->{config}->get_bit('httpmodules', 'carddav')) { require Net::CardDAVTalk; $self->{carddav} = Net::CardDAVTalk->new( From 3251954347fe0357e52eff34722eb21d130c4f9f Mon Sep 17 00:00:00 2001 From: ellie timoney Date: Fri, 24 Jan 2025 12:29:16 +1100 Subject: [PATCH 3/7] junk debug logging --- cassandane/Cassandane/Cyrus/TestCase.pm | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/cassandane/Cassandane/Cyrus/TestCase.pm b/cassandane/Cassandane/Cyrus/TestCase.pm index e0c3df176e..29db2f770f 100644 --- a/cassandane/Cassandane/Cyrus/TestCase.pm +++ b/cassandane/Cassandane/Cyrus/TestCase.pm @@ -821,6 +821,9 @@ sub _setup_http_service_objects require Net::DAVTalk; require HTTP::Tiny; + xlog $self, "XXX have Net::DAVTalk version " . Net::DAVTalk->VERSION(); + xlog $self, "XXX have HTTP::Tiny version " . HTTP::Tiny->VERSION(); + if (version->parse($Net::DAVTalk::VERSION) < version->parse('0.23') && version->parse($HTTP::Tiny::VERSION) >= version->parse('0.083')) { @@ -830,10 +833,14 @@ sub _setup_http_service_objects # For HTTP::Tiny >= 0.086, we can set an environment variable # to turn off SSL certificate verifications. # For HTTP::Tiny in 0.083 .. 0.085, ¯\_(ツ)_/¯ + xlog $self, "XXX will need http tiny env"; HTTP::Tiny->VERSION('0.086'); $need_http_tiny_env = 1; } }; + if ($@) { + xlog "XXX eval failed: $@"; + } local $ENV{PERL_HTTP_TINY_SSL_INSECURE_BY_DEFAULT} = $need_http_tiny_env; if ($self->{instance}->{config}->get_bit('httpmodules', 'carddav')) { @@ -845,6 +852,8 @@ sub _setup_http_service_objects ); } if ($self->{instance}->{config}->get_bit('httpmodules', 'caldav')) { + xlog "XXX http tiny env: " + . $ENV{PERL_HTTP_TINY_SSL_INSECURE_BY_DEFAULT}; require Net::CalDAVTalk; $self->{caldav} = Net::CalDAVTalk->new( %common_args, From 231102e1cc2d28c3b9a27abe93e2dbcf8aad532b Mon Sep 17 00:00:00 2001 From: ellie timoney Date: Fri, 24 Jan 2025 14:39:36 +1100 Subject: [PATCH 4/7] JMAPCore: add echo-tls test minimal exercising of JMAP over HTTPS --- cassandane/tiny-tests/JMAPCore/echo-tls | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 cassandane/tiny-tests/JMAPCore/echo-tls diff --git a/cassandane/tiny-tests/JMAPCore/echo-tls b/cassandane/tiny-tests/JMAPCore/echo-tls new file mode 100644 index 0000000000..015ff1d966 --- /dev/null +++ b/cassandane/tiny-tests/JMAPCore/echo-tls @@ -0,0 +1,25 @@ +#!perl +use Cassandane::Tiny; + +sub test_echo_tls + :TLS :want_service_https :needs_component_httpd +{ + my ($self) = @_; + + my $jmap = $self->{jmap}; + + my $req = { + hello => JSON::true, + max => 5, + stuff => { foo => "bar", empty => JSON::null } + }; + + xlog $self, "send ping"; + my $res = $jmap->CallMethods([['Core/echo', $req, "R1"]]); + + xlog $self, "check pong"; + $self->assert_not_null($res); + $self->assert_str_equals('Core/echo', $res->[0][0]); + $self->assert_deep_equals($req, $res->[0][1]); + $self->assert_str_equals('R1', $res->[0][2]); +} From b2573823feb6991e9bc8a93e10a2dad8f4ea3bf6 Mon Sep 17 00:00:00 2001 From: ellie timoney Date: Fri, 24 Jan 2025 14:42:00 +1100 Subject: [PATCH 5/7] TestCase: prefer https over http if configured --- cassandane/Cassandane/Cyrus/TestCase.pm | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cassandane/Cassandane/Cyrus/TestCase.pm b/cassandane/Cassandane/Cyrus/TestCase.pm index 29db2f770f..23f6a66b1b 100644 --- a/cassandane/Cassandane/Cyrus/TestCase.pm +++ b/cassandane/Cassandane/Cyrus/TestCase.pm @@ -798,8 +798,8 @@ sub _setup_http_service_objects my ($self) = @_; # nothing to do if no http or https service - my $service = $self->{instance}->get_service("http"); - $service ||= $self->{instance}->get_service("https"); + my $service = $self->{instance}->get_service("https"); + $service ||= $self->{instance}->get_service("http"); return if !$service; my $ca_file = abs_path("data/certs/cacert.pem"); From c08c363bcbec2238f53f42108dcff038be6636f7 Mon Sep 17 00:00:00 2001 From: ellie timoney Date: Fri, 24 Jan 2025 14:42:59 +1100 Subject: [PATCH 6/7] WIP TestCase: refactor HTTP Tiny env stuff (has debug logging) --- cassandane/Cassandane/Cyrus/TestCase.pm | 69 +++++++++++++++---------- 1 file changed, 43 insertions(+), 26 deletions(-) diff --git a/cassandane/Cassandane/Cyrus/TestCase.pm b/cassandane/Cassandane/Cyrus/TestCase.pm index 23f6a66b1b..00c5071621 100644 --- a/cassandane/Cassandane/Cyrus/TestCase.pm +++ b/cassandane/Cassandane/Cyrus/TestCase.pm @@ -793,6 +793,48 @@ sub _create_instances } } +sub _need_http_tiny_env +{ + # Net::DAVTalk < 0.23 and Mail::JMAPTalk < 0.17 don't pass through + # SSL_options, but HTTP::Tiny >= 0.083 enables SSL certificate + # verification, which will fail without our SSL_options. + # + # For HTTP::Tiny >= 0.086, we can set an environment variable + # to turn off SSL certificate verifications. + # + # For HTTP::Tiny in 0.083 .. 0.085, ¯\_(ツ)_/¯ + eval { + require Net::DAVTalk; + require HTTP::Tiny; + }; + return undef if $@; + + my $ndv = version->parse($Net::DAVTalk::VERSION); + my $mjv = version->parse($Mail::JMAPTalk::VERSION); + my $htv = version->parse($HTTP::Tiny::VERSION); + + xlog "XXX have Net::DAVTalk version " . Net::DAVTalk->VERSION(); + xlog "XXX have Mail::JMAPTalk version " . Mail::JMAPTalk->VERSION(); + xlog "XXX have HTTP::Tiny version " . HTTP::Tiny->VERSION(); + + # not needed: old HTTP::Tiny doesn't check certificates + return undef if $htv < version->parse('0.083'); + + # not needed: new Net::DAVTalk and Mail::JMAPTalk pass through SSL_options + return undef if $ndv >= version->parse('0.23') + && $mjv >= version->parse('0.17'); + + xlog "XXX will need http tiny env"; + + # awkward: HTTP::Tiny new enough to check certificates, but not new + # enough to override that by the environment variable. if you get errors + # here, you need to either upgrade HTTP::Tiny to 0.086 or later, or + # upgrade Net::DAVTalk to 0.23 and Mail::JMAPTalk to 0.17 + HTTP::Tiny->VERSION('0.086'); + + return 1; +} + sub _setup_http_service_objects { my ($self) = @_; @@ -816,32 +858,7 @@ sub _setup_http_service_objects }, ); - my $need_http_tiny_env; - eval { - require Net::DAVTalk; - require HTTP::Tiny; - - xlog $self, "XXX have Net::DAVTalk version " . Net::DAVTalk->VERSION(); - xlog $self, "XXX have HTTP::Tiny version " . HTTP::Tiny->VERSION(); - - if (version->parse($Net::DAVTalk::VERSION) < version->parse('0.23') - && version->parse($HTTP::Tiny::VERSION) >= version->parse('0.083')) - { - # Net::DAVTalk < 0.23 doesn't pass through SSL_options, but - # HTTP::Tiny >= 0.083 enables SSL certificate verification, - # which will fail without our SSL_options. - # For HTTP::Tiny >= 0.086, we can set an environment variable - # to turn off SSL certificate verifications. - # For HTTP::Tiny in 0.083 .. 0.085, ¯\_(ツ)_/¯ - xlog $self, "XXX will need http tiny env"; - HTTP::Tiny->VERSION('0.086'); - $need_http_tiny_env = 1; - } - }; - if ($@) { - xlog "XXX eval failed: $@"; - } - local $ENV{PERL_HTTP_TINY_SSL_INSECURE_BY_DEFAULT} = $need_http_tiny_env; + local $ENV{PERL_HTTP_TINY_SSL_INSECURE_BY_DEFAULT} = _need_http_tiny_env(); if ($self->{instance}->{config}->get_bit('httpmodules', 'carddav')) { require Net::CardDAVTalk; From 7aa9dd0ad57c5db680bc267bb13e861528136451 Mon Sep 17 00:00:00 2001 From: ellie timoney Date: Fri, 24 Jan 2025 15:18:23 +1100 Subject: [PATCH 7/7] fix jmap (has logging still) --- cassandane/Cassandane/Cyrus/TestCase.pm | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/cassandane/Cassandane/Cyrus/TestCase.pm b/cassandane/Cassandane/Cyrus/TestCase.pm index 00c5071621..0ff8baac0a 100644 --- a/cassandane/Cassandane/Cyrus/TestCase.pm +++ b/cassandane/Cassandane/Cyrus/TestCase.pm @@ -881,6 +881,8 @@ sub _setup_http_service_objects "cassandane\@example.com"); } if ($self->{instance}->{config}->get_bit('httpmodules', 'jmap')) { + xlog "XXX http tiny env: " + . $ENV{PERL_HTTP_TINY_SSL_INSECURE_BY_DEFAULT}; # XXX would be nice if Mail::JMAPTalk would pass through SSL_options # XXX to its HTTP::Tiny constructor too... require Mail::JMAPTalk; @@ -889,6 +891,9 @@ sub _setup_http_service_objects %common_args, url => '/jmap/', ); + + # preload default UA while the HTTP::Tiny env var is still set + $self->{jmap}->ua(); } xlog $self, "http service objects setup complete!";