You are here: Home Tech Apache with RADIUS authentication for Trac (with RSA server)

Apache with RADIUS authentication for Trac (with RSA server)

by Chris Shenton last modified Feb 05, 2010 04:13 PM
We wanted to have a Trac instance authenticate to our corporate RSA server which exposes a RADIUS protocol; the mod_auth_radius module did the trick.

Background

We have a customer using Trac but whose security policy requires something stronger than reusable passwords. The enterprise has RSA SecurID token authentication systems. Past experience with the native RSA protocol has been excruciatingly painful: poor docs, difficult to use software libraries. Fortunately, the RSA server provides a RADIUS standard protocol access. I've used this in the past from within python applications using Wiggy's excellent pyrad module. For this, however, I didn't want to muck with the Trac code, and instead wanted to have Apache do the authentication. If this worked, it should work for any Apache-fronted webapp, and that could be a big win.

You may not have an RSA server, but RADIUS servers are quite commonly used for authentication. Their use seems to be increasing as a mechanism to provide authentication for handfuls of network devices. I have used FreeRADIUS for many years and like it, but there are others.

Local development on OS X

First I wanted to prototype it on my OS X laptop. FreeRADIUS will stand in for RSA's RADIUS server. Once the technology is proven to work, I'll reproduce it on the it production Linux server.

RADIUS

Install FreeRADIUS to emulate the RSA's RADIUS server since we have none on our dev LAN:

port install freeradius

Default install allows client localhost 127.0.0.1 with secret 'testing123'.

Create a user and password in /opt/local/etc/raddb/user:

tracuser Cleartext-Password := "tracpassword"

You don't need to supply any reply attributes like PPP address since we're not operating as a network access service. Ensure you don't have any indented lines right after this one.

Modern RADIUS auth and acct ports are 1812, 1813. Old ones are 1645, 1646.

Create some dirs the daemon uses by default:

mkdir -p /opt/local/var/log/radius
mkdir -p /opt/local/var/run/radiusd

Run the daemon in foreground to watch for auth problems:

/opt/local/sbin/radiusd -f -X

Test client auth; specify the server on localhost by IPv4 address, else it tries the IPv6 address which didn't work; the port 666 here doesn't matter:

$ radtest tracuser tracpassword 127.0.0.1:1812 666 testing123

Sending Access-Request of id 170 to 127.0.0.1 port 1812
        User-Name = "tracuser"
        User-Password = "tracpassword"
        NAS-IP-Address = 65.246.78.192
        NAS-Port = 666
rad_recv: Access-Accept packet from host 127.0.0.1 port 1812, id=170, length=20

$ radtest tracuser badpassword 127.0.0.1:1812 666 testing123

Sending Access-Request of id 19 to 127.0.0.1 port 1812
        User-Name = "tracuser"
        User-Password = "badpassword"
        NAS-IP-Address = 65.246.78.192
        NAS-Port = 666
rad_recv: Access-Reject packet from host 127.0.0.1 port 1812, id=19, length=20

If you've configured your server to live on the old RADIUS port, change this command appropriately.

Add mod_auth_radius to Apache

We can see that mod_auth_radius isn't already built into Apache:

httpd -M

On OS X SnowLeopard, the configs live in:

/private/etc/apache2/apache2/httpd.conf

and it, like most Linuxes, includes other files:

