Warning

This document hasn't been checked for compatibility with current versions of Plone. Use at your own risk.

Single Sign On In Windows Domains

by Hannes Schmidt last modified Sep 04, 2012 03:13 PM
This HowTo tells you how to integrate Zope, Plone and Apache in a Windows domain such that domain users are automatically logged into Plone. It's also a tutorial about getting Zope/Plone to run behind an Apache web server using FastCGI.

The original of this HowTo can be found at Diary Products.

Single-Sign-On for Zope & Plone in Windows Domains: SSO
Single-Sign-On for Zope & Plone in Windows Domains: Remote User Folder

One of my clients runs his own intranet for his employees. The intranet currently consists of a bunch of static HTML pages which are written and maintained by a single editor. This approach might have been sufficient five years ago but as the intranet grew it became apparent that the static HTML would become unmanageable in the near future. I therefore suggested replacing the existing solution with a community-oriented CMS. Due to certain requirements the set of contestants was cut down to two: Drupal and Plone. I favored Plone because a) I am slightly disappointed with the direction and pace that Drupal’s development has recently taken and b) I wanted to learn Zope and Plone anyway.

Table Of Contents

State Of The Art
Step 1 - NTLM authentication for Apache: mod_ntlm
Step 2 - Zope/Plone and Apache
Step 3 - FastCGI
Step 4 - Enable NTLM authentication for requests to Zope
Step 5 - Install Remote User Folder
Appendix 1 - Exempting the ZMI from NTLM authentication
Appendix 2 - Schizophrenia
Appendix 3 - Disabling Zope's http server
Appendix 4 - Virtual Host Monster (VHM)
Appendix 5 - What about Group User Folder?
Appendix 6 - Problems with mod_ntlm under load
Appendix 7 - Multiple Windows Domains
Appendix 8 - Kerberos

State Of The Art

The most important requirement was the integration of Plone’s user management into the client’s Windows domain. This requirement had two implications:

  1. Unified logon: Any domain user should automatically be a Plone user. There should not be any additional user administration in Plone.
  2. Seamless authentication: Users should only have to logon once. After a user is logged into the domain, Plone should not ask for a password a second time.

The above requirements are usually summarized as single-sign-on. This article describes how single-sign-on can be achieved by gluing together various pieces of software in addition to Zope/Plone; namely, Apache, mod_ntlm, mod_fastcgi, a Samba or Windows 2000 Server and Zope’s Remote User Folder.

In coming to my conclusions I examined several possible solutions:

  • LDAPUserFolder could be used to fulfill the unified logon requirement by integrating Active Directory (Microsoft’s extended LDAP directory implementation for Windows domains) with Plone. However, with LDAPUserFolder the user would need to login into the CMS separately. This is basically because there is no trust relationship between the Zope Server that Plone runs on and the Windows workstation that the client logs into. Currently, such trust can only be established using Microsoft’s own proprietary HTTP authentication protocol, called NTLM authentication. NTLM auth was initially supported by Internet Explorer (IE) and Microsoft’s web server, Internet Information Server (IIS). The NTLM authentication scheme is sneered at by many because it’s thought to be rather weak (I remember reading somewhere that there is a stronger Kerberos extension to it). Notwithstanding, it has been adopted by a variety of open source projects, probably because of user demand and usefulness. The Squid proxy server supports it, Firefox 1.0 supports it and for Apache there is mod_ntlm. I successfully used all of these in conjunction with NTLM authentication in my client’s intranet. The security risk imposed by NTLM authentication in a controlled LAN environment is acceptable.
  • The Plone product exUserFolder can authenticate against a windows domain but it doesn’t support NTLM authentication either.
  • The Plone product PluggableAuthService (PAS) looks very promising but I doubt that at the time of this writing it’s ready for production or that it even supports NTLM auth. There were posts on the corresponding mailing list that indicate someone is working on NTLM auth though.

This leaves us with one remaining option: Running Zope behind Apache with mod_ntlm installed. The remainder of this article focuses on the details of how to get Apache 2.0 to do the authentication and yet let Zope serve the content for the Plone CMS. The procedure for Version 1.3 of the Apache web server should be very similar but is not addressed in this article.

