Posts Tagged ‘Apache’

Because my brain is a sieve here’s how to serve custom maintenance pages based on the subdomain

Tuesday, May 25th, 2010

Just like the favicons post of yore, this is another one that came up in conversation, “Can we display a custom maintenance page for each subdomain?” Since I had managed to sketch out a proof-of-concept with the favicons I figured this one shouldn’t be too hard.

The way this works is that Apache checks for a file called maintenance.on and if it exists it rewrites all traffic to maintenance-[subdomain].html. In practice, maintenance.on is set during deploys that require downtime such as one that might do work on the database, and at the end of a successful deploy the file is either removed or moved to something like maintenance.off.

Granted, this is a little kludgy as it depends on a separate html file for each subdomain and I don’t have a fallback position if that maintenance file doesn’t exist, Apache will just serve up a 404.

Anyway, here’s what’s in the Apache site file:

# Check for the maintenance.on file and redirect all requests to
# to a custom maintenance page based on the following formula
# maintenance-[subdomain].html
# get the requested subdomain name into variable %1
RewriteCond %{HTTP_HOST} ^([^.]+)\.*


# skip the rules for images and css
RewriteCond %{REQUEST_URI} !\.(png|jpg|jpeg|gif|css|ico)$


# check to see if maintenance.on exists
RewriteCond %{DOCUMENT_ROOT}/system/maintenance.on -f
RewriteCond %{SCRIPT_FILENAME} !maintenance-1%.html


# write out the custom maintenance page based on the domain
RewriteRule ^.*$ /system/maintenance-%1.html [L]

This was gratefully stolen and extended from the Codahale post, Time For A Grown-Up Server: Rails, Mongrel, Apache, Capistrano and You.

Because my brain is a sieve here’s how to serve custom favicons based on the subdomain.

Thursday, January 21st, 2010

The ability to serve, from the same code base, custom favicons based on subdomains came up recently and because my mod_write fu is about as strong as a water soaked kleenex I’m documenting just what the hell I did to get it working as a proof-of-concept.


RewriteEngine on
# This is just for troubleshooting purposes, comment it out
RewriteLog /var/log/apache2/rewrite.log
RewriteLogLevel 9


# Rewrite index.html to %2
RewriteRule ^/index.html$ /%2.html


# If the request is not for the the main domain or the www subdomain
RewriteCond %{HTTP_HOST} !^(www\.)?megafuntimefor\.us


# get the requested subdomain name into variable %2
RewriteCond %{HTTP_HOST} ^(www\.)?([^.]+)\.megafuntimefor\.us


RewriteRule ^/images/favicon.ico /images/%2.ico

Above is just the mod_rewrite block and while I’m sure that their is a drier way to write it I have yet to attain that level of enlightenment.

Using ELB to Serve Multiple Domains Over SSL on EC2 for Giggles and Unicorns

Wednesday, December 23rd, 2009

