Problem:

You need to setup Zope behind Apache with SSL and you need to access some/all of the CGI environment variables set by the mod_ssl from within Plone. How to do it ?

To setup Zope behind Apache with SSL is not the hard part. I'll give anyway an example of setting an apache virtualhost with SSL.

Apache doesn't forward the mod_ssl CGI environement variables to Zope. Why ? Because Zope doesn't support SSL until now.

When you setup apache with SSL as proxy for your Plone site, it (apache) receives HTTPS-requests from the outside but it sends HTTP-requests to Zope. That's why you don't get the SSL headers through to the proxied Plone site.

Certificates:

How to generate your certificate authority, the server certificate and a client certificate to test the setup is out of the scope of this post. Here are 2 links where you can get help for that. Just copy/past the commands if you don't understand. You will finish with getting all certificates:

Or if you have Perl/Gtk in your system use TinyCA. TinyCA is a simple graphical user interface written in Perl/Gtk to manage a small Certification Authority. it works as a frontend for openssl. If your are Ubuntu:

$ aptitude install tinyca

the executable is tinyca2 and not tinyca:

$ /usr/bin/tinyca2

Apache VirtualHost:

Here is an example of setting a VirtualHost with SSL:

<VirtualHost *:443>

ServerName my.server.com

<LocationMatch "^[^/]">
Deny from all
</LocationMatch>

SSLEngine on
SSLCipherSuite HIGH:MEDIUM
SSLProtocol all -SSLv2
SSLCertificateFile /etc/apache2/conf.d/server.cert
SSLCertificateKeyFile /etc/apache2/conf.d/server.key
SSLCertificateChainFile /etc/apache2/conf.d/authority.crt
SSLCACertificateFile /etc/apache2/conf.d/authority.crt

SSLVerifyClient optional
SSLVerifyDepth 1
SSLOptions +stdEnvVars

SetEnvIf User-Agent ".*MSIE.*" nokeepalive ssl-unclean-shutdown

RewriteEngine on
RewriteRule ^/(.*) http://127.0.0.1:8080/VirtualHostBase/https/my.server.com:443/site1/VirtualHostRoot/$1 [P,L]

</VirtualHost>

The most important line related to our problem is the line in red. This mod_ssl directive creates the standard set of SSL related CGI/SSI environment variables. Now, how to forward these variables over HTTP to Zope.

Forwarding the SSL variables:

1. The mod_headers way:

The easiest, not flexible and not secure way is to use mod_headers directives.
Be sure that mod_headers is installed and you have something like this line in your httpd.conf file:

LoadModule headers_module /usr/lib/apache2/modules/mod_headers.so

Now, just forward all the variables you need:

<VirtualHost *:443>

ServerName my.server.com

<LocationMatch "^[^/]">
Deny from all
</LocationMatch>

SSLEngine on
SSLCipherSuite HIGH:MEDIUM
SSLProtocol all -SSLv2
SSLCertificateFile /etc/apache2/conf.d/server.cert
SSLCertificateKeyFile /etc/apache2/conf.d/server.key
SSLCertificateChainFile /etc/apache2/conf.d/authority.crt
SSLCACertificateFile /etc/apache2/conf.d/authority.crt

SSLVerifyClient optional
SSLVerifyDepth 1
SSLOptions +stdEnvVars

SetEnvIf User-Agent ".*MSIE.*" nokeepalive ssl-unclean-shutdown

RequestHeader set SSL_CLIENT_VERIFY %{SSL_CLIENT_VERIFY}e
RequestHeader set SSL_CLIENT_S_DN_CN %{SSL_CLIENT_S_DN_CN}e
RequestHeader set SSL_CLIENT_S_DN_Email %{SSL_CLIENT_S_DN_Email}e


RewriteEngine on
RewriteRule ^/(.*) http://127.0.0.1:8080/VirtualHostBase/https/my.server.com:443/site1/VirtualHostRoot/$1 [P,L]

</VirtualHost>

This way you will get HTTP_SSL_CLIENT_VERIFY, HTTP_SSL_CLIENT_S_DN_CN and HTTP_SSL_CLIENT_S_DN_Email environment variables in the request object. You can access these variables from within your Plone site with something like this:
 
validated = context.request.get('HTTP_SSL_CLIENT_VERIFY')
isValidClientCertificate = validated == 'SUCCESS'
username = context.request.get('HTTP_SSL_CLIENT_S_DN_CN')
useremail = context.request.get('HTTP_SSL_CLIENT_S_DN_Email')

This is not flexible because if you need other variables in the future you will need to edit the apache config files and to restart it. This is also not secure. See the "Securty issue" section.
2. The mod_mustap way:

I worte an apache module that does forward all the SSL evironment variables. Even though it was not so easy to write (It is my first apache module :-) ) it is so easy to use and re-use. It exposes just one directive named MUSTAP_ENABLED that you can set 'On' or Off in your VirtualHost. Here is a step by step installation and use instructions:

1. Get the source code of the mod_mustap and save it as "mod_mustap.c". You can find it in the end of this post.
2. compile the module:

$ gcc -fPIC -fno-stack-protector -DSHARED_MODULE \
-I/usr/include/apache2 -I/usr/include/apr-1.0 -c mod_mustap.c
$ ld -Bshareable -o mod_mustap.so mod_mustap.o

