Ĩesky | english
NOTA BENE - WORK IN PROGRESS - Your Inputs & Thoughts
To Technology Knowledge Base - To Technology Knowledge Base - Overview - To Technology Knowledge Base - Server - This Article you find as well in Support for System Administrators
As well by DanielBlack: OSDC-2009 Not-Another-Damn-Password - OSDC Programme 2009 November 25-27
Apache Server Client Certificate Authentication
by DanielBlack
|
This article assumes that you have downloaded the CAcert root certificates to root.crt and class3.crt for Apache. However, you download new CAcert root certificates as root_X0F.crt or class3_x14E228.crt, where the number after X is the hex sequence number of the new CAcert root certificates (15 and 14). The easiest way is to rename these downloaded files with new root certificates to the original names listed in the following article. |
|
Apache client side authentication is based off the httpd mod_ssl documentation and has been deployed for a number of CACert systems like lists and webmail (for staff). Apache configurations for client side authentication should appear in a VirtualHost directive though they can exist under other directives like Location. These directives are in addition to SSL server configuration though I tend to use SSLCACertificatePath and not use SSLCertificateChainFile.
Basic Client Side Authentication
This is for the case we want a preposition of the website to be accessible by certificate only. In this case any certificate from a set of CA's.
## Client Verification SSLVerifyClient optional SSLVerifyDepth 3 SSLCADNRequestPath /usr/share/ca-certificates/cacert.org/ # error handling RewriteEngine on RewriteCond %{SSL:SSL_CLIENT_VERIFY} !=SUCCESS RewriteRule .? - [F] ErrorDocument 403 "You need a client side certificate issued by CAcert to access this site"
SSLCADNRequestPath contains a path of the certificates that you will accept for this site. This will need to be in the openssl format contain links from the subject_hash to the file like follows. Openssl packages contain a rehash or c_rehash script that can generate these using a command c_rehash /usr/share/ca-certificates/cacert.org/.
drwxr-xr-x 2 root root 4096 2009-05-14 23:22 . drwxr-xr-x 9 root root 4096 2007-05-16 23:12 .. lrwxrwxrwx 1 root root 8 2009-05-14 23:22 5ed36f99.0 -> root.crt -rw-r--r-- 1 root root 2151 2007-03-04 05:23 class3.crt lrwxrwxrwx 1 root root 10 2009-05-14 23:22 e5662767.0 -> class3.crt -rw-r--r-- 1 root root 2569 2007-03-04 05:23 root.crt
Note I have made SSLVerifyClient optional. This is because the error message when SSLVerifyClient required and a person without a certificate installed access the site is rather unintuitive(firefox request to improve). The simple Rewrite directives at the bottom mean that a forbidden page with that error as per ErrorDocument. You will need mod_rewrite installed and enabled to use this.
Specific Certificates allowed - by List
In addition to those directives above:
SSLOptions +FakeBasicAuth SSLRequireSSL AuthName "Admin Only Area" AuthType Basic AuthUserFile /var/www/.htpasswd require valid-user
The /var/www/.htaccess is like:
/CN=Daniel Black/emailAddress=daniel@cacert.org:xxj31ZMTZzkVA
The password bit xxj31ZMTZzkVA is always the same. The first bit is obtained by openssl x509 -noout -subject -in certificate.crt where certificate.crt is the certificate that you want to give access to.
Specific Certificates allowed - Expression
Sometime you want to say - yes accept any certificate from CAcert that has an email of @example.com and not worry about maintaining long lists. This is possible as follows:
SSLRequire %{SSL_CLIENT_S_DN_Email} =~ m/^[^@]*@example\.com$/
A full list is here http://httpd.apache.org/docs/trunk/mod/mod_ssl.html#sslrequire. Sometime certificates can contain more that one email so:
SSLRequire %{SSL_CLIENT_S_DN_Email} =~ m/^[^@]*@example\.com$/ or %{SSL_CLIENT_S_DN_Email_0} =~ m/^[^@]*@example\.com$/ or %{SSL_CLIENT_S_DN_Email_1} =~ m/^[^@]*@example\.com$/ or %{SSL_CLIENT_S_DN_Email_2} =~ m/^[^@]*@example\.com$/ or %{SSL_CLIENT_S_DN_Email_3} =~ m/^[^@]*@example\.com$/
You should change your error message (above) to say that certificates for @example.com are required also.
Logging
The standard apache combined log file has a field for username, however using client certificates doesn't utilise this. Keeping the log in the same format however is handly if you every want to analysis it without customing analysis software.
To do this use the CustomLog using the combined log format replacing %u with %{SSL_CLIENT_S_DN_Email}x for an email address (or any other SSL_CLIENT* variable you may find useful.)
Web Application Authentication
A number of web application can use the REMOTE_USER environment variable to provide access control to areas of the web application. These web application normally will describe the usage of this feature with the Apache Basic or Apache Digest authentication. You can use SSL certificates here.
How you do this is using the SSL option SSLUserName followed with a username environment variable. SSL_CLIENT_S_DN_Email is a useful though it depend on the web application and the users if having an email as a username is acceptable.
CustomLog /var/log/apache2/ssl_access.log "%h %l %{SSL_CLIENT_S_DN_Email}x %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-agent}i\""
Revoked Certificate Checking
OCSP can be used to check if certificates have been revoked. Only versions of Apache after 2.3 are able to check this for you OCSPEnable.
If you use Apache 2.2 or lower you will have to use CRLs to do the revocation checking because it does not support OCSP. To do that you have to set up a cron job that downloads the current CRLs and tell Apache to use them:
Create a directory where the CRLs get stored into. A good place is usually /var/local/ssl/crls
Set up the cron job that does the downloading. I have prepared a shell script that you can just put into /etc/cron.hourly (or daily or whatever). The script requires rsync, the c_rehash utility from openssl and relies on service apache2 reload to reload the Apache configuration. Adjust it to your needs if you have a setup that doesn't fulfil these dependencies.
- Put the following into your Apache config:
SSLCARevocationPath /var/local/ssl/crls/
- Manually run the cron job script for the first time which will also reload the Apache configuration
- Check if everything works correctly
Inputs & Thoughts
20100524-UlrichSchroeter
Win32 (w/ Cygwin) implementation details: 1.) The Openssl rehash nor the c_rehash nor the c_rehash perl script doesn't work for me. Also Sysinternals Junction or other solutions doesn't do what expected. After 2 days of experiences I've stopped playing around to find a workaround and decided to do the 2 essential parts of the scripts manualy from the DOS prompt: $>openssl x509 -noout -hash <root.crt noted the hash result and added to the next Cygwin ln program command line $>ln --symbolic --no-target-directory root.crt 5ed36f99.0 ^^^^^^^^ $>openssl x509 -noout -hash <class3.crt noted the hash result and added to the next Cygwin ln program command line $>ln --symbolic --no-target-directory class3.crt e5662767.0 ^^^^^^^^ note: Cygwin's 'ln' command has another usage syntax. 2.) The SSLRequire doesn't work for me under Apache/2.2.11 (Win32) mod_ssl/2.2.11 OpenSSL/0.9.8e ... The apache conf syntax SSLRequire %{SSL_CLIENT_S_DN_Email} =~ m/^[^@]*@example\.com$/ or %{SSL_CLIENT_S_DN_Email_0} =~ m/^[^@]*@example\.com$/ or %{SSL_CLIENT_S_DN_Email_1} =~ m/^[^@]*@example\.com$/ or %{SSL_CLIENT_S_DN_Email_2} =~ m/^[^@]*@example\.com$/ or %{SSL_CLIENT_S_DN_Email_3} =~ m/^[^@]*@example\.com$/ needs to be changed to: SSLRequire ( (%{SSL_CLIENT_S_DN_Email} in {"alias@example.com"}) or (%{SSL_CLIENT_S_DN_Email} in {"alias2@example.com"}) ) all in one row, parts included in brackets (), otherwise i've ever got service start exceptions: "The Apache service terminated with service-specific error 1" or connection failures: "failed, reason: SSL requirement expression not fulfilled" 3.) in http.conf I've added the basic definitions for an Alias: Alias /users/ "D:/Apache/Apache2/wwwroot2/users" <Directory "D:/Apache/Apache2/wwwroot2/users"> SSLOptions +FakeBasicAuth +StdEnvVars +ExportCertData Options Indexes MultiViews AllowOverride None Order allow,deny Allow from all </Directory> and with the ssl.conf inclusion I've added the SSLrequire statement under a location section: <Location /user1/> SSLRequire (( ( %{SSL_CLIENT_S_DN_Email} in {"alias@example.com"} ) or ( %{SSL_CLIENT_S_DN_Email} in {"alias2@example.com"}) ) and ( %{SSL_CLIENT_V_REMAIN} > 0 ) and (( %{SSL_CLIENT_I_DN_CN} in {"CA Cert Signing Authority"}) or ( %{SSL_CLIENT_I_DN_CN} in {"CAcert Class 3 Root"}) )) </Location> The SSLRequire statement have to be all in one line (!) (see top 2) The SSL_CLIENT_V_REMAIN checks the client cert if its not expired. The SSL_CLIENT_I_DN_CN checks the CAcert's root and class3 issuer statements in the client cert. regards, uli ;-)
20110521-u60
script to run once a week or once every 2 weeks (with cygwin installed) wget -nc http://crl.cacert.org/revoke.crl wget -nc http://crl.cacert.org/class3-revoke.crl rem for all 0 files delete old hash 0 files for %%a in (*.r0.lnk) do attrib -R %%a for %%a in (*.r0.lnk) do del %%a openssl crl -in revoke.crl -inform DER -out revoke-cacert_root_now.crl -outform PEM openssl crl -noout -hash<revoke-cacert_root_now.crl>temphash.tmp set /p hash= <temphash.tmp ln --symbolic --no-target-directory revoke-cacert_root_now.crl %hash%.r0 openssl crl -in class3-revoke.crl -inform DER -out revoke-cacert_class3_now.crl -outform PEM openssl crl -noout -hash<revoke-cacert_class3_now.crl>temphash.tmp set /p hash= <temphash.tmp ln --symbolic --no-target-directory revoke-cacert_class3_now.crl %hash%.r0 ## don't forget to restart the apache daemon / service
20110527-u60
To check at application level with php on top of apache, you have to do some php coding. An example is given in following article:
20110602-u60
Configuration for a mixed Client Cert required / No Client Cert required environment 1. configure your https apache server to not require client certs by default, except special areas /secure/ Client Cert required area /unsecure/ Area where no client cert is required, also to place a landing page /other/ if other directories should be secured with Client Cert required, add a httpd.conf <directory ..> directive like for /secure/, add a .htaccess like below /secure/.htaccess sample httpd.conf ......................................... [...] # default port 80 configuration /secure/.htaccess <Directory .../WWWROOT/secure/> ...................................... # #Redirect permanent RewriteEngine on # from http://../secure/.* RewriteCond %{SERVER_PORT} !^443$ # to //../unsecure/index.php RewriteRule ^(.*)$ /unsecure/index.php [R=301,L] AllowOverride All ...................................... </Directory> [...] <IfModule mod_ssl.c> Include conf/ssl.conf </IfModule> [...] ......................................... ssl.conf ......................................... <VirtualHost _default_:443> SSLEngine on SSLVerifyDepth 3 #Server keys SSLCertificateFile /Apache/Apache2/conf/ssl.crt/your-pub-server-key.pem SSLCertificateKeyFile /Apache/Apache2/conf/ssl.key/your-private-server-key.pem #Server keyfile verification against CA SSLCertificateChainFile /Apache/Apache2/conf/ssl.crt/cacert-chain.pem # Client Cert verification pre-definition, that is required in later <directory ..> directives SSLCACertificateFile /Apache/Apache2/conf/cacert.org/root.crt SSLCACertificatePath I:/Apache/Apache2/conf/ssl.vrf/ SSLCADNRequestPath /Apache/Apache2/conf/ssl.vrf/ # Certificate Revocation Lists (CRL): SSLCARevocationFile /Apache/Apache2/conf/ssl.crl/revoke-cacert_root.crl SSLCARevocationFile /Apache/Apache2/conf/ssl.crl/revoke-cacert_class3.crl # EOT default definitions # NO CLIENT CERT REQUIRED <Directory /unsecure/> ### NO Client Verification SSLVerifyClient none </Directory> # CLIENT CERT REQUIRED <Directory /Apache/Apache2/WWWROOT/secure/> ## SSLRequireSSL ### Client Verification SSLVerifyClient require SSLVerifyDepth 3 #SSLCADNRequestPath /Apache/Apache2/conf/ssl.vrf/ see pre-configuration SSLRequire ( ( %{SSL_CLIENT_V_REMAIN} > 0 ) and (( %{SSL_CLIENT_I_DN_CN} in {"CA Cert Signing Authority"}) or ( %{SSL_CLIENT_I_DN_CN} in {"CAcert Class 3 Root"}) )) ### error handling RewriteEngine on RewriteCond %{SSL:SSL_CLIENT_VERIFY} !=SUCCESS RewriteRule .? - [F] ErrorDocument 403 "You need a valid client certificate from CAcert.Org to access this page on this site." </Directory> </VirtualHost> ......................................... php scripts /unsecure/index.php ......................................... [...] <script language="javascript" type="text/javascript"> <!-- function $(id) { return document.getElementById(id); } // --> </script> [...] Description/Warning: You need a valid client cert installed in your browser to continue with client cert login [...] <form action="https://../secure/login.php" method="post" id="login"> <input type="submit" value="Cert Login"> <button onclick="$('login').action='/unsecure/cancel-infopage.html';$('login').submit();">Cancel</button> </form> [...] ......................................... /secure/login.php ......................................... <?php $user = $_SERVER["SSL_CLIENT_S_DN_CN"]; $email = $_SERVER["SSL_CLIENT_S_DN_Email"]; $server = $_SERVER["SSL_SERVER_S_DN_CN"]; $clisscert = $_SERVER["SSL_CLIENT_I_DN_CN"]; if ($_SERVER["SSL_CLIENT_VERIFY"]!="SUCCESS") { header ("Location: /unsecure/index.php"); exit; } else { ?> [html code goes here] [...] <h2>Successful Client Cert Login</h2> <p>Hello <?php echo $user; ?>,<br> You've successfuly logged in to <?php echo $server; ?><br> with your <?php if ($clisscert=="CAcert Class 3 Root") { echo $clisscert." (Class3 subroot)"; } else { echo $clisscert." (Class1 root)"; } ?> issued client cert.<br> <br> This page you can only read if successfuly authenticated with a valid client cert.<br> So now you have access to client cert enabled, restricted areas.<br> Feel free to continue your journey on this website ...</p> </body> </html> <?php } ?> .........................................
YYYYMMDD-YourName
Text / Your Statements, thoughts and e-mail snippets, Please