Apache, Unicorn & SSL

Having used Apache with Mongrel, Thin and Passenger, I’ve now moved on to Unicorn.  Setting up Apache on Ubuntu to proxy to Unicorn requires the following installation commands:

apt-get install apache2 -y
apt-get install libapache2-mod-proxy-html libxml2-dev -y
a2enmod headers
a2enmod proxy
a2enmod proxy_http
a2enmod proxy_balancer
a2enmod rewrite
a2enmod ssl
/etc/init.d/apache2 restart

With these modules in place a virtual host can be configured something like this:

<VirtualHost *:80>
  ServerName     myhost.example.com
  DocumentRoot   /opt/example/app/public

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

  <Proxy balancer://unicornservers>
    Allow from any
    BalancerMember http://127.0.0.1:8080
  </Proxy>
</VirtualHost>

<VirtualHost *:443>
  ServerName     myhost.example.com
  DocumentRoot   /opt/example/app/public

  RewriteEngine On
  # Redirect all non-static requests to unicorn
  RewriteCond %{DOCUMENT_ROOT}/%{REQUEST_FILENAME} !-f
  RewriteRule ^/(.*)$ balancer://unicornservers%{REQUEST_URI} [P,QSA,L]
  RequestHeader set X-Forwarded-Proto "https"

  <Proxy balancer://unicornservers>
    Allow from any
    BalancerMember http://127.0.0.1:8080
  </Proxy>

  SSLEngine     on
  SSLCertificateFile /etc/apache2/ssl/ssl.crt
  SSLCertificateKeyFile /etc/apache2/ssl/ssl.key
</VirtualHost>

mod_headers and the RequestHeader directive are useful if the Rails app is using ssl_requirement.

Using Apache with Mongrel or Thin and Rails 3

When using mod_proxy to load balance between members of a mongrel or thin cluster it’s important not to pass requests through for static content.  A basic virtual site can be configured like this:

<VirtualHost *:80>

    ServerName myapp.mydomain.com
    DocumentRoot /opt/myapp/public

    <Proxy balancer://thinservers>
        BalancerMember http://127.0.0.1:3000
        BalancerMember http://127.0.0.1:3001
        BalancerMember http://127.0.0.1:3002
    </Proxy>

    ProxyPreserveHost On
    ProxyPass /images !
    ProxyPass /stylesheets !
    ProxyPass /javascripts !
    ProxyPass / balancer://thinservers/
    ProxyPassReverse / balancer://thinservers/

    <Proxy *>
     Order deny,allow
     Allow from all
    </Proxy>

    ErrorLog /var/log/apache2/error.log
    LogLevel warn
    CustomLog /var/log/apache2/access.log combined

</VirtualHost>

Thin with Apache

Having run into compatibility issues between Passenger and RJB and between Mongrel and Rails 2.3.8 I’ve been looking at Thin.

Slicehost has a couple of good articles on setting up a Thin cluster with Apache on Ubuntu.

In summary:

gem install thin
thin install
/usr/sbin/update-rc.d -f thin defaults
thin –h # help
thin -C /etc/thin/${appenv}.yml -c ${apphome} -s 3 -e production config
su – ${appenv}bat ; thin -C /etc/thin/${appenv}.yml start

Installing Phusion Passenger on Ubuntu 10.04 LTS

Before installing Phusion Passenger you will need a working installation of Rails:

apt-get install ruby rubygems # distro packages rubygems 1.3.7
gem install rubygems-update
PATH=$PATH:/var/lib/gems/1.8/bin
update_rubygems
gem install rails -v=2.3.4 # or whatever

The Phusion installation script builds an Apache loadable module by linking various libraries. Make sure these libraries are installed before running the script.

apt-get install build-essential ruby1.8-dev libopenssl-ruby
apt-get install apache2 apache2-prefork-dev libapr1-dev libaprutil1-dev
apt-get install libcurl4-openssl-dev

gem install passenger
passenger-install-apache2-module # compilation dependencies satisfied above

