Zope/Plone and Pubcookie

If you wish to use Pubcookie to provide authentication for Zope/Plone instances, you need to use a web front end for Zope, since Zope cannot directly use Pubcookie. This page explains how to use Apache as a front end. It is assumed you have already successfully set up Pubcookie and Apache, and also assumes you have a means of authorizing users. This example needs features in Apache 2.2, and is not designed to work completely with previous versions of Apache (it relies on being able to set incoming headers, as well as passing along the HTTP Host: header).

This document will also describe how to use Pubcookie in a way such that users can view your Plone sites anonymously, while still forcing logins to connect to the ZMI.

The method used is to make Apache a proxy server, passing authentication information to Zope. You can use Apache to authorize users who should be allowed to visit the Zope site, and further limit users within Zope. The flow of information is:

Apache/Zope flow diagram

It is important to understand that this method completely disables Zope authentication and replaces it with Pubcookie authentication. Zope will blindly accept any user passed to it by Apache, so you must make sure that Zope will not answer requests from anyone other than the local host, and that access to the server is restricted.

While Apache will be handling all authentication, it will handle none of the authorization outside of the main ZMI. Your Plone installations will need to allow/deny access as necessary.

The steps involved in setting up Apache and Zope are:

  1. Add Apache module to pass authenticated username to Zope.
  2. Enable proxy to local Zope instance.
  3. Make Apache configuration changes.
  4. Set up auth.
  5. Restart Apache.
  6. Add CACSiteRoot to Zope.
  7. Add Zope RemoteUserAuth to Zope.
  8. Add Zope managers
  9. Prohibit remote access directly to Zope.
  10. Restart Zope
  11. Create CACSiteRoot Object

Once you have your server set up, you will need to make some modifications to change the Plone login form to be a link to the login CGI.

Add Apache module

The first step is to add a module to Apache which will pass the username of the Pubcookie-authenticated user to Zope. Since Zope knows how to handle Basic Authentication, the Apache module fakes a Basic Auth header when it sends the proxied request to Zope. The source to this module is at http://rici.ricilake.net/src/mod_fba.c (local copy) and will compile for both Apache 1.3 and Apache 2.

Build mod_fba with DSO

The easiest way to use mod_fba is to make sure your Apache is installed with DSO support. You build and install the module with:

% apxs -c mod_fba.c
% apxs -i mod_fba.la

The second command will also add a line to your httpd.conf file to make sure the module gets loaded.

Enabling mod_fba

In each of your <VirtualHost> sections you need to add:

FakeBasicAuthEnable on
FakeBasicAuthType uwnetid

If you are using a different auth type with Pubcookie, use that instead of uwnetid.

Enable Proxy

Assuming you have not changed the default port on which Zope listens for requests, you need to add to each <VirtualHost> section:

RewriteEngine on
RewriteCond %{REQUEST_URI} !^/server-           # for /server-status, etc.
RewriteCond %{REQUEST_URI} !^/LOGIN$            # for forcing login
RewriteCond %{REQUEST_URI} !^/PubCookie.reply$  # for return from weblogin
RewriteRule ^(.*)$ http://localhost:8080$1 [P,L]
ProxyPreserveHost on                            # for CACSiteRoot

This will cause most of the URI space on your sever to be proxied to Zope. Because the Zope Management Interface uses several different paths for images, as well as CSS and JavaScript files, telling Apache which URIs to not proxy is much easier than telling it what to proxy.

One more change needs to be made for the proxy. The CACSiteRoot needs to know whether Apache received the requst over http or https. For your <VirtualHost> which is on the non-SSL port, add:

RequestHeader set X-Scheme http

For your SSL <VirtualHost>, add:

RequestHeader set X-Scheme https

Set up Auth

There are many pieces to setting up auth in Apache, because much of it is to support anonymous access. In your httpd.conf, add the following code. It is assumed that users is the list of UWNetIDs who should have access to the ZMI.

# Allow anonymous access to whole server
<Location />
    PubcookieOnDemand ondemand \
        "AuthType UWNetID" \
        "PubcookieAppID zope" \
        "require valid-user"
    AllowOverride all
    order deny,allow
    allow from all
    PubcookieInactiveExpire -1
</Location>

# /LOGIN is actually a CGI
<Location /LOGIN>
    SetHandler cgi-script
</Location>

# Force login to the ZMI; make sure the Pubcookie OnDemandKey cookie gets set
<LocationMatch "^/manage">
    PubcookieOnDemand ondemand \
        "AuthType UWNetID" \
        "PubcookieAppID zope" \
        "require user users"
    RewriteEngine on
    RewriteCond %{REMOTE_USER} !.
    RewriteRule ^(proxy:)?(https*://[^/]*)?(.*)$ http://%{SERVER_NAME}/LOGIN?$3 [R,L]
</LocationMatch>

You next need to install the login CGI. The source is available as LOGIN.c and be compiled with:

% cc -O -o LOGIN LOGIN.c

Be sure to place the resulting file, LOGIN, at the top of your DocumentRoot. It's best to make sure the LOGIN.c source file is not in your DocumentRoot.

Restart Apache

At this point, you have completed the changes for Apache. You'll need to restart Apache for the changes to take effect.

Add CACSiteRoot to Zope

CACSiteRoot.tar.gz

The Zope SiteRoot product will cause Zope to rewrite all URLs to match the external address rather than the internal, proxied address. The CACSiteRoot product is much like the Zope SiteRoot product, but its differences are:

CACSiteRoot needs to be able to serve URLs for both SSL and non-SSL. If the user is not logged in, they will most likely be viewing the site without SSL. If CACSiteRoot were to always rewrite the URLs to use https://, links and embedded objects (images, CSS, JavaScript, etc.) will cause the browser to use SSL, even though it is not necessary. On the other hand, if the user is logged in, CACSiteRoot couldn't rewrite the URLs with http:// without either causing links to force another round trip through the weblogin server, or causing embedded objects to fail on many browsers.

Add RemoteUserAuth to Zope

RemoteUserAuth.tar.gz

You will need to tell Zope to accept the username passed from Apache and not require a password. To do this, install the RemoteUserAuth product, written by Matt Hamilton.

Add Zope Managers

In your Zope instance directory, there should be a file named access which contains your initial username and a password. You need to edit this file to remove the password, which is everything after the colon. Be sure to leave the colon after the username. You can take this opportunity to add other Zope manager usernames to this file, each on a separate line and followed by a colon.

The first user in this list will be your emergency user, who cannot create objects in Zope. One thing you can do is add a dummy user in the first line, such as:

emergency:

or:

root:

It's best to make this a user who cannot login through Pubcookie.

Prohibit Remote Access to Zope

Since your Zope server now requires no passwords, you need to make sure it will accept no connections from other hosts. Edit the file etc/zope.conf in your Zope instance to include the line:

ip-address 127.0.0.1

Restart Zope

Once you have completed the above steps, restart your Zope server. At this point, you should only be able to reach your Zope server through the Apache proxy.

Create CACSiteRoot Object

The last step is to create a CACSiteRoot object so URLs are correctly rewritten. Connect to the ZMI at http://yourhostname/manage. You should be routed through weblogin, and you may get warnings about mixed SSL and non-SSL content.

Once in the ZMI, you should be at the root level. Using the "Select type to add..." menu, select "CACSiteRoot". When you get to the "Add CACSiteRoot" page, click on "Add" (there is no need to fill in the Title).

Once this is done, you have completed the necessary steps to use Pubcookie with Zope.