Step 1 – NTLM authentication for Apache: mod_ntlm

Update, Feb 1st, 2005: Read this section and then immediately read Appendix 6!

Apache’s mod_ntlm is available for Apache 1.3 and 2.0 and although still in beta it is pretty much usable as long as you use it in one domain only. I prefer loading it as a module, which is the default build setting. Download the modntlm2 source package [3] and compile and install it according to the instructions found either on the home page [4] or in the package itself. The compilation requires the Apache header files. The installation uses the apxs program to install the compiled module but that didn’t work on my Suse Linux system. If this is the case for your system too, there are two things you will have to do. First, you will need to copy the mod_ntlm.so file to the directory that contains the other Apache modules, e.g /usr/lib/apache2. Looking into httpd.conf for LoadModule statements of other modules should give you an idea.

LoadModule ntlm_module /usr/lib/apache2/mod_ntlm.so

The bold part will probably need to be adjusted to match your system’s module path. Restart Apache to see if it starts up correctly. Then add this to your httpd.conf:

<Location /ntlmtest>
AuthType NTLM
NTLMAuth on
NTLMAuthoritative on
NTLMDomain YOURDOMAIN
NTLMServer yourDomainController
NTLMBackup yourBackupDomainController
Require valid-user
</Location>

Instead of putting the above statements into a <Location> section, you can also put them into an existing <Directory> section. Point your browser to /ntlmtest on your server (http://yourserver/ntlmtest) or to the directory to which you added the above http.conf statements. If you get a password prompt or an “Authentication Required” error page, something isn’t working. If you get an “Object Not Found” 404-error page, that’s good. Note that you must use Mozilla Firefox 1.0 for Windows or Internet Explorer when you test this and that you have to be logged on as a domain user.

Firefoxies, read this: In order to use NTLM auth on direct HTTP connections (as opposed to proxy connections that support NTLM out of the box) you need to add your server to the list of trusted URIs. To do so, start Firefox, type about:config into the address text field, find the setting network.automatic-ntlm-auth.trusted-uris, double-click it and enter http://yourserver (no slash at the end). This might seem tedious but for security reasons it’s a good idea to limit the servers that a user agent exchanges NTLM challenges with because NTLM auth is susceptible to man-in-the-middle attacks. Better safe then sorry. If you want to be sorry, you can set the trusted URIs setting to http:// (two slashes at the end).

Also, check your server access logs (usually somewhere in /var/log) and see whether your test requests were logged with your user name. If not, something is not working properly or your log format is non-standard (unlikely). You need to fix this before you continue.

If you haven’t done so yet, you should install Zope and Plone now and setup up your Plone site. Don’t add any users yet. When install a Zope instance, you need to specify the credentials of the instance manager. The manager should be the only user available in Zope and Plone.

Step 2 – Zope/Plone and Apache

OK, we got NTLM authentication working and we have a functioning Plone and Zope installation. What’s left is chaining Apache and Zope such that

  • Apache forwards the requests to Zope,
  • Apache passes the authenticated user’s name to Zope and
  • Plone accepts that user as a member of the Plone site.

There are plenty of ways to run Zope behind Apache:

  • CGI -- For every request, Apache runs the python binary, passing the request headers in environment variables and the request body in a pipe (I think). The Zope response is sent back through a pipe. After this, the Zope process finishes. Extremely slow and not recommended.
  • FastCGI – Apache creates a socket connection to an already running Zope and passes the request info through that connection. Zope handles the request and sends the response back through that connection. The connection is closed and re-opened for every request but the Zope process continues to run. Much faster. Additional environment variables can be transferred through the socket connection.
  • mod_proxy alone – This is conceptually similar but technically different from FastCGI. Apache works as a proxy and forwards the HTTP requests to Zope. This method is as fast as FastCGI. No environment variables can be passed along with the forwarded request. The URLs that Apache receives have the same postfix as the ones that Apache sends to Zope but the prefix can be different.
  • mod_rewrite together with mod_proxy – Very similar to the previous solution but it allows for rewriting the URLs.

Although all of the above techniques have pros and cons, there is one killer requirement that rules out all but one technique: transferring the authenticated user from Apache over to Zope. So before we investigate how we are going to connect Apache and Zope, let’s have a look at the Zope authentication and permission system.

Disclaimer: I am more or less a Zope newbie so from here on out we will be moving into unknown territory. Don’t take everything I say for granted but don’t lose your faith either – the solution described in this article does actually work!

Zope (and hence Plone) organizes users in special folders called user folders. When added to an object, the user folder determines the available set of users for this object. Generally speaking, if a user folder is added to the Plone object, only the users in that user folder can access the Plone site. Well, not quite. Firstly, the Plone site can also be accessed by users of user folders further up in the hierarchical Zope database. For example, the users in the acl_users object at the Root Folder are granted access to the Plone site as well. Secondly, the users can be assigned Roles that further restrict the operations available to the users.

Different authentication mechanisms are added to Zope by adding specialized types of user folders to a Zope instance. The default user folder is acl_users at the Root Folder of any vanilla Zope instance. Another type of user folder is LDAP  user folder. This user folder type doesn’t store the users in the Zope database but in an LDAP directory such that LDAP user entries appear as Zope users. I also want to mention GRUF (Group User Folder) as it is a very common user folder for Plone sites. (See Appendix 5 – What about Group User Folder? )

Another type of user folder, the so-called Remote User Folder is of particular interest to us. It doesn’t contain any persistent user objects at all. Instead, it assumes that authentication has already been completed for an incoming HTTP request and it looks up the request’s authenticated user in its own list of users. A Remote User Folder obtains the name of the authenticated user from the REMOTE_USER environment variable. As mentioned earlier, environment variables are not part of a standard HTTP request. For this reason we cannot use either of the two mod_proxy solutions because mod_proxy uses HTTP only. We’ll have to stick to either CGI or FastCGI.

If the user isn’t present, a Remote User Folder will either reject the user (this is the default setting) or create a matching user entry automatically, provided that this feature is enabled. The on-the-fly creation of users is particularly helpful for our purposes, as it completely delegates authentication and the user management to some other instance. Finally, we have all the ingredients:

  1. The browser sends a request to the Apache web-server together with an invitation for NTLM authentication of the currently logged on user
  2. Apache receives the request
  3. The ntlm_auth module authenticates the request by mediating the NTLM challenge/response handshake between the user’s browser and the domain controller
  4. Once the user is authenticated, Apache dispatches the request to the Zope server, using the FastCGI protocol and including the authenticated user’s name in the REMOTE_USER environment variable
  5. Zope receives the request and - based on the path of the requested object – passes the request to the appropriate user folders for authorization
  6. The Remote User Folder obtains the user’s name from REMOTE_USER, scans its user list for that user name and adds an entry for the user if it is not yet present. Depending on the type of the requested object and the roles assigned to the user, the request is either denied with a 401 response or granted and executed.

Although this all sounds very lengthy, it is in fact rather easy to achieve.

Step 3 – FastCGI

Download mod_fastcgi package from the FastCGI homepage and follow the instructions in the INSTALL file (or INSTALL.AP2 for Apache 2). Compiling and installing FastCGI was a bit of headache for me because FastCGI uses Apache’s build system and I didn’t have a proper set of Apache headers and makefiles. My Apache installation is a backport RPM for my ancient Suse 7.1 so I think my difficulties are more the packager’s fault than FastCGI’s.

LoadModule fastcgi_module /usr/lib/apache2-prefork/mod_fastcgi.so
FastCgiExternalServer /usr/local/httpd/htdocs/zope -host localhost:8081 -pass-header Authorization
<Location /zope>
SetHandler fastcgi-script
</Location>

The FastCgiExternalServer statement tells FastCGI which request paths should be handled by mod_fastcgi. I’m not sure why it’s specified as a file system path instead of a URL path but the file name is just a dummy and the file does not need to exist. You can choose any other name instead of zope but if you do that, you’ll need to adjust the following instructions accordingly. The Location statement tells Apache that requests to /zope are handled by FastCGI.

The -pass-header option tells Apache to send authorization information along with the requests. This doesn’t affect NTLM authentication because environment variable REMOTE_USER will be set regardless of this option. It only affects whether standard HTTP authentication headers will be handed over to Zope, thus enabling us to use the Zope management interface (ZMI) through Apache (see Appendix 1 – Exempting the ZMI from NTLM auth and Appendix 2 – Schizophrenia).

The -host option denotes the host name and port number to which FastCGI request will be sent. We need to enable Zope’s FastCGI server and configure it to listen on the given port. Add the following lines to the zope.conf configuration file which is usually located in the Zope instance’s /etc directory.

<fast-cgi>
address localhost:8081
</fast-cgi>

Restart Apache and the Zope instance.

Step 4 – Enable NTLM authentication for requests to Zope

You already tested NTLM auth. in Apache by adding a dummy /ntlmtest location section to Apache’s configuration file. Edit http.conf one more time and modify the <Location /ntlmtest> section we added earlier:

<Location /zope/intranet>
AuthType NTLM
NTLMAuth on
NTLMAuthoritative on
NTLMDomain YOURDOMAIN
NTLMServer yourDomainController
NTLMBackup yourBackupDomainController
Require valid-user
</Location>

It is important that the intranet component of the location path matches the identity of the Plone site object. You can verify the identity using the ZMI.

Also note that we now have two Location sections: one with FastCGI for /zope and one with NTLM for /zope/intranet. Because /zope is a prefix of /zope/intranet, FastCGI and NTLM auth will be active for /zope/intranet. (For an explanation of why we do not use NTLM auth for /zope see Appendix 1 – Exempting the ZMI from NTLM auth)

Restart Apache. For the remainder of this article I will assume that you know the basics about ZMI, like creating and deleting objects or changing object properties. (In case you don’t, there is plenty of information about ZMI on the web.)

Step 5 – Install Remote User Folder

  1. Download and install the Remote User Folder product in your Zope instance directory.
  2. Point your browser to the ZMI and log in as manager. Use Zope’s HTTP port (default is 8080) because we are not quite ready to connect via Apache/FastCGI.
  3. Remove the existing user folder from the Plone site object.
  4. Add a Remote User Folder to the Plone site object. It will be called acl_users. Make sure to add it to the Plone site, not the Root Folder.
  5. Enable automatic creation of user objects in Plone. This option is called Auto  Add Users and can be found on the property tab of the acl_users folder that we just created.

Appendix 1 – Exempting the ZMI from NTLM authentication

In Step 3 – FastCGI ) and Step 4 – Enable NTLM authentication for requests to Zope ) we used distinct location sections, one for FastCGI and one for NTLM auth. We did this for one main reason: Although Apache wraps the entire Zope instance, NTLM auth should only be performed for URLs that point to the Plone site. It’s important that /zope and especially /zope/manage are exempt from NTLM auth because we do not want to lock out the Zope manager. The Zope manager should be able to login using standard HTTP authentication. Furthermore, there might be other Zope objects beside your Plone site for which NTLM auth would not be appropriate.