The Passenger installation script displays a message to add some lines to your Apache configuration script. One way to do this with Apache2 on Ubuntu is:

cat > /etc/apache2/mods-available/passenger.load <
   LoadModule passenger_module /usr/lib/ruby/gems/1.8/gems/passenger-2.2.15/ext/apache2/mod_passenger.so
   PassengerRoot /usr/lib/ruby/gems/1.8/gems/passenger-2.2.15
   PassengerRuby /usr/bin/ruby1.8
EOF
a2enmod passenger
/etc/init.d/apache2 restart

Trac on Solaris using Apache mod_python and https

If Trac is being used by a distributed team over the internet we want to remove all privileges from unauthenticated users:

for perm in BROWSER_VIEW CHANGESET_VIEW FILE_VIEW LOG_VIEW MILESTONE_VIEW 
REPORT_SQL_VIEW REPORT_VIEW ROADMAP_VIEW SEARCH_VIEW TICKET_CREATE TICKET_MODIFY TICKET_VIEW 
TIMELINE_VIEW WIKI_CREATE WIKI_MODIFY WIKI_VIEW
do
  trac-admin $tracenv permission remove anonymous $perm
  trac-admin $tracenv permission add authenticated $perm
done

We also want to encrypt traffic to the site. To do this I tried stunnel…

/opt/csw/bin/pkg-get -i stunnel

…and placed the following in /opt/csw/etc/stunnel/stunnel.conf

 [https]
accept  = 443
connect = 8000

I also commented out the chroot setup. Once configured all that is required is to run

cd /opt/csw/etc/stunnel 
/opt/csw/bin/stunnel

…and change /var/opt/csw/trac/conf/trac.ini

 [trac]
authz_file =
authz_module_name =
base_url = https://trac.mydomain.com

The bad news is that Trac 0.10.4 does not consistently use base_url, so creating a ticket, for example, redirects the user to an http page.

PATH=/opt/csw/bin:$PATH
tracenv=/var/opt/csw/trac
HTTPS=1; export HTTPS
nohup tracd --port 8000 $tracenv &

To resolve this issue I decided to move from tracd/stunnel to Apache2/mod_python. The default Solaris 10 distribution includes apache2 but not mod_python. Instead I installed mod_python from Blastwave, which in turn automatically installs the Blastwave cswapache2 package below /opt/csw/apache2.

pkg-get install ap2_modpython

We will want to run trac under apache2 using a dedicated account:

groupadd -g 202 trac
useradd -g trac -u 202 -d /var/opt/csw/trac trac
chown -R trac:trac /var/opt/csw/trac

Modified /opt/csw/apache2/etc/httpd.conf

User trac
Group trac
…
RewriteEngine On
RewriteCond %{HTTPS} off
RewriteRule (.*) https://%{HTTP_HOST}%{REQUEST_URI}
…

   SetHandler mod_python
   PythonInterpreter main_interpreter
   PythonHandler trac.web.modpython_frontend
   PythonOption TracEnv /var/opt/csw/trac

Created a self-signed certificate for the site:

cd /opt/csw/apache2/etc
PATH=$PATH:/usr/sfw/bin
/usr/sfw/bin/openssl genrsa -out server.key 2048
/usr/sfw/bin/openssl req -new -x509 -key server.key -out server.crt -days 365 -subj "/C=US/ST=Florida/O=My Company/CN=trac.mydomain.com"

Modified /opt/csw/apache2/etc/extra/httpd-ssl.conf

ServerName trac.mydomain.com
…

   SetHandler mod_python
   PythonInterpreter main_interpreter
   PythonHandler trac.web.modpython_frontend
   PythonOption TracEnv /var/opt/csw/trac

To start Blastwave Apache2 using SMF on Solaris:

svccfg -s cswapache2 setprop httpd/ssl=true
svccfg -s cswapache2 listprop

svcadm enable cswapache2

To check status

svcs cswapache2
svcs –xv