Running Trac on Nginx with Phusion Passenger
I’m currently investigating ways to replace Apache httpd with nginx on srcbox.net.
One of the services that runs on srcbox.net is Trac for hosting a wiki-based website and bug tracking for software projects. Because I’m already using Phusion Passenger for hosting Ruby on Rails projects, I decided to also give it a try to host WSGI applications together with Nginx.
Please note that this post is quite similar to Hosting Trac with Passenger and nginx written by Graham Edgecombe about the same subject. I found a few things missing and notable so I decided to do my own little writeup on what I had to do to get things working.
Trac Setup
As the actual Trac setup is already present on my server and I just wanted to replicate a similar setup in my testing VM, I setup Trac like this:
- Installed package
trac
, I used version 0.12.x which is available via Debian Backports - Added a system user that owns all trac project files and is used to run the WSGI webserver processes: adduser –system –home /var/lib/trac –group –disabled-password –disabled-login trac
- Initialized a trac environment below trac home: trac-admin /var/lib/trac/project initenv
- Deployed the webserver files using: trac-admin /var/lib/trac/project deploy /var/www/trac/
- Added symlinks to make Phusion Passenger accept Trac as a WSGI application: ln -s /var/www/trac/htdocs /var/www/trac/public ln -s /var/www/trac/cgi-bin/trac.wsgi /var/www/trac/passenger_wsgi.py
Installing Nginx
Nginx installation is quite a bit of work if you’re used to a single aptitude install package
, all features/modules have to be enabled/added at build-time
which means rebuilding Nginx if you want to use Phusion Passenger. Because I
plan to move authentication to a central place soon I also added
Nginx HTTP Auth PAM
to the mix.
Furthermore I’m also tinkering with the idea to give
ownCloud a spin
so WebDAV support is probably helpful too.
The installation basically went like this (rough steps, this is not a place for step-by-step tutorials):
- Downloaded latest stable Nginx release
- Downloaded latest ngx_http_auth_pam_module release
- Unpacked both in a convenient place
- Installed Phusion Passenger via rubygems (I used ruby and gem from
ruby1.9.1
in Debian Squeeze) - Ran
passenger-install-nginx-module
- Chose
2. No: I want to customize my Nginx installation.
- Told the installer where to find unpacked Nginx sources
- Added additional configure arguments. For Auth PAM and optionally needed WebDAV support I used: –add-module=/path/to/unpacked/ngx_http_auth_pam_module –with-http_dav_module
- Grabbed an Espresso until the build was finished (didn’t really take long)
On Debian it’s probably helpful to install a fake webserver package to satisfy
dependencies for other packages that depend on http-server
. I used equivs to
generate myself an nginx-dummy package.
Configuring Nginx
For starting up Nginx I simply grabbed the init-script from the Debian source
package of nginx, , put it into /etc/init.d/
, adapted the paths to my Nginx
install in /opt/nginx
and installed it in the default runlevels using
insserv nginx
.
Adding a virtual host to Nginx that serves Ruby on Rails or a WSGI application is usually very easy, for Trac however a tiny bit more care had to be taken…
Pitfall 1: HTTP Auth
With Trac there’s one catch: HTTP Authentication.
It took me quite some time to find out why HTTP-Auth worked fine according to
the Nginx access.log
but Trac still thought I wasn’t logged in:
Nginx needs to forward the user name to the WSGI application. For Phusion
Passenger this can be done using the following line:
passenger_set_cgi_param REMOTE_USER $remote_user;
Pitfall 2: Location Blocks
Nginx uses location blocks for different behavior depending on the
url. For Trac the /login
path should trigger HTTP authentication.
Together with Phusion Passenger all I got after successful login however was
a simple “404 Not Found” page.
It turned out that for location blocks one has to enable Phusion Passenger
again, a prior passenger_enabled on;
line in the parent block does not
apply to the inner location block.
Final Nginx Config
The complete host configuration for my test setup that contains all needed things to avoid the above pitfalls looks like this:
server {
listen 80;
server_name trac.domain.lan;
root /var/www/trac/public;
passenger_enabled on;
# Forward HTTP-Auth user to WSGI
passenger_set_cgi_param REMOTE_USER $remote_user;
location /login {
auth_pam "Trac on domain.lan - Restricted Access";
# Use the same PAM service as dovecot for now
auth_pam_service_name "dovecot";
# Reenable Passenger to get a redirect after login instead of a 404
passenger_enabled on;
# Forward HTTP-Auth user to WSGI
passenger_set_cgi_param REMOTE_USER $remote_user;
}
}
What next?
This setup is probably still far from complete. Most notable things that I did not setup or test yet include:
- Paths for a manual Nginx install do not obey the FHS at all, logs and temporary files end up on the wrong partition which is a bad idea security wise.
- Log rotation is missing. I can probably grab parts from the Debian Nginx package again.
- Serve multiple projects/environments with the above webserver setup
- Replace the rather ugly HTTP Authentication with Trac Account Manager plugin
- Use TLS and enforce login to happen encrypted only
Of course Trac is by far not the only web-application, I’m probably going to document a few other things I have installed as part of my new Nginx test installation :)