Theoretically speaking, it should be possible to add the Remote User Folder to the Zope instance root instead of the Plone site and to enable NTLM auth for the entire Zope instance but there’s a caveat. I wasn’t able to replace the default acl_users folder with a different one, say Remote User Folder (RUF) because after removing the acl_users folder, I couldn’t do anything at all because the manager (me) was locked out. There may be a trick to get around this, though.

Appendix 2 – Schizophrenia

When accessing the ZMI through Apache/FastCGI, you will notice that you’ll still have to enter the credentials of the manager. When you click on the intranet icon representing the Plone site in the tree view on the left hand side frame, you may notice that you turn from Zope manager into the Plone user that corresponds with your Windows domain account. This is because the Plone part of the ZMI also uses the /zope/intranet URL prefix for which NTLM authentication is enabled in Apache. For requests to this URL the browser sends NTLM authentication headers instead of HTTP authentication. I’m not quite sure as to why NTLM authentication overrides HTTP authentication, as there clearly is a conflict between the two. If your domain account is not assigned the Manager role in Plone, you will not get the management view of the Plone folder. Instead you will get the Plone home page. In order to fix this you need to connect to the ZMI directly via Zope’s HTTP port and assign the Manager role to the user object in Plone’s acl_users folder that corresponds to your Windows domain account.

