LDAP authentication in an Apache fronted Rails app
If you manage anything but the simplest of setups you've probably got an LDAP server setup providing directory services to your network. If you don't you should probably stop reading now ;)
Authenticate using LDAP
The first step to getting your Rails application authenticating using LDAP is to get Apache to authenticate all requests before they reach the application. This stuff is tricky and Apache already has a rather lovely module, mod_authnz_ldap, that does all the heavy lifting for us.
<VirtualHost 193.219.108.xxx:443>
# I've used port 443 above because I'm dealing with passwords.
# [...snip...]
<Directory /var/www/foo.example.com/current/public>
AuthType Basic
AuthName "Foo Application Control Panel"
AuthBasicAuthoritative off
AuthBasicProvider ldap
AuthLDAPUrl ldap://ldap.example.com/ou=people,dc=example,dc=com?userid?one
Require valid-user
</Directory>
# [...snip...]
# Your normal Rails HTTP configuration goes here
</VirtualHost>
Look up the user in Rails
Okay, so any request that hits your application is now authenticated against your LDAP directory. Next, tell Rails to look for the user. For authentication I wrote a rather funky (if I do say so myself) mixin, Xeriom::Acts::ProtectedSystem.
module Xeriom # :nodoc:
module Acts # :nodoc:
module ProtectedSystem # :nodoc:
def self.included(base)
base.send(:extend, ClassMethods)
end
module ClassMethods
def acts_as_protected_system
include InstanceMethods
send(:before_filter, :ensure_user_is_logged_in)
send(:helper_method, :current_user)
send(:helper_method, :logged_in?)
end
end
module InstanceMethods
def ensure_user_is_logged_in
if !logged_in?
authenticate_user
end
end
def logged_in?
!current_user.blank?
end
def current_user
@current_user ||= User.find_by_id(session[:user_id])
end
def current_user=(user)
@current_user = user
session[:user_id] = user.blank? ? nil : user.id
end
def authenticate_user
authenticate_or_request_with_http_basic("Protected Area") do |username, password|
# Lock your application servers down to listen to only
# the web tier or this will kick your ass.
send(:current_user=, User.find_by_username(username))
end
end
end
end
end
end
ActionController::Base.send(:include, Xeriom::Acts::ProtectedSystem)
Like the code licence section in the sidebar says: this code is totally public domain, just don't sue me. To use it just drop the code in your lib/ directory and then call acts_as_protected_system in your ApplicationController.
class ApplicationController < ActionController::Base
helper :all # include all helpers, all the time
protect_from_forgery # because CSRF sucks!
acts_as_protected_system # lock the door
end
For bonus points...
If you found this article useful, give me some love over at Working With Rails.
Firewall a pristine Ubuntu 8.04 box
Follow these simple instructions to block all traffic but SSH to your box. Once you have these rules running you can punch more holes as required.
sudo apt-get install iptables
sudo iptables -A INPUT -i lo -j ACCEPT
sudo iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
sudo iptables -A INPUT -p tcp --dport ssh -j ACCEPT
sudo iptables -A INPUT -j DROP
sudo sh -c "iptables-save -c > /etc/iptables.rules"
If you'd like to save your current rules when you stop - or load the rules when you start the box, change your /etc/network/interfaces file so that it contains pre-up and post-down hooks to load / save the rules.
pre-up iptables-restore < /etc/iptables.rules
post-down iptables-save -c > /etc/iptables.rules
If you're hosted at Xeriom Networks and would like to be monitored by the monitoring service there, allow ICMP Type 8 from monitor.xeriom.net.
sudo iptables -I INPUT 4 -s 193.219.108.245 -p icmp -m icmp --icmp-type 8 -j ACCEPT
Remember to save the new rules to the iptables.rules.
sudo sh -c "iptables-save -c > /etc/iptables.rules"


