Minimal Single Sign On for Plone using Kerberos
Motivation
This document has been written during a SSO implementation for Plone on a Windows 2003 domain, so that the credentials used for logging in to the Windows domain are also used for logging in to Plone. This allows for seamless integration of a Plone intranet site for users. Most of the steps taken in this process were taken from the howto Single Sign On with Active Directory, but the process described here is somewhat simpler. Also, the alternative method of using NTLM is left out, since NTLM is deprecated for Windows domains, and the software needed for NTLM is abandoned.
Assumptions
The setup is based on a Windows 2003 domain, using Active Directory (ADS) for authentication/authorization. The clients used are also Windows clients (using Citrix), using both FireFox 3.x and Internet Explorer 7. The server used for Plone is a Suse Linux 11.2, but any Linux distribution should have more or less the same packages and setup. Apache 2.x is used as a front-end for Plone. All FireFox, Chrome and IE browsers support SSO settings officially.
It is assumed you have set up Plone using buildout, and have it running on the same machine as the Apache server, on port 8080. Your Plone site within Zope has the id 'plone'.
Our example setup:
- Windows domain
- EVILEMPIRE.LAN
- intranet site
- intranet.evilempire.lan
- Windows domain controller (primary)
- dc01.evilempire.lan
- Windows domain controller (backup)
- dc02.evilempire.lan
How does it actually work?
The SSO process works as follows, given that a user has logged in to her/his machine and navigates to the intranet site on http://intranet.evilempire.lan/: Apache decides that this site/location is restricted access and uses the Apache Kerberos authentication module to negotiate with the client how access can be gained. The client (FireFox or IE) receives a challenge for authentication, and sends the credentials of the currently logged in user. The Apache Kerberos module checks the credentials with ADS (Kerberos server) and if ok, sets the username into an environment variable, before proxying the request to Plone. Plone recieves the request and can, using the WebServerAuth module, retrieve the credentials from the environment variable, and tell Plone you are logged in.
Implementation phases
The process can split into smaller parts, that enable testing of each step. The parts are:
- enable Kerberos on the webserver machine;
- enable Kerberos authentication for Apache;
- enable webserver authentication for Plone;
- enable SSO with the logged in Windows user.
Enable Kerberos on your server
To enable Kerberos on the server, you'll need the Kerberos libraries to be installed. These are in the krb5 package and may already be installed. For testing purposes some handy client side tools are available as well. Install this package: krb5-client. Configure /etc/krb5.conf as per your needs. In the example case, it would be something like
[libdefaults]
default_realm = EVILEMPIRE.LAN
clockskew = 300
[realms]
EVILEMPIRE.LAN = {
kdc = dc01.evilempire.lan
kdc = dc02.evilempire.lan
default_domain = EVILEMPIRE.LAN
}
[logging]
kdc = FILE:/var/log/krb5/krb5kdc.log
admin_server = FILE:/var/log/krb5/kadmind.log
Note the clockskew statement... For any server to be able to authenticate against a Kerberos server, the system time of the client should really not be to far off the system time of the Kerberos server. It is convenient to make sure this is the case, by using the ntp daemon (package ntp), and using the Kerberos server as the master time server. Add these lines to the ntp.conf file:
server dc01.evilempire.lan prefer server dc02.evilempire.lan
and comment out any other servers.
Make sure you have a user account in the ADS. Let's say the account name is 'plone'. You can now test your Kerberos setup using the 'kinit' tool. Calling it like this:
kinit plone
you should be asked to type your password, and on success Kerberos will grant you a ticket. You can check whether you actually got a ticket with the klist command. It should give you output like this:
Ticket cache: FILE:/tmp/krb5cc_1000 Default principal: plone@EVILEMPIRE.LAN Valid starting Expires Service principal 03/24/10 14:32:55 03/25/10 00:32:58 krbtgt/EVILEMPIRE.LAN@EVILEMPIRE.LAN renew until 03/25/10 14:32:55
if all is well, you now have enabled calling Kerberos authentication from your server.
Enabling Kerberos for Apache
To enable Apache authentication using a Kerberos server (or ADS for that matter), you'll need to install the mod_auth_kerb module for Apache. For Debian, this module is available as libapache2-mod-auth-kerb, for SuSe/RedHat you can fetch the package from rpmbone. The package is called apache2-mod_auth_kerb-<version>.<arch>.rpm.
After installation, enable it by either manually making sure it gets loaded by Apache on startup (this is configured differently on SuSe and Debian), or using the command a2enmod that is distro independent and takes care of the proper configuration for your distro.
a2enmod mod_auth_kerb
This module gives you some directives to enable Kerberos authentication for your webserver. An example configuration is given for a virtualhost intranet.evilempire.lan:
<VirtualHost *:80> ServerAdmin webmaster@evilempire.lan ServerName intranet.evilempire.lan DocumentRoot /var/www/intranet.evilempire.lan <Directory "/var/www/intranet.evilempire.lan"> AllowOverride None Allow from all </Directory> <Location /> AuthType Kerberos AuthName "Kerberos Login" KrbMethodNegotiate On KrbMethodK5Passwd On KrbVerifyKDC Off # On KrbAuthRealms EVILEMPIRE.LAN Krb5Keytab /etc/apache2/krb5.keytab KrbServiceName HTTP/intranet.evilempire.lan@EVILEMPIRE.LAN Require valid-user </Location> </VirtualHost>
Last but not least, you'll need to generate a keytab file that holds the ticket that the Kerberos module will use to contact the Kerberos server, This can be created on the ADS machine with the following command:
ktpass -out c:\plone.keytab /princ HTTP/intranet.evilempire.lan@EVILEPMIRE.LAN /pass <your secret password> -mapOp set /ptype KRB5_NT_PRINCIPAL /mapUser plone
N.B.: the command is really one line, but hey, doesn't this read a bit easier? It is rather hard to find info on other ways to create the keytab file, but anyone that has more info is invited to add a comment, and I'll incorporate it into this howto.
You should now have a file called plone.keytab on your C drive. This file needs to be transported (safely) to your server, using scp, sftp or the likes. When you have the file on your server, name it to the location you have configured in the virtual host directive Krb5Keytab. Note: if you have more keytab files, you can merge them using the 'ktutil' command. This enables you to read multiple keytab files, and write as one. 'man ktutil' should give you the idea...
Now if you add this configuration to Apache, restart the web server, and navigate to this site, assuming you have provided a page called index.html, with the URL http://intranet.evilempire.lan/index.html, you should be challenged with a popup, saying 'Kerberos Login'.
Enable webserver authentication for Plone
Assuming you got this far, it is now time to enable the authentication that you have in place for Plone. This basically boils down to setting up the WebServerAuth module for Plone. In you buildout config file, add Products.WebServerAuth to the eggs section, rebuild and go to the ZMI, quickinstaller. Install WebServerAuth. Now navigate to the acl_users of your Plone. There should be a webserverauth plugin there. If not, pick it from the list of addable types. Depending on your setup, you may need to configure the webserverauth plugin, depending on the type of login names that are used in the Windows domain.
This plugin basically tells Plone that whatever user is given in the configured header (X_REMOTE_USER) is to be trusted by Plone. Please note that anyone can actually pass this header to any request, so this is a dangerous way of authentication if you don't secure the process!
So now the only thing left here, is to make sure that the user that logs in with the Kerberos authentication module is set in this header. For this you'll need to activate three more Apache modules (you know how to do that now...): rewrite, headers and proxy_http. (Please note that some distro's will not automatically also enable dependencies. For SuSe for instance, make sure you enable the proxy module as well, and before you enable proxy_http.)
Below is given an example configuration, using virtual host:
<VirtualHost *:80>
ServerAdmin webmaster@evilempire.lan
ServerName intranet.evilempire.lan
<Directory "/var/www/intranet.evilempire.lan">
AllowOverride None
Allow from all
</Directory>
<Location />
AuthType Kerberos
AuthName "Kerberos Login"
KrbMethodNegotiate On
KrbMethodK5Passwd On
KrbVerifyKDC Off # On
KrbAuthRealms EVILEMPIRE.LAN
Krb5Keytab /etc/apache2/krb5.keytab
KrbServiceName HTTP/intranet.evilempire.lan@EVILEMPIRE.LAN
Require valid-user
RequestHeader set X_REMOTE_USER %{remoteUser}e
</Location>
<Proxy *>
Order deny,allow
Allow from all
</Proxy>
RewriteEngine On
RewriteRule ^/(.*)$ http://localhost:8080/VirtualHostBase/http/intranet.evilempire.lan:80/plone/VirtualHostRoot/$1 [L,P,E=remoteUser:%{LA-U:REMOTE_USER}]
</VirtualHost>
The Proxy directive is somewhat odd, and so is the setting of the remoteUser variable, but there seem to be some problems with the remote user setting that are resolved this way...
Navigating to the intranet again, after restarting Apache of course, should now again give a popup, and should log you in to Plone using your Windows login name.
SSO
The last part is to automatically use your Windows login name for login to Plone, withou the popup. This is really only a matter of configuration of your browser. In the case of FireFox, open a tab, and type the url: about:config. Filter on 'network.nego'. Edit the 'network.negotiate-auth.trusted-uris' directive to include the 'evilempire.lan' domain.
In case of IE, goto Tools->Internet Options->Security. The setting you'll need depends a bit on the situation. If your intranet is recognized as such (see the icon on the browser footer), you need to change the settings for that, otherwise change the settings for internet sites. You may even add the site to trusted sites, but then you'll need https (you may want that anyway... read on!).
For the given class of your site, go to Custom Level->User authentication->Logon and select either 'Automatic logon only in Intranet zone' or 'Automatic logon with current user name and password'.
Restart IE (sure..!) and when navigating to the intranet site, you should automatically be logged in!
N.B. Make sure that the domain used for the Plone site is a primary domain in the DNS settings on your Windows domain, and that it is regarded as a 'trusted site' by your IE!
Further steps
The setup as described above completes the SSO part, but there is some more steps you may want to do:
- securing the setup
- adding ADS as back-end for Plone users
Security
The setup so far has two main security risks:
- the fallback for when SSO fails is still to use the Basic challenge with a popup. This causes the password of the user to travel over the network unencrypted. This may or may not be a problem in your situation.
- the X_REMOTE_USER can simply be added to any request to Plone. Since Plone assumes that this header contains the actual user, anyone could be logging in as 'admin' or anyone else. This may not be a problem to you, but it's hard to see that...
To make the setup more secure, it is recommened that you enable SSL for the intranet, and use it over HTTPS only. This howto will not describe how to do that, plenty others will...
To prevent abuse of the X_REMOTE_USER header, make sure that the intranet is only reachable through Apache (you can use a firewall for that).
ADS as user back-end
Since we're using ADS to authenticate to, we may as well use the same user database as back-end for Plone. For this you'll need Products.PloneLDAP. The process is fully described in Single Sign On with Active Directory. However, there's a few considerations. Make sure that you have the packages openldap2-client and openldap2-devel installed. The first is handy for testing, the latter is required for python-ldap, that is installed as a dependency for PloneLDAP.
If the LDAP client stuff is installed, check whether you can actually connect to the ADS:
ldapsearch -h dc01.evilempire.lan -s base -x -LLL -b "" "objectClass=*"
This should give you some info on the ADS root. At least you should get no error. You should determine the base for users in ADS in normal LDAP terms, which may take some time... Usually you'll have the domain as base, and then organisational units as sub containers. Your mileage may vary, but something along these lines: ou=Users,dc=evilempire,dc=lan.
Per default, anonymous access to ADS is not allowed any deeper than root level, so use your plone user to test access to the users:
ldapsearch -h dc01.evilempire.lan -x -LLL -D "plone@evilempire.lan" -W -b "ou=Users,dc=evilempire,dc=lan"
This should give you a password prompt, and after that a list of user details. The bind name (after '-D') depends on the ADS user name configuration. Please check with your sysadmin.
When configuring the ADS plugin, make sure your base property is syntactically correct, otherwise you'll get a nasty DECODE error thrown at you.