Appendix 3 – Disabling Zope’s http server

Unless you already disabled the HTTP server in zope.conf, your Zope instance is listening on both the FastCGI and the HTTP ports. Using HTTP you should still be able to access everything in Zope including the management interface and excluding the Plone site. But because a Remote User Folder depends on the REMOTE_USER environment variable, access to the Plone site is only possible via FastCGI and thus through Apache. I recommend disabling the HTTP port in zope.conf because we don’t need it. The entire Zope site is accessible through Apache and FastCGI. Because HTTP is a more direct and robust way of connecting to your Zope instance, it might be necessary to temporarily enable the HTTP port for maintenance and debugging.

Appendix 4 – Virtual Host Monster (VHM) …

… is not needed apparently. VHM or SiteRoot objects are used whenever Zope runs behind something that disguises Zope’s internal URLs. VHM and SiteRoot make sure that URLs generated by Zope are mapped to the external URL format. Although this also applies to the solution described here, it seems that the URL mapping is taken care of by Zope’s FastCGI server.

Appendix 5 – What about Group User Folder?

Group User Folder (GRUF) can store users and groups. Instead of maintaining its own list of users and groups, it delegates the user and group storage to any number of user folders of any type. In that respect it is some kind of compound user folder, because it contains other user folders. The contained user folders are called sources. At the time of writing, Remote User Folder and GRUF are not compatible, i.e. Remote User Folder cannot be used as a source for GRUF. Stay tuned for updates on this issue …