One of the complaints about EC2 is that you only have one IP address allocated per instance which makes it difficult to host, in a clean manner, multiple domains that require SSL certs.  Where you have control over IP allocation you could punch down a couple for one server and then set up your domains and SSL certs by IPs. That method is a no go so you are left with the ugly method of allocating those certs by port, something that Joe and Jane public are a little skittish about (https://superawesomefuntime.com is cool but https://superawesomefuntime.com:8443 smells phishy). Thankfully, ELB makes for a great proxy to hide the hideousness of the port-based workaround.

Essentially, as solutions go, this one is stupid easy; for each domain that you need to handle SSL for create a load balancer. Many balancers to one or a pool of instances. That’s it. Here’s a sloppy diagram…

For this example, we’ll be serving superawesomefuntime.com and unicorns-unlimited.com:


elb-create-lb superawesome --headers --listener "lb-port=443,instance-port=8443,protocol=TCP" --listener "lb-port=80,instance-port=80,protocol=http" --availability-zones us-east-1c


elb-create-lb unicorns --headers --listener "lb-port=443,instance-port=8445,protocol=TCP" --listener "lb-port=80,instance-port=80,protocol=http" --availability-zones us-east-1c

Take note of the address which are returned to you as need that for the cname portion of the game. They’ll look something like this: superawesome-123456789.us-east-1.elb.amazonaws.com and unicorns-987654321.us-east-1.elb.amazonaws.com.

The next step would be to add your instance to those load balancers:


elb-register-instances-with-lb superawesome --instances i-12ab3c45


elb-register-instances-with-lb unicorns --instances i-12ab3c45

Over on the server set up your virtual hosts in a way that hopefully is as hasty as mine…


NameVirtualHost *:8443
<VirtualHost *:8443>
ServerName superawesomefuntime.com
ServerAlias *.superawesomefuntime.com
SSLEngine On
SSLCertificateFile /etc/ssl/superawesome.crt
SSLCertificateKeyFile /etc/ssl/superawesome.key
RequestHeader set X_FORWARDED_PROTO 'https'
## Directories, Includes, Rewrites, oh my...
</VirtualHost>


<VirtualHost *:8445>
ServerName unicorns-unlimited.com
ServerAlias *.unicorns-unlimited.com
SSLEngine On
SSLCertificateFile /etc/ssl/unicorn.crt
SSLCertificateKeyFile /etc/ssl/unicorn.key
RequestHeader set X_FORWARDED_PROTO 'https'
## Directories, Includes, Rewrites, oh my...
</VirtualHost>

Then all you need to do is set up the cname for each domain to point at the load balancer address, in this example the cnames would be for *.superawesomefuntime.com = superawesome-123456789.us-east-1.elb.amazonaws.com and *.unicorns-unlimited = unicorns-987654321.us-east-1.elb.amazonaws.com.

The only loose end is configuring your healthcheck but that is a deeply personal decision best left for you and your app to work out.

EC2 Elastic Load Balancing for Fun and Profit

Monday, November 23rd, 2009

This one is so easy I don’t know why I haven’t gotten around to it sooner. It took my dev to be all, “OMG! The servers! How will we do a rolling deploy without impacting uptime!1!!eleven!” Thankfully, Amazon is there with Elastic Load Balancing (ELB) so I didn’t really need to get off my ass and be excited. Amazon Web Services: Doing the hard work so you don’t have to.

Before we dive in, a little background on operations. We’ve been making use of the elastic IP feature since it came out along with round robin DNS to handle the distribution of traffic over the web servers. Sure, it is crude and primitive but it works and I am a big fan of implementing the simplest possible solution since more moving parts results in exponentially more headaches. And, yes, we tried the HAProxy and the Nginx route but the cost+plus heartburn factor was too high for what we needed. Anyway, things have worked just fine with a minimum of effort on our part but our needs are changing so we need a solution that is both flexible and forgiving. Brittle is only good with peanuts.

First things first, create an ELB…

elb-create-lb eeniemeenie --headers --listener "lb-port=443,instance-port=8443,protocol=TCP" \
--listener "lb-port=80,instance-port=80,protocol=http" --availability-zones us-east-1c

Now you might be saying to yourself, “WTF, James. What is with the 443 => 8443 when 80 => 80?” Simple: “Currently, Elastic Load Balancing does not have SSL termination capability.” That basically means your application servers need to handle the SSL part but honestly this is fine because the way ELB is engineered the traffic between the balancer and your instances outside the firewall so you’ll want that traffic passing over SSL anyway.

Next, configure health checks…

elb-configure-healthcheck eeniemeenie --headers --target "TCP:8443" --interval 30 --timeout 20 \
--unhealthy-threshold 2 --healthy-threshold 2

elb-configure-healthcheck eeniemeenie --headers --target "HTTP:80/up.html" --interval 10 --timeout 5 \
--unhealthy-threshold 2 --healthy-threshold 2

These two health checks are intended to do different things. The one for TCP:8443 is to see if our application is up and serving (we force all traffic over SSL) while the latter is intended for rolling deploys, when we begin a deploy we’ll rename that file with the intention that the balancer sees it go missing and pulls the instance out of the pool. When everything is done and the deploy was successful we plop up.html back in and the balancer throws the instance back in the pool.

On the Apache side of things I decided to do a little mod_rewrite mumbo jumbo to deal with the new up.html file. Since I want all application related traffic forced to SSL the monitoring of that file by ELB presents a little wrinkle to my normal sledgehammer approach to things. Easily addressed though:

RewriteEngine on
RewriteCond %{REQUEST_URI} !(up.html)
RewriteRule ^(.*)$ https://%{SERVER_NAME}$1 [L,R]

Now, because I am a grumpy bastard I really don’t want up.html to be available over SSL.

RewriteEngine on
RewriteCond %{REQUEST_URI} (up.html)
RewriteRule ^(.*)$ http://%{SERVER_NAME}$1 [L,R]

That is basically it. Mind you, I have not punched this down for production but our initial tests have been very promising, in particular the rolling deployment feature.

(Parts of this solution were gratefully cribbed from Serk and Lead Thinking as well as the ever popular RTFM)

Continuous Integration Testing, CruiseControl.rb, Github, Apache, and You!

Friday, March 20th, 2009

“Ain’t no test like an elwoodicious test ’cause an elwoodicious test don’t pass!”

So one one of my co-workers thought it would be an awesome idea if we were continually reminded of our shortcomings regarding tests so he passed along a link to the CruiseControl.rb project. Getting up an running is a fairly simple affair, particularly if you are just running it locally, but we wanted to drop this on our QA server and have the dashboard served up via Apache.  Not too difficult, just a couple more steps.

Grabbing the code is the easy part, git clone git://github.com/thoughtworks/cruisecontrol.rb.git.

Add your project, ./cruise add Project-Name -s git -b branch-name-here -r git@github.com:username/project-name.git

Set up the configuration for your projects by editing ~/.cruise/site_config.rb.  The file is largely self-explanatory but it would be helpful to set Configuration.dashboard_url and Configuration.default_polling_interval which are nested in the middle of the file.

Set up the defaults for each project, like getting emails and polling times, in ~/.cruise/projects/project-name/cruise_config.rb.

Now the fun part, we use Apache with passenger so the tact we took was to install mongrel and configure Apache’s balancer to look for it on a specified port as well as password protect the app with basic authentication:

Edit your site file and add a virtual host…

<VirtualHost *:80>
ServerName cruise.mydomain.com
ServerAlias cruise.mydomain.com

Include /etc/apache2/sites-available/app.cruise

</VirtualHost>

Then create the app.cruise file with the following content…

ServerSignature Off

<Directory />
Options FollowSymLinks
AllowOverride None
</Directory>

DocumentRoot /where/you/put/cruisecontrol.rb/public

<Directory “/where/you/put/cruisecontrol.rb/public”>
Options FollowSymLinks
AllowOverride None
Order allow,deny
Allow from all
AuthName “Secure Area”
AuthType Basic
AuthUserFile
/where/you/put/.htpasswd
require valid-user
</Directory>

RewriteEngine On

# Redirect all non-static requests to cluster
RewriteCond %{DOCUMENT_ROOT}/%{REQUEST_FILENAME} !-f
RewriteRule ^/(.*)$ balancer://mongrel_cluster%{REQUEST_URI} [P,QSA,L]

# Deflate
AddOutputFilterByType DEFLATE text/html text/plain text/xml application/xml application/xhtml+xml text/javascript text/css
BrowserMatch ^Mozilla/4 gzip-only-text/html
BrowserMatch ^Mozilla/4.0[678] no-gzip
BrowserMatch \bMSIE !no-gzip !gzip-only-text/html

Lastly create a proxy.conf file in /etc/apache2/conf.d/…

<Proxy balancer://mongrel_cluster>
BalancerMember http://127.0.0.1:8000
AuthType Basic
AuthName “Supa Sekrit”
AuthUserFile /where/you/put/.htpasswd
Require valid-user
</Proxy>

One all that is done you can cd into the CruiseControl.rb directory and issue, ruby ./cruise start -p 8000 –daemon, and you should be ready to watch your tests fail like me.

Using mod_rewrite to force SSL on directories

Tuesday, December 30th, 2008

Not that this is exceptionally difficult but I really needed to post this just so I would have a reference in the future.

<VirtualHost *:80>
ServerName your.server.here
ServerAlias your.server.here

RewriteEngine on
RewriteCond  %{REQUEST_URI} (private|secret|goaway)
RewriteRule ^(.*)$ https://%{SERVER_NAME}$1 [L,R]

# The rest of the configuration follows…
</VirtualHost>

<VirtualHost *:443>
ServerName your.server.here
ServerAlias your.server.here

RewriteEngine on
RewriteCond  %{REQUEST_URI} !(private|secret|goaway)
RewriteRule ^(.*)$ http://%{SERVER_NAME}$1 [L,R]

# The rest of the configuration follows…
</VirtualHost>

Essentially any requests for private, secret, or goaway will get punted to https while all others will be handle as non-SSL.  In the 443 block, to make sure that you aren’t using SSL for assets that don’t require it we check again this time to see if it is not the listed directories and punt them back to non-SSL. This can also be used in htaccess.