================================
Enable SSL Client authentication
================================

Intro
=====
Tested with Apache 2 and mod_ssl.
Django over mod_wsgi. From http://docs.djangoproject.com/en/dev/howto/deployment/modwsgi/
"Deploying Django with Apache and mod_wsgi is the recommended way to get Django into production."

Generate Keys
=============
* Create a CA (passphrase)
openssl genrsa -des3 -out ca.key 2048
openssl req -new -x509 -days 3650 -key ca.key -out ca.crt
openssl x509 -in ca.crt -text -noout
* Server key material (challenge)
openssl genrsa -des3 -out server.key 1024
openssl req -new -key server.key -out server.csr
openssl x509 -req -in server.csr -out server.crt -sha1 -CA ca.crt -CAkey ca.key -CAcreateserial -days 3650
openssl x509 -in server.crt -text -noout
* User Key material (challenge/password)
openssl genrsa -des3 -out c.key 1024
openssl req -new -key c.key -out c.csr
openssl x509 -req -in c.csr -out c.crt -sha1 -CA ca.crt -CAkey ca.key -CAcreateserial -days 3650
openssl pkcs12 -export -in c.crt -inkey c.key -name "Mikael Ates" -out c.p12
openssl pkcs12 -in c.p12 -clcerts -nokeys -info

Configure Apache and WSGI
=========================
Add a file django.wsgi, e.g.:
"""
import os
import sys

sys.path.append("/usr/local/lib/python2.6/site-packages/")
try:
    import lasso
except:
    print >> sys.stderr, "Unable to import Lasso."

apache_configuration= os.path.dirname(__file__)
project = os.path.dirname(apache_configuration)
sys.path.append(project)
try:
    import authentic2.settings
    os.environ['DJANGO_SETTINGS_MODULE'] = 'authentic2.settings'
except:
    print >> sys.stderr, "Unable to import settings."

import django.core.handlers.wsgi
application = django.core.handlers.wsgi.WSGIHandler()
"""

Activate apache2 modules:
* a2enmod wsgi
* a2enmod ssl

Add a Apache vhost for SSL.
"""
<IfModule mod_ssl.c>
<VirtualHost *:443>

LimitInternalRecursion 1000
ServerAdmin webmaster@entrouvert.org
ServerName localhost

Alias /media/admin/ /usr/local/lib/python2.6/dist-packages/django/contrib/admin/media/

WSGIScriptAlias / /Donnees/devs/authentic/apache/django.wsgi

<Directory /Donnees/devs/authentic/>
SSLVerifyClient optional_no_ca
Options Indexes MultiViews FollowSymLinks
AllowOverride None
Order deny,allow
Allow from all
</Directory>

SSLEngine on
SSLCipherSuite HIGH:MEDIUM
SSLProtocol all -SSLv2

SSLCertificateFile /Donnees/devs/authentic/apache/key_mat/server.crt
SSLCertificateKeyFile /Donnees/devs/authentic/apache/key_mat/server.key

SSLCertificateChainFile /Donnees/devs/authentic/apache/key_mat/ca.crt
SSLCACertificateFile /Donnees/devs/authentic/apache/key_mat/ca.crt

SSLOptions +StdEnvVars +ExportCertData

BrowserMatch "MSIE [2-6]" \
	nokeepalive ssl-unclean-shutdown \
	downgrade-1.0 force-response-1.0
BrowserMatch "MSIE [17-9]" ssl-unclean-shutdown

</VirtualHost>
</IfModule>
"""

Give rights to Apache on your Authentic directory.
Reload Apache.

Configure Authentic
===================

Key                        Description
-------------------------- ----------------------------------------
ACCEPT_SELF_SIGNED         accept certificate for which the validation failed,
                           default: False
STRICT_MATCH               do a binary compare to match certificate and users,
                           default: False
SUBJECT_MATCH_KEYS         SSL information to use to match recorded
                           certificates. default: ('subject_dn', 'issuer_dn'),
                           possible values: serial, subject_dn, issuer_dn, cert.
CREATE_USERNAME_CALLBACK   function receiving a SSLInfo object as first
                           parameter and returning a username, default: None
CREATE_USER                function receiving a SSLInfo object as first
                           parameter and returning a user, default: None
USE_COOKIE                 to be described

in settings.py:
Set AUTH_SSL = True
To create a user with the mail adress as identifier:
SSLAUTH_CREATE_USER = True
To use another identifier:
def myusernamegen(ssl_info):
    import re
    if(ssl_info.subject_cn):
        return return re.sub('[^a-zA-Z0-9]', '_', ssl_info.subject_cn)
    else:
        return return re.sub('[^a-zA-Z0-9]', '_', ssl_info.serial)
SSLAUTH_CREATE_USERNAME_CALLBACK = myusernamegen


Nginx configuration
===================

You must be able to retrieve SSL environment variable, for example with the
SCGI backend you must add those lines to /etc/nginx/scgi_params::

    scgi_param SSL_CLIENT_CERT $ssl_client_cert;
    scgi_param SSL_CLIENT_RAW_CERT $ssl_client_raw_cert;
    scgi_param SSL_CLIENT_S_DN $ssl_client_s_dn;
    scgi_param SSL_CLIENT_I_DN $ssl_client_i_dn;
    scgi_param SSL_CLIENT_SERIAL $ssl_client_serial;
    scgi_param SSL_CLIENT_M_SERIAL $ssl_client_serial;
    scgi_param SSL_CLIENT_VERIFY $ssl_client_verify;

It would be the same with FCGI but using the fcgi_param directive in the
fcgi_params file. It does not currently work when using proxy_pass.

A virtualhost configuration example::

    server {
      listen 80;
      server_name authentic.localhost;

      rewrite ^ https://$server_name$request_uri? permanent;
    }

    server {
      listen 443;
      server_name authentic.localhost;

      ssl on;
      ssl_certificate /etc/ssl/certs/ssl-cert-snakeoil.pem;
      ssl_certificate_key /etc/ssl/private/ssl-cert-snakeoil.key;
      ssl_verify_client optional_no_ca;

      location / {
      include scgi_params;
            scgi_pass localhost:8000;
      }
    }

The serveur must be run using the SCGI protocol, with this command line for
example::

    ./manage.py runfcgi protocol=scgi method=threaded daemonize=false host=localhost port=8000
