How to have Apache::Test generate SSL certificates?
Sometimes one needs to write module tests for modperl or CGI applications that involve connections over SSL.
Apache::Test can help with generating the certificates. But how
to do that isn't documented anywhere.
After a few hours searching and reading the code I discovered that it is really simple and comes down to a single
mkdir t/conf/ssl
The Details
When using Apache::Test a typical directory layout looks like:
Makefile.PL (or Build.PL)
t/
t/TEST.PL
t/conf/
t/conf/extra.conf.in
The typical Makefile.PL reads
use strict;
use 5.008008;
BEGIN {
eval {
require ModPerl::MM;
require Apache::TestMM;
1;
} or exit 0;
Apache::TestMM->import( qw(test clean) );
}
# accept the configs from command line
Apache::TestMM::filter_args();
Apache::TestMM::generate_script('t/TEST');
ModPerl::MM::WriteMakefile(
NAME => 'My::Module',
VERSION_FROM => 'lib/My/Module.pm',
ABSTRACT_FROM => 'lib/My/Module.pm',
AUTHOR => 'Just Me <me@mymail.com>',
PREREQ_PM => {
'Apache2::Const' => 0,
'ModPerl::MM' => 0,
'Apache::TestMM' => 0,
'Test::More' => 0,
},
dist => {
COMPRESS => 'gzip -9f',
PREOP => './mk_README.sh',
},
clean => {
FILES=>"t/TEST",
},
);
The important thing here is Apache::TestMM::generate_script('t/TEST');. This line converts t/TEST.PL into t/TEST.
Now, lets have a look at t/TEST.PL.
#!perl
use strict;
use warnings FATAL => 'all';
use lib qw(lib);
use Apache::TestRunPerl ();
Apache::TestRunPerl->new->run(@ARGV);
Nothing new so far.
Now, create a directory called t/conf/ssl and run Makefile.PL and then t/TEST -configure (or simply make test)
and Apache::Test will create a bunch of certificates for you (provided OpenSSL is available):
$ mkdir t/conf/ssl
$ perl Makefile.PL
[ info] generating script t/TEST
...
$ t/TEST -configure
[warning] setting ulimit to allow core files
ulimit -c unlimited; /usr/bin/perl /home/r2/xx/t/TEST -configure
[warning] cleaning out current configuration
[ error] port 8529 is in use, cannot determine server pid to shutdown
[warning] generating SSL CA for asf
[ info] openssl req -new -x509 -keyout keys/ca.pem -out certs/ca.crt -days 365 -config conf/ca.cnf
...
There are certainly ways to influence the parameters of the certificates created. But for the simple case of testing some code over SSL
the certificate/key pair in t/conf/ssl/ca/asf/certs/server.crt and t/conf/ssl/ca/asf/keys/server.pem is sufficient.
Using the certificates
Apache configuration
To use the certificates one probably wants to create a VirtualHost to handle SSL requests. This can be done in any configuration
file in t/conf. Usually, there is already such a file called t/conf/extra.conf.in or t/conf/extra.last.conf.in.
Put the following snippet there:
<IfModule ssl_module>
SSLRandomSeed startup builtin
SSLRandomSeed connect builtin
<VirtualHost ssl>
SSLEngine on
SSLCertificateFile "@SSLCA@/asf/certs/server.crt"
SSLCertificateKeyFile "@SSLCA@/asf/keys/server.pem"
</VirtualHost>
</IfModule>
Note how the certificate is referred to.
Accessing the server
So, now that the server is up and running we have to find a way to figure out what port it is listening on. In the configuration file we have written
<VirtualHost ssl> instead of the normal <VirtualHost _default_:443>. Further, the Listen
directive has been completely omitted. The point is, ssl in the code snippet above is simply a name. Test scripts can use that name
to fetch the actual host and port. Here a one-liner, without a parameter it returns the hostport to access the default server. The parameter
ssl which references the virtual host name produces the hostport of the SSL server.
$ perl -MApache::TestRequest -le 'Apache::TestRequest::module shift; print Apache::TestRequest::hostport'
localhost:8529
$ perl -MApache::TestRequest -le 'Apache::TestRequest::module shift; print Apache::TestRequest::hostport' ssl
localhost:8533
So, lets try it using curl as browser:
$ curl https://$(perl -MApache::TestRequest -le 'Apache::TestRequest::module shift; print Apache::TestRequest::hostport' ssl)/
curl: (60) SSL certificate problem, verify that the CA cert is OK. Details:
error:14090086:SSL routines:SSL3_GET_SERVER_CERTIFICATE:certificate verify failed
More details here: http://curl.haxx.se/docs/sslcerts.html
curl performs SSL certificate verification by default, using a "bundle"
of Certificate Authority (CA) public keys (CA certs). If the default
bundle file isn't adequate, you can specify an alternate file
using the --cacert option.
If this HTTPS server uses a certificate signed by a CA represented in
the bundle, the certificate verification probably failed due to a
problem with the certificate (it might be expired, or the name might
not match the domain name in the URL).
If you'd like to turn off curl's verification of the certificate, use
the -k (or --insecure) option.
Oops! Of course, that cannot work. Our browser does not know how to trust the certificate displayed by the server.
One can use the -k option as suggested. But that would make the whole SSL thing useless.
Though, for a test it may be permissible.
However, Apache::Test has certainly generated a CA certificate. Lets find it! First thing, the
SSLCA location:
$ perl -MApache::Test -le 'print Apache::Test::vars shift' sslca
/path/to/My-Module/t/conf/ssl/ca
The CA certificate is named asf/certs/ca.crt within this directory.
Now, we have all the tools together to issue a HTTPS request to our test server:
curl https://"$(perl -MApache::TestRequest -le 'Apache::TestRequest::module shift; print Apache::TestRequest::hostport' ssl)"/ \
--cacert "$(perl -MApache::Test -le 'print Apache::Test::vars shift' sslca)"/asf/certs/ca.crt
<!-- WARNING: this file is generated, do not edit
generated on Sat Mar 13 12:17:28 2010
...
And how about test scripts?
I use this approach:
# switch to ssl VHost
Apache::TestRequest::module 'ssl';
my $sslhostport=Apache::TestRequest::hostport;
t_debug "Using $sslhostport for HTTPS";
# Define a helper function. Note the prototype.
# It allows for 'GET S $url, header=>...' w/o parentheses
sub S ($) {'https://'.$sslhostport.$_[0]}
# switch back
Apache::TestRequest::module 'default';
# fetch it unencrypted
$resp=GET '/shop/something', Cookie=>...;
# or encrypted
$resp=GET S '/shop/something, Cookie=>...;
Now, sometimes you need to generate links other resources in the application. Of course, for testing
purposes only one can generate HTTPS links as https://localhost/... but I like it if I
can start the test server via t/TEST -start-httpd and then browse the application. But
then one needs to generate links that contain the correct SSL port. For this I use something like
the following <Perl> section in the extra.last.conf.in. Then the code
can check $ENV{SSL_PORT} when it generates links.
<Perl>
use Apache::Test;
$ENV{SSL_PORT}=Apache::Test::vars('ssl_port');
</Perl>
Letzte Aktualisierung: 14.03.2010

