The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.

NAME

App::CamelPKI::SysV::Apache - Modeling the Camel-PKI web server.

SYNOPSIS

   use App::CamelPKI::SysV::Apache;
   use App::CamelPKI::Error;

   my $apache = load App::CamelPKI::SysV::Apache($directory);
   $apache->set_keys(-certificate => $cert, -key => $key,
                     -certification_chain => [ $opcacert, $rootcacert ]);
   $apache->https_port(443);
   try {
       $apache->start();
   } catch App::CamelPKI::Error::OtherProcess with {
       die "Dude, your Apache is out of whack!" if $apache->is_wedged;
       die "Could not start Apache: " .
           $apache->tail_error_logfile();
   };
   $apache->update_crl($crl);
   $apache->stop();

DESCRIPTION

Instances of App::CamelPKI::SysV::Apache each represent an Apache Web server that serves the App-PKI application. App::CamelPKI::SysV::Apache encapsulates all the system- and distribution-specific knowledge needed to run an Apache web server: it knows how to create a configuration file, start and stop it, manage its PID file, log files, and so on.

In the current implementation, an instance of App::CamelPKI::SysV::Apache only listens to one TCP port in HTTP/S, and the URLs are (mostly) interpreted relative to App::CamelPKI's standard URL namespace. The essential feature of App::CamelPKI::SysV::Apache that the default, Catalyst-provided server lacks (camel_pki_server.pl) is the support for client-side authentication using SSL certificates. Thanks to this feature, App-PKI is able to use itself for its own authentication needs.

CONSTRUCTOR AND METHODS

load($directory)

Creates and returns an instance of App::CamelPKI::SysV::Apache by loading it from the file system. Like all constructors that take a directory as argument, load is subdued to capability discipline using App::CamelPKI::RestrictedClassMethod.

$directory is the path where the server's various persistent files are stored (configuration file, PID file, cryptographic keys etc). $directory must exist, but can be empty.

https_port()

https_port($portnum)

Gets or set the port on which the daemon will listen for HTTP/S requests. The default value is 443 if the current process is privileged enough to bind to it, or 3443 otherwise. This port number is persisted onto disk and therefore only needs to be set once.

test_php_directory()

test_php_directory($dir)

test_php_directory(undef)

Gets, sets or disables the test PHP script directory in this instance of App::CamelPKI::SysV::Apache. The default is to disable this feature, which only serves for Camel-PKI's self-tests (unit and integration).

The value of test_php_directory is persisted to disk, so that it need not be reset at each construction. It only takes effect the next time the server is restarted with "start".

has_camel_pki()

has_camel_pki($boolean)

Gets or sets the "has App-PKI" flag, which defaults to true. Instances of App::CamelPKI::SysV::Apache that have has_camel_pki() set to false do not contain the Camel-PKI application. Again, this is only useful for tests.

The value of has_camel_pki is persisted to disk, so that it need not be reset at each construction. It only takes effect the next time the server is restarted with "start".

set_keys(-certificate => $cert, -key => $key, -certification_chain => \@chain)

Installs key material that will allow this Apache daemon to authenticate itself to its HTTP/S clients ($cert and $key, which must be instances of App::CamelPKI::Certificate and App::CamelPKI::PrivateKey respectively), and also to verify the identity of HTTP/S clients that themselves use a certificate (@chain, which is a list of instances of App::CamelPKI::Certificate; see also "update_crl"). If $cert is a self-signed certificate, -certification_chain and its parameter \@chain may be omitted.

is_operational()

Returns true if and only if the ad-hoc cryptographic material has been added to this Web server using "set_keys".

certificate()

Returns the Web server's SSL certificate, as an instance of App::CamelPKI::Certificate.

update_crl($crl)

Given $crl, an instance of App::CamelPKI::CRL, verifies the signature thereof and stores it into this Apache server if and only if it matches one of the CAs previously installed using "set_keys"' -certificate_chain named option, and $crl is older than any CRL previously added with update_crl(). If these security checks are successful and Apache is already running, it will be restarted so as to take the new CRL into account immediately.