Appendix 6 - Problems with mod_ntlm under load

People have reported that repeatedly refreshing the page causes a basic authentication dialog to appear. I initially though that it was a browser problem so I tested both Firefox and Internet Explorer. I was able to reproduce that problem under both browsers. The forum and bugtracking area sourceforge project page contains numerous posts describing the same symptoms, so the problem must be in mod_ntlm. In this post, Michael Cai and Jamie Kerwick announce their own improved version of mod_ntlm and mod_ntlm2 [8], which supposedly fixes this and other issues. I tried it out and was not able to reproduce the problem anymore. The bottom line is that you shouldn't use the official version. Instead, use Michael Cai's unofficial MOD_NTLM Apache module.

Sidenote: The mod_ntlm project managers seems to have abondened their baby. The last sign of life from them was spotted in May 2004, announcing a new version to be due "in a few days". Since then, various people have attempted to fix/rewrite mod_ntlm (see the project forum [3]).

Appendix 7 - Multiple Windows Domains

The mod_ntlm version described in Appendix 6 has support for multiple domains. If anybody has tested this feature, I'd appreciate it if they could contact me.

Appendix 8 - Kerberos

Andrew Bartlett has rewritten mod_ntlm_winbind and he claims that it's coded more cleanly than mod_ntlm and that

"It is also a very good base to add a SPENGO (Negotiate) module, that accepts NTLMSSP as well as kerberos".

There is no official homepage and it may only work with Samba3. You're on your own. Let me know if you succeed.

Resources

  1. Zope.org
  2. Plone.org
  3. mod_ntlm Sourceforge project page
  4. mod_ntlm project home page
  5. FastCGI.com - The FastCGI home page
  6. Remote User Folder
  7. Group User Folder (GRUF)
  8. Unofficial MOD_NTLM Apache Module
  9. mod_ntlm_winbind back alive

Contribute

Something wrong or out of date? Anybody can edit or create a new article in the knowledge base. Simply create an account on this site, log in, and click the Edit button to contribute.