Setting up Plone behind Apache with SSL
I had a tough time finding accurate information on how to put Apache in front of Zope / Plone. I hope that this detailed explanation, of what I did, will help you. I put Apache in front of Zope because I wanted to encrypt the sessions between browsers and Zope. I'm implementing an Intranet over the Internet. I expect that a lot of people will want to do the same.
Reviewer note: The second half of document is primarily concerned with forcing HTTPS/SSL connections in Plone. There is a performance penalty incurred when you do this. It may be desirable to use HTTPS only for the login form (to secure passwords), for example. For more details, see related items at the bottom of this page.
Overview
- Get Zope and Plone working on its own running off the default port on 8080
- Get Apache working on its own running off the default port on 80.
- Implemented the rewrite rules and proxy rules in Apache's configuration file (httpd.conf)
- If necessary, adding the VirtualHostMonster to Zope's root folder using the Zope Management Interface.
- Test that you can browse zope on port 8080 by looking at Apache on port 80.
- Get SSL Apache working with a test certificate.
- Change the rewrite rules to the apache httpd.conf file for SSL.
This document is going to focus on steps 3, 4, 5, 6 and 7.
Step 1. Get Plone working.
Download Plone and set it up according to the install documentation.
Step 2. Get Apache working.
Most Linux systems already have this running in some shape or form, on Windows you will have to download and set up Apache manually.
Step 3. Create the Rewrite Rules
Ok, this is where this document starts to get into more detail. It seems that Apache can be put in front of Zope and Plone in different ways. But the short answer is that for most people the best way is to use "Rewrite" Rules. If you have any doubts about this, you can read the details at: http://www.zope.org/Members/regebro/Zope_and_Apache . Detailed information on rewrite rules van be found in the Apache manual .
Rewrite rules simply tranfrom one URL (e.g. http://localhost) into another URL (e.g. http://localhost:8080/myownfolder/ ). i.e. Apache receives one URL, and then changes the URL and resubmits it on your behalf. The rules for transformation are written in a small, tight language called "regular expressions". Regular expression are used a great deal in the UNIX and Linux worlds in programs like "vi", "sed" and later in languages like Perl and PHP. (I cannot comment on Python yet.)
Now, many Zope adminstrators will want apache to match all incoming !URLs and change them to forward onto Zope. In this case, the pattern matching rule to use Apache is pretty easy. It could go like this :
RewriteRule ^/(.*) http://localhost:8080/$1 [P]
The first part of the Rule is say "What incoming URL patterns do you want me to transform?" and that part of the rule that does that is simply:
^/(.*)
What does that mean? Well it is a "regular expression" which matches nearly everything. I'll take it slowly:
- The ^ means start at the begining where the beginning is after the domainname (e.g. after http://www.yoursite.com)
- The / means match on the first / that you come to (after the domainname)
- The ( means remember everything that you match between here and ) and call it $1
- The . means match any single character which is not white space (space or tabs)
- The * means actually the operator on my left can be matched 0 or more times - in other words match conintous text until you come to an end of line or white space.
As as I said, this rule matches most URLs. The key thing for you to understand is that what ever is was matched between the brackets has now become $1.
The second part of the rule specifies the transformation. In this case that is:
http://localhost:8080/$1
So the new URL becomes http://localhost:8080/ followed by $1 which is what we matched. (Note that as we did not remember the first / in the matching pattern, we have not inadvertently create // after http://localhost:8080 - i.e. we have not put a / at the front of $1) Pretty simple. Now I implemented this rule on my system BUT it is only half the solution that is required .
Don't get me wrong - the above solution does work. But it has a serious flaw. Once your browser has loaded the Zope pages - all the internal site links on those pages will point straight back to http://localhost:8080/some_url_for_the_link and so when you follow the next link your browser will connect straight back to Zope and completely bypass Apache. Unless we do some clever stuff in Zope, the internal links in the Zope web site, will tell the user's browser to bypass Apache.
Step 4. Using the VirtualHostMonster
The Zope folks have created a neat, elegant solution which solves the problem of the Internal links on the Zope site called the VirtualHostMonster. It rewrites all the internal link URLs on a given part of the Zope site on the fly. In this way, you can ensure that when users follow through on internal links on the site, that those links always reroute them back through the Apache server.
The Zope folks have also arranged that all configuration for this happens at the Apache end. They have designed the Virtual Host Monster so that it does not need to be configured, just added.
A Virtual Host Monster is automatically installed at the root of your Zope site when your object database is created. If you accidentally delete it, you may just add a VirtualHostMonster to the root folder of my zope site using the Zope Managment Interface (ZMI). i.e. I browsed http://localhost/manage, logged in as admin, and using the drop down tab on the right hand side, clicked on VirtualHostMonster, and then clicked Add.
I was prompted for an id, I typed "apache" (it can be anything). I then clicked on the new icon for the VirtualHostMonster which was in the root folder. I clicked on About which told me some more about the object - this was just reassuring, but was not mandatory.
I then modified the rewrite rule in apache's httpd.conf to use the VirtualHostMonster:
RewriteRule ^/(.*) http://localhost:8080/VirtualHostBase/http/%{SERVER_NAME}:80/$1 [P]
I saved the file and restarted apache.
You are probably looking at that transformation and thinking - "does not make a lot of sense". Here is how the right hand side works:
- http://localhost:8080/VirtualHostBase
- Now the !VirtualHostBase is a special Object of the !VirtualHostMonster. (It's created when you add the !VirtualHostMonster to the root of your site which I will show you how to do later.)
- The rest of the transformation URL is really a message that is used by the Object.
- /http/ means when you transform links put an http:// on the front
- /%{SERVER_NAME}:80 says what machine name (%{SERVER_NAME} uses the value defined by the ServerName directive) and port to use. If it's 80 then it will just be a normal web URL - there will be no need to put 80 in the URL. Note that this will be used by the virtual host monster to rewrite all the links in your web page so that they point back through the apache proxy and not straight to the zope server. Otherwise when people click on a relative link in your zope site, those links would point straight to Zope rather than the Zpache proxy.
- /$1 will be the original "directories" matched from the original URL (actually objects) and tells the VirtualHostingMonster which part of the site to serve up.
Step 5: Test the system
Browse http://localhost to see if was forwarded to http://localhost:8080. I followed through on some links to check that they I would be re-routed back through the normal port 80 on apache.
Step 6: I Got the SSL version of Apache working.
I'm not actually going to go into the detail of how to set up an SSL Apache server. All the SSL stuff is pretty much ready to go on most Linux distros. They provide some standard tools for creating a test certificate to use.
For those of you not running SuSE and thus wondering how to create a certificate, here it is (you will need the openssl program):
- Create the server key (in this example RSA 1024 bit):
openssl genrsa -out MYSITE.key 1024
- Make a certificate out of it (it won't be signed yet):
openssl req -new -key MYSITE.key -out MYSITE.csr
- If you are planning on a running a serious site (like an E-Commerce site), you should consider getting the certificate signed by a public certificate authority (CA). It will cost money though. See this web site for more information.
- If you just want security and don't mind browser popups telling you that the server certificate is not signed by a public certificate authority, you may sign it yourself:
- Create your CA key (like above):
openssl genrsa -out ca.key 1024 - Create your CA certificate (will be valid for a year)::
openssl req -new -x509 -days 365 -key ca.key -out ca.crt
- Sign your server certificate with your CA certificate using the
sign.shshell script from the mod_ssl distribution.
- Create your CA key (like above):
For those of you running a Debian-like distro, you can create a self signed certificate by just running apache2-ssl-certificate. Two new files will be created inside /etc/apache2/ssl. You can also use /usr/share/doc/apache2/examples/ssl-std.conf.gz as a model for your ssl site.
Then I started Apache and testes whether it has worked by giving the command "netstat -an | more" as root. I looked for a port LISTENING on port 443 - that is the SSL port. It was not there. On my particular distribution, /etc/httpd/httpd.conf file had a number of "Define" statements for DUMMYSSL and SSL. I commented out the "Define" tags and their closing tags for all the SSL stuff. This way all the SSL statements were permanently on.
Step 7: Edit the Rewrite Rules to enable use of the SSL server
To complete the picture, here is the SSL rewrite rule that I'm using with SSL Apache:
RewriteEngine On
RewriteRule ^/(.*) http://localhost:8080/VirtualHostBase/https/%{SERVER_NAME}:443/$1 [P]
I've shown an additional apache config command which you will need to turn on Rewrite. The [P] on the end tells apache to act as a proxy server - in this way apache can forward your URL onto any other web server on the network.
Note that the VirtualHostMonster is converting all internal links on the zope web site to use a https. This will force the user's browser to use SSL encryption when they follow the internal links on the zope site.
Do NOT enable PROXY REQUESTS ON or you may unwintingly become a proxy for anyone in the word, and be used as a relay for various attacks. The proxy forward capability from within the rewrite rule will still work without "ProxyRequests on". My section of the config file looks like:
<IfModule mod_proxy.c>
#ProxyRequests On
# <Directory proxy:*>
# Order deny,allow
# Deny from all
# Allow from .your-domain.com
# </Directory>
#
# Enable/disable the handling of HTTP/1.1 "Via:" headers.
# ("Full" adds the server version; "Block" removes all outgoing Via: headers)
# Set to one of: Off | On | Full | Block
#
ProxyVia On
#
# To enable the cache as well, edit and uncomment the following lines:
# (no cacheing without CacheRoot)
#
# CacheRoot "/var/cache/http"
# CacheSize 5
# CacheGcInterval 4
# CacheMaxExpire 24
# CacheLastModifiedFactor 0.1
# CacheDefaultExpire 1
# NoCache a-domain.com another-domain.edu joes.garage-sale.com
</IfModule>
Although I've used the proxy, you can see that my proxy cache is currently commented out and turned off - I'm only using Plone for small number of people in the Intranet at the moment. But it is good to know that I have this mechanism available if I should need it later.
Tightening Security Further
Because Plone stores login information in a cookie, if you want a really secure site you'll have to make sure that all requests by logged-in users happen over SSL.
You can force people to use https if they are logged in. This is useful since the session cookie used by Plone sites includes the users name and password. These rewrite rules force requests to use https if the __ac session cookie is set:
# Redirect to https if the __ac cookie is set
RewriteCond ${HTTPS} off
RewriteCond %{HTTP_COOKIE} __ac=
RewriteRule ^(.*) https://%{SERVER_NAME}$1 [NE,L]
If you are using Plone 3 this is not as important due to its use of the more secure crytographic hash as session key.
It's useful in some cases to be able to default the "login" page to HTTPS, regardless of whether the user's currently on an SSL connection or not. This makes things easier for them and means a little less server load because unencrypted pages aren't having to go by HTTPS.
The gotcha is that Plone dumps you back to an HTTP connection after login. This is due to the value stored in the hidden came_from field of login_form. However you can rewrite came_from= using Apache:
# Rewrites the came_from in the URL
RewriteCond %{QUERY_STRING} came_from=http(.*)
RewriteRule ^/(.*)login_form$ https://%{SERVER_NAME}/$1login_form?came_from=https%1 [NE,L]
The first two lines handle the case of a redirect to login_form from a protected page, and it rewrites it to point to the secure login form with the came_from= URL rewritten from http to https. The last line handles the default case without a "came_from=" in the URL. Note that Apache strips out the QUERY_STRING before rewriting, so it's necessary to capture it using a RewriteCond; you can't match ?came_from= in a RewriteRule.
For the case where the login_form URL doesn't has a came_from parameter (i.e. direct access) or it has an invalid template (i.e. logged_out) you need an additional rule in your httpd.conf file as follows:
# This will switch the user to https when they hit login_form or login_success
RewriteRule ^/login_(.*) https://%{SERVER_NAME}/login_$1 [NE,L]
Then in your ssl.conf file add the following Rewrite rules:
# Logs user out of https upon logout
RewriteRule ^/(.*)logged_out http://%{SERVER_NAME}/$1logged_out [L,P]
# Keeps them on https until they log out
RewriteRule ^/(.*) http://localhost:8080/VirtualHostBase/https/%{SERVER_NAME}:443/Plone/VirtualHostRoot/$1 [L,P]
Additional notes
- The portlet_login template needs some tweaks to also change the came_from parameter to use https.
- You need both mod_proxy and mod_rewrite for rewrite rules. This may be obvious, but it wasn't for me. Might save you some time.
- If you are tweaking a new Apache 2 installation, don't forget to add "RewriteEngine on" line to the httpd.conf file. Otherwise your RewriteRules never wake up.
- In order to make SSL work in Redhat (Fedora), the rewrite rules must be in /etc/httpd/conf.d/ssl.conf at the bottom. It may just need to be after the SSLEngine on line, but it did not work at the top. You need the "RewriteEngine On" as well as the 443 line.
- How can I restrict users from going into the ZMI using apache? You could set up zope.conf so that your ZMI is published only at 127.0.0.1 (localhost). That way even if someone from the outside tries to go to your port 8080/manage URL it won't work. Or you could use a rule for Apache that forwards port 8080 to some other (bogus?) URL.