Change the include paths to reflect your system paths.

Now move the mod_mustap.so file to the apache modules folder:

$ mv mod_mustap.so /usr/lib/apache2/modules/

make apache load it when it starts. add this line to the apache config file:

LoadModule mustap_module /usr/lib/apache2/modules/mod_mustap.so

enable the module in your VirtualHost:

<VirtualHost *:443>

ServerName my.server.com

<LocationMatch "^[^/]">
Deny from all
</LocationMatch>

SSLEngine on
SSLCipherSuite HIGH:MEDIUM
SSLProtocol all -SSLv2
SSLCertificateFile /etc/apache2/conf.d/server.cert
SSLCertificateKeyFile /etc/apache2/conf.d/server.key
SSLCertificateChainFile /etc/apache2/conf.d/authority.crt
SSLCACertificateFile /etc/apache2/conf.d/authority.crt

SSLVerifyClient optional
SSLVerifyDepth 1
SSLOptions +stdEnvVars

SetEnvIf User-Agent ".*MSIE.*" nokeepalive ssl-unclean-shutdown

MUSTAP_ENABLED On


RewriteEngine on
RewriteRule ^/(.*) http://127.0.0.1:8080/VirtualHostBase/https/my.server.com:443/site1/VirtualHostRoot/$1 [P,L]

</VirtualHost>

That's all. Now restart apache. You will get all mod_ssl CGI environment variables available in Plone.

Security issue:

1. Make sure that the only way to reach your Plone site is through apache. If your Plone site is accessible without apache, one can inject all the SSL headers without accessing your site through HTTPS. This problem is true regardless if you use mod_mustap or mod_headers or whatsoever. This problem is also true if you use a Plone products like PASSSL, ApachePas or any other product that delegate authentication to apache.

2. make sure that apache in HTTP mode does filter SSL headers. If it doesn't, one can inject all SSL headers through HTTP to your Plone site. If you use mod_mustap, you haven't to worry about this point. mod_mustap filters all SSL headers comming through HTTP so they do not reach your plone site. All you have to do is to enable also mod_mustap in your HTTP VirtualHost as you did in your SSL VirtualHost:

<VirtualHost *:80>

ServerName my.server.com

<LocationMatch "^[^/]">
Deny from all
</LocationMatch>

MUSTAP_ENABLED On

RewriteEngine on
RewriteRule ^/(.*) http://127.0.0.1:8080/VirtualHostBase/http/my.server.com:80/site1/VirtualHostRoot/$1 [P,L]

</VirtualHost>

Conclusion:

mod_mustup removes all injected SSL headers when it is enabled inside a HTTP VirtualHost and fowards all CGI environment variables created by mod_ssl when it is enabled inside HTTPS VirtualHosts.

mod_mustap.c:


#include "httpd.h"
#include "http_config.h"
#include "http_protocol.h"
#include "http_log.h"
#include "ap_config.h"

typedef struct {
int enabled;
} mustapConfig;

module AP_MODULE_DECLARE_DATA mustap_module;

static void *create_mustap_server_config(apr_pool_t *p,
server_rec *s)
{
mustapConfig *pConfig = apr_pcalloc(p, sizeof *pConfig);
pConfig->enabled = 0;
return pConfig;
}

static const char *set_mustap_enable(cmd_parms *cmd,
void *dummy, int arg) {
mustapConfig *pConfig = ap_get_module_config(
cmd->server->module_config,
&mustap_module);
pConfig->enabled = arg;
return NULL;
}

int copy_subprocess_env(void *req,
const char *key,
const char *value) {

request_rec *r = (request_rec *)req;
if (key == NULL || value == NULL || value[0] == '\0')
return 1;

apr_table_set(r->headers_in, key, value);
return 1;
}

int del_ssl_headers(void *req,
const char *key,
const char *value) {

request_rec *r = (request_rec *)req;
if (key == NULL || value == NULL || value[0] == '\0')
return 1;

if (strncmp(key, "SSL_", 4) == 0)
apr_table_unset(r->headers_in, key);
return 1;
}

static int mustap_fixups(request_rec *r) {
char *val;
mustapConfig *pConfig = ap_get_module_config(
r->server->module_config,
&mustap_module);
//is module enabled
if (!pConfig->enabled)
return DECLINED;

//forword ssl variables if it is a HTTPS request
//else delete ssl variables if any, if it is HTTP request
if ((val = (char*)apr_table_get(r->subprocess_env, "HTTPS")) && \
(strcmp(val, "on") == 0))
apr_table_do(copy_subprocess_env, r, r->subprocess_env, NULL);
else
apr_table_do(del_ssl_headers, r, r->headers_in, NULL);

return OK;
}

static const command_rec mustap_cmds[] = {
AP_INIT_FLAG( "MUSTAP_ENABLED",
set_mustap_enable, NULL,
OR_FILEINFO, "Turn on mod_mustap"),
{NULL}
};

static void mustap_register_hooks(apr_pool_t *p) {
ap_hook_fixups(mustap_fixups, NULL, NULL, APR_HOOK_LAST);
}

// API hooks
module AP_MODULE_DECLARE_DATA mustap_module = {
STANDARD20_MODULE_STUFF,
NULL,
NULL,
create_mustap_server_config,
NULL,
mustap_cmds,
mustap_register_hooks
};