Include /private/etc/apache2/other/*.conf

The modules live in:

/usr/libexec/apache2/

and sadly, there's no RADIUS module there.

Get the module source code from:

http://freeradius.org/mod_auth_radius/

Specifically the file for Apache 2.0:

http://freeradius.org/mod_auth_radius/mod_auth_radius-2.0.c

Download then install it into your Apache modules directory:

$ sudo apxs -i -a -c mod_auth_radius-2.0.c
[...]
/usr/share/httpd/build/instdso.sh SH_LIBTOOL='/usr/share/apr-1/build-1/libtool' mod_auth_radius-2.0.la /usr/libexec/apache2
/usr/share/apr-1/build-1/libtool --mode=install cp mod_auth_radius-2.0.la /usr/libexec/apache2/
cp .libs/mod_auth_radius-2.0.so /usr/libexec/apache2/mod_auth_radius-2.0.so
cp .libs/mod_auth_radius-2.0.lai /usr/libexec/apache2/mod_auth_radius-2.0.la
cp .libs/mod_auth_radius-2.0.a /usr/libexec/apache2/mod_auth_radius-2.0.a
chmod 644 /usr/libexec/apache2/mod_auth_radius-2.0.a
ranlib /usr/libexec/apache2/mod_auth_radius-2.0.a
[...]
chmod 755 /usr/libexec/apache2/mod_auth_radius-2.0.so
[activating module `radius_auth' in /private/etc/apache2/httpd.conf]

Verify it's there:

$ httpd -M | grep radius

Warning: DocumentRoot [/usr/docs/dummy-host.example.com] does not exist
Warning: DocumentRoot [/usr/docs/dummy-host2.example.com] does not exist
Syntax OK
 radius_auth_module (shared)

The 'apxs' command above added the line to the httpd.conf that included the module. Let's add a line to tell it to look in an external file to get the RADIUS configs, using the directory the httpd.conf file already includes. Put the following In /private/etc/apache2/other/radius.conf:

<IfModule radius_auth_module>
  AddRadiusAuth localhost:1812 testing123 5:3
  AddRadiusCookieValid 2
</IfModule>

<Location /secure/>
  AuthType Basic
  Order Allow,Deny
  Satisfy any
  Require valid-user
  AuthName "RADIUS authentication"
  AuthBasicAuthoritative Off
  AuthRadiusAuthoritative on
  AuthBasicProvider radius
  # Does this cookie REALLY expire the specified number of minutes?
  AuthRadiusCookieValid 1
  AuthRadiusActive On
</Location>

It appears to me that the cookie is getting set to 2 minutes, not the lower of the two settings as the docs suggest. It also seems that the cookie is never causing the session to time-out: it just keeps getting extended by 2 minutes on each page refresh, even if I waited more than 2 minutes to reload. This could be a show-stopper for sites that require automatic session time-out.

mod_auth_xradius: couldn't build

An alternate implementation, boasting:

  • Distributed Authentication Cache using apr_memcache.
  • Local Authentication Cache using DBM.
  • Uses standard HTTP Basic Authentication, unlike mod_auth_radius which uses cookies for sessions.

but I couldn't compile it; then again, I didn't specify the memcache but that doesn't appear to be the problem:

gcc -DHAVE_CONFIG_H -I. -I. -I./include -I./libradius
-I/usr/local/include -DDARWIN -DSIGPROCMASK_SETS_THREAD_MASK
-I/usr/include/apache2 -I/usr/include/apr-1 -I/usr/include/apr-1 -g
-O2 -MT libradius_la-radlib.lo -MD -MP -MF
.deps/libradius_la-radlib.Tpo -c libradius/radlib.c -fno-common
-DPIC -o .libs/libradius_la-radlib.o

libradius/radlib.c:64: error: static declaration of 'strsep' follows non-static declaration
/usr/include/string.h:135: error: previous declaration of 'strsep' was here
make: *** [libradius_la-radlib.lo] Error 1

and I don't feel like shaving that yak now.

Trac

I built Trac from a buildout I did for a different client.

Now I have to integrate it like our target Trac. Normally I integrate Trac into Apache with mod_wsgi, but this is not how our wiki server was built: it runs as a CGI.

I used the same trac.cgi as on the target box (see below), with some path changes, installed into /Library/WebServer/CGI-Executables and can now connect via Apache to Trac. Login fails of course because I've not configured it.

I then set the RADIUS auth as for standalone above:

<Location "/cgi-bin/trac.cgi/login">
  AuthType Basic
  Order Allow,Deny
  Satisfy any
  Require valid-user
  AuthName "Trac RADIUS"
  AuthBasicAuthoritative Off
  AuthRadiusAuthoritative on
  AuthBasicProvider radius
  AuthRadiusCookieValid 1
  AuthRadiusActive On
</Location>

and it worked. W00t.

Target Linux Server

Now implement the above on the target Linux server.

Apache

  • /etc/httpd/conf.d: There's a python.conf using mod_python for *.html but nothing trac specific
  • /etc/httpd.conf: access restriction for /trac/login
  • /var/log/httpd: needs privs to access
  • Ours is httpd-2.2.3.

Trac

Location:

/usr/local/trac:
drwxr-xr-x  4 tracuser tracuser 4096 Jan 11 04:51 .
drwxr-xr-x  9 root     root     4096 Jan  7 03:33 explorers
drwxr-xr-x  9 apache   tracuser 4096 Jan 12 08:03 helio
-rw-r--r--  1 kcunning kcunning  355 Feb  2 07:36 trac.htpasswd

Trac is integrated not as mod_python or mod_wsgi but as a CGI in /var/www/cgi-bin/trac.cgi.

Installation

Build, install the module after downloading it:

$ sudo /usr/sbin/apxs -i -a -c mod_auth_radius-2.0.c

Watchtower is now at:

watchtower.sef.hq.nasa.gov. 86400 IN    A       131.182.40.52

Added the following stanzas to the httpd.conf:

<IfModule radius_auth_module>
  AddRadiusAuth 131.182.40.52:1645 radiussecret 5:2
  AddRadiusCookieValid 2
</IfModule>

<Location "/cgi-bin/trac.cgi/login">
  AuthType Basic
  Order Allow,Deny
  Satisfy any
  Require valid-user
  AuthName "Trac RSA authentication, enter: PIN + PassCode"
  AuthBasicAuthoritative Off
  AuthRadiusAuthoritative on
  AuthBasicProvider radius
  AuthRadiusCookieValid 1
  AuthRadiusActive On
</Location>

And this worked fine, since my account was in the RSA server's user database. If our RSA server is configured to offer cross-realm authentication services, it can authenticate remote users as well. This is something we needed in our enterprise situation, and the RSA server hid the details so we didn't need to worry about it.

Only allow our users

I soon realized this config allowed anyone who could RSA authenticate to have access to our Trac: we were missing authorization. We could do that by creating a group in Trac and listing each user in that, but it seemed easier and more reliable to use Apache authentication against a group file. So I created /usr/local/trac/trac.htgroup:

rsatrac: username1 username2 username3

I then modified the Apache RADIUS and authentication stanzas above to require membership in the 'rsatrac' group

<IfModule radius_auth_module>
  AddRadiusAuth 10.9.8.7:1645 radiussecret 5:2
  AddRadiusCookieValid 2
</IfModule>

<Location "/cgi-bin/trac.cgi/login">
  AuthType Basic
  Order Allow,Deny
  Satisfy any
  AuthGroupFile /usr/local/trac/trac.htgroup
  Require group rsatrac
  AuthName "Trac RSA authentication, enter: PIN + PassCode"
  AuthBasicAuthoritative Off
  AuthRadiusAuthoritative on
  AuthBasicProvider radius
  AuthRadiusCookieValid 1
  AuthRadiusActive On
</Location>

This achieved the desired effect. Our non-local users could authenticate (cross-realm), and RSA users not in our 'rsatrac' group of users could not gain access to the site.

Potential Problems

RSA is OTP

RSA tokens are a One Time Password system. So if a users first authenticates to our VPN server to gain access to our network, they must wait for the token to generate a new code before using that code to authenticate to the Wiki.

No Indication of Token Problems

When the user can authenticate, all is fine.

However if authentication fails it could be for a number of reasons. It might be because the user typed in their passphrase wrong, or re-used one instead of waiting for a new token.

Or it may be because their token's been "locked" due to too many failures, or that the token's "expired". I believe (but cannot confirm) that the RSA RADIUS server returns different failure codes to indicate the condition. But even if it did, there is no mechanism in the mod_auth_radius to change behaviour based on this, nor does the HTTP Basic authentication mechanism have a way to relay an error status message to the user.

This can be confusing to users. It's probably best to educate users about this possibility, and how to recover. Ask the RSA folks where to got -- what server -- and what to do to recover from a locked or expired token.

Share this: