Apache, Rails, and REMOTE_USER
I’m currently redeveloping our intranet site at work, rewriting it in Rails. I want to keep authentication as simple as possible, and since the current site is set up to authenticate against our LDAP server using mod_authnz_ldap, I want to keep that setup (and avoid having to build any authentication into the Rails app itself).
I quickly discovered that the REMOTE_USER environment variable, which for a CGI app would contain the username which was authenticated by Apache, doesn’t get passed via the proxying magic to Rails (when run via mongrel or the like). My Google-fu turned up this thread, which has a solution that sets an X-Forwarded-User header to the value of REMOTE_USER. That header gets passed on in the proxied request to the Rails app. Here’s the magic incantation:
RewriteCond %{LA-U:REMOTE_USER} (.+)
RewriteRule . - [E=RU:%1]
RequestHeader add X-Forwarded-User %{RU}e
It put me on the right track, but still wasn’t working. There’s two problems with the solution given there:
1. The RewriteRule only matches the root URL of the application (the . matches any single character). Back to regex class. It should read ^.*$, that is, match all URLs.
- It uses
mod_rewrite‘s ‘lookahead’ to determine the final (post-rewriting) value ofREMOTE_USER. I, however, am using this in a directory configuration context, which for esoteric implementation reasons doesn’t require lookahead—we can use theREMOTE_USERvariable directly.
Here, then, is my final config, added before the rest of the normal Rails .htaccess rules:
RewriteCond %{REMOTE_USER} (.+)
RewriteRule ^.*$ - [E=RU:%1]
RequestHeader add X-Forwarded-User %{RU}e
Using the X-Forwarded-User header in Rails
The other thing that the aforementioned post didn’t tell me was how to use the forwarded header once Apache was forwarding it. Rails doesn’t do anything automatically with an X-Forwarded-User header; you have to get at it in your controllers through the request object. I added the following method to ApplicationController (and used helper_method to make it available to views):
# app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
def http_remote_user
request.env['HTTP_REMOTE_USER'] || request.headers['X-Forwarded-User']
end
helper_method :http_remote_user
end
It checks first for the HTTP_REMOTE_USER environment variable, so that if you want to deploy your app differently you don’t need to change the code.
You could write a before_filter to enforce authentication, but I’ll leave that as an exercise for the reader. For this app, I need to know the authenticated username, but I don’t want the app to have anything more to do with authentication. The above does the job handily.
1 Comment
Jump to comment form | comments rss [?] | trackback uri [?]