Note that a Web server works perfectly without a CRL, and therefore calling update_crl is optional. However, remember that CRLs have expiration dates: once a CRL has been installed using this method, one should plan for a suitable mechanism (e.g. a crontab entry) that will download updated CRLs on a regular basis and submit them using update_crl().

start(%opts)

Starts the daemon synchronously, meaning that start will only return control to its caller after ensuring that the Apache process wrote its PID file and bound to its TCP port. start() is idempotent, and terminates immediately if the serveur is already up.

An "App::CamelPKI::Error::OtherProcess" in App::CamelPKI::Error exception will be thrown if the server doesn't answer within "async_timeout" seconds. An "App::CamelPKI::Error::User" in App::CamelPKI::Error exception will be thrown if one attempts to start() the server before providing it with its certificate and key with "set_keys".

Available named options are:

-strace => $strace_logfile

Starts Apache under the strace debug command, storing all results into $strace_logfile.

-X => 1

Starts Apache with the -X option, which causes it to launch only one worker and to not detach from the terminal.

-gdb => 1
-gdb => $tty

Starts Apache under the GNU debugger attached to tty $tty (or the current tty, if the value 1 is specified). Incompatible with -strace. If this option is specified, start() will not time out after "async_timeout" seconds, but will instead wait an unlimited amount of time for the server to come up.

-exec => 1

Don't fork a subprocess, use the exec system call instead (see "exec" in perlfunc) to run Apache directly (or more usefully, some combination of Apache and a debugger, according to the above named options). The current UNIX process will turn into Apache, and the start method will therefore never return.

stop()

Stops the daemon synchronously, meaning that stop will only return control to its caller after ensuring that the Apache process whose PID is in the PID file is terminated, and the TCP port is closed. Like "start", this method is idempotent and returns immediately if the server was already down.

An exception of class "App::CamelPKI::Error::OtherProcess" in App::CamelPKI::Error will be thrown if the server still hasn't stopped after "async_timeout" seconds.

Note that the "started" or "stopped" state is persisted to the filesystem using the usual UNIX PID file mechanism; therefore it is not necessary to use the same Perl object (or even the same process) to "start" and stop() a given server.

is_started()

Returns true iff the PID file currently contains the PID of a live Apache process, and one can connect to the TCP port.

is_stopped()

Returns true iff the PID file (if it exists at all) contains something that is not the PID of a live Apache process, and the TCP port is closed.

is_wedged()

Returns true iff neither "is_stopped", nor "is_started" are true (e.g. if the TCP port is taken, but not by us). One cannot call "start" or "stop" against an instance of App::CamelPKI::SysV::Apache that is_wedged() ("App::CamelPKI::Error::OtherProcess" in App::CamelPKI::Error exceptions would be thrown). More generally, neither can one call any method that act upon other processes such as "update_crl". The systems administrator therefore needs to take manual corrective action to get out of this state.

is_current_interpreter()

Returns true iff the Perl interpreter we're currently running under is a mod_perl belonging to this object's App::CamelPKI::SysV::Apache instance.

is_running_under()

Returns true iff the Perl interpreter currently running is mod_perl. Contrary to "is_current_interpreter", this method returns true even if called from within another Apache container; in other words it doesn't look at $self, and indeed it can be called as a class method too.

async_timeout()

async_timeout($timeout)

Gets or sets the maximum time (in seconds) that "start" and "stop" will wait for the Apache server to come up (resp. down). The default value is 20 seconds; it does not get persisted, and therefore must be set by caller code after each "load".

tail_error_logfile()

Returns the amount of text that was appended to the error log file since the object was created since the previous call to tail_eror_logfile() (or barring that, to "load"). Returns undef if the log file does not exist (yet).

TODO

In App::CamelPKI::Apache's current implementation, only Apache 2 for Ubuntu Edgy is supported. However, the encapsulation of the class makes it easy to support other environments, without changing anything in the rest of Camel-PKI.