Amazon Elasticload Balancers with authentication via htpasswd protection

3 Tier Web App

A simple 3 Tier AWS Web Application

The Problem: I recently had the task of setting up a production stack and a dev stack for a Symfony application in AWS. It was a pretty standard layout: a ELB, the frontend webservers, and the backend RDS database servers, but the tricky part was when rolling out the development environment because “Dev needs to match production in almost every way”.  So nothing fancy there, it’s the same 3 tier architecture: LB, Web FE, DB backend and almost the same URLs, dev.example.com vs prod.example.com, simple enough.

The monkey wrench starts to come in when dealing with Symfony’s dev and prod environments. Symfony’s dev environment is awesome, it tells you all sorts of crazy information: credentials, db, session information. The problem is that this information open to the public can tell a lot about the environment so you need to lock it down. Typically in Symfony the dev environment is redistricted and allowed to only specific IPs, but when you throw a Amazon Elastic Loadbalancer in there then you have a problem since all requests are the same: they come from the ELB. There’s ways to work around that with the PHP session variable “http_x_forwarded_for” but for this case, the requirement was to lock the environment down with a htaccess file using a htpasswd file.

Sounds simply enough right?, throw a htaccess and htpasswd file on there and problem solved. Except for the ELB Health Check, which determines if the Web-fe is “healthy” and should be receiving traffic. A “403 – Forbidden” is definitely going to fail the ELB health check and now the frontend web servers have gotten removed from rotation and there are no servers left to handle the traffic.

Ok, so we need to pass the ELB health check without relying on the ELB’s IP (or CNAME, since the IP is subject to change) but we want almost everything locked down so we don’t “leak” any information to the world. The solution lies in a relatively obscure called the Satisfy directive which if you search for you’ll find is the subject of a lot of .htaccess tricks, this post included: http://httpd.apache.org/docs/2.2/mod/core.html#satisfy I first found out about it by trying to filter by source IP and make the “LAN” trusted which is exactly what the documentation shows, but it has uses beyond that and the best I can describe it: it sort of acts like a “AND” or “OR” parameter when specifying authorization restrictions.

The Solution to the problem: This becomes easy to use with a health check from an AWS ELB. Just stating the problem in a logical way almost spells out the solution: We want anyone to be able to hit the health check url unrestricted but any other page, directory, etc requires a valid user. In Apache config speak (main or vhost config, I haven’t tried to do this in a .htacess file alone) becomes:

<VirtualHost *:80>
ServerName dev.example.com
DocumentRoot /var/www/html/
<Directory /var/www/html/>
# enable the .htaccess rewrites
AllowOverride All
Order allow,deny
Allow from All
AuthName "Access"
AuthType Basic
AuthUserFile /var/www/html/htpasswd.users
<Files "*">
Allow from All
Require valid-user
</Files>
<Files "index.htm">
Allow from all
Satisfy any
</Files>
</Directory>
</VirtualHost>

So what is happening? The two key pieces are the two Files directives after the auth block, we say that all files require a valid user, but then a specific page: index.htm is ok for anyone to access and that it satisfies the auth requirements. In this case, index.htm is a simple choice because it is a simple static page we can make in the symfony web root. That means we are only using the ELB health checking on apache and not on symfony where we could see if it is getting valid info back from the pages and the DB connection, but that’s acceptable since this is “Dev” and not production. Running with that, I could see this possibly being expanded out to an actual symfony health check page showing a proper DB connection or something but that might be super complicated since in symfony the app_dev.php page is always the file hit and the URLs are what’s different, due to the routing.yml file of the application, so I feel you wouldn’t be able to use the “File” directive alone and would have to tie this in somehow with rewrite rules or conditions as well and already my brain is starting to spin….

But the end result works. I get an Dev environment behind an ELB so I’m free to test things like SSL or whatever I’m doing in prod and get the same result, but I’m able to lock down my dev environment by password rather than IP so I’m not showing debug info out to the world.

Leave a Reply

Your email address will not be published. Required fields are marked *