Posterous theme by Cory Watilo

Filed under: SSL

Rails-Anwendung auf https umleiten

Immer wieder stehe ich vor dem gleichen Problem, der Blogbeitrag soll also auch gleichzeitig als Gedächtnisstütze für mich dienen.

Für mein Standardsetup verwende ich als Webserver nginx (Anzahl Worker = Anzahl Cores), als Rails-Applicationserver kommt thin mit zwei Workern zum Einsatz. Eine Standardkonfiguration könnte nun beispielsweise so aussehen:

# /etc/nginx/sites-available/my-site.de

upstream my-site {
  server   unix:/tmp/thin.my-site.0.sock;
  server   unix:/tmp/thin.my-site.1.sock;
}

server {
  listen   80;
  server_name my-site.de www.my-site.de;

  access_log /var/www/my-site.de/www/logs/access.log;
  error_log /var/www/my-site.de/www/logs/error.log;

  root   /var/www/my-site.de/www/htdocs/public/;
  index  index.html;

  location / {
    proxy_set_header  X-Real-IP  $remote_addr;
    proxy_set_header  X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header  Host $http_host;
    proxy_redirect    off;

    if (-f $request_filename/index.html) {
      rewrite (.*) $1/index.html break;
    }


    if (-f $request_filename.html) {
      rewrite (.*) $1.html break;
    }

    if (!-f $request_filename) {
      proxy_pass http://my-site;
      break;
    }

  }
}

In Verbindung mit dieser /etc/thin/my-site.yml funktioniert eine Rails-Anwendung auch einwandfrei:

---
pid: tmp/pids/thin.pid
wait: 30
timeout: 30
log: log/thin.log
max_conns: 1024
require: []

environment: production
max_persistent_conns: 512
servers: 2
daemonize: true
chdir: /var/www/my-site.de/www/htdocs
socket: /tmp/thin.sock

Sämtlicher Traffic läuft bei diesem Setup unverschlüsselt über Port 80, also Standard-http. Wenn man nun aber ein Login, bspw. für ein CMS wie Radiant, realisieren möchte, wäre es natürlich wünschenswert, den Traffic verschlüsselt über Port 443, also https, laufen zu lassen. Notwendig ist das aber aus meiner Sicht nur bei Zugriffen auf das Admin-Backend. Im nachfolgenden Setup werden sämtliche Anfragen auf http://www.my-site.de/admin/{IRGENDWAS} auf https://www.my-site.de/admin/{IRGENDWAS} umgeleitet. Requests auf die Website selbst bleiben von der Umleitung also unberührt.

# /etc/nginx/sites-available/my-site.de

upstream my-site {
  server   unix:/tmp/thin.my-site.0.sock;
  server   unix:/tmp/thin.my-site.1.sock;
}

server {
  listen   80;
  server_name my-site.de www.my-site.de;

  access_log /var/www/my-site.de/www/logs/access.log;
  error_log /var/www/my-site.de/www/logs/error.log;

  root   /var/www/my-site.de/www/htdocs/public/;
  index  index.html;

  rewrite ^/admin/(.*) https://www.my-site.de/admin/$1 permanent; # Rewrite-Regel für die Umleitung aller Anfragen auf /admin

  location / {
    proxy_set_header  X-Real-IP  $remote_addr;
    proxy_set_header  X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header  Host $http_host;
    proxy_redirect    off;

    if (-f $request_filename/index.html) {
      rewrite (.*) $1/index.html break;
    }


    if (-f $request_filename.html) {
      rewrite (.*) $1.html break;
    }

    if (!-f $request_filename) {
      proxy_pass http://my-site;
      break;
    }

  }
}

server {
  listen 443;
  server_name my-site.de www.my-site.de;

  ssl                 on;
  ssl_certificate     /etc/ssl/certs/startssl.crt;
  ssl_certificate_key /etc/ssl/private/startssl.key;
  ssl_ciphers         ALL:!ADH:!EXPORT:!SSLv2:RC4+RSA:+HIGH:+MEDIUM;
  ssl_protocols       SSLv3 TLSv1;

  access_log /var/www/my-site.de/www/logs/access.log;
  error_log /var/www/my-site.de/www/logs/error.log;

  root   /var/www/my-site.de/www/htdocs/public/;
  index  index.html;

  location / {
    proxy_set_header  X-FORWARDED_PROTO https;
    proxy_set_header  X-Real-IP  $remote_addr;
    proxy_set_header  X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header  Host $http_host;
    proxy_redirect    off;

    if (-f $request_filename/index.html) {
      rewrite (.*) $1/index.html break;
    }

    if (-f $request_filename.html) {
      rewrite (.*) $1.html break;
    }

    if (!-f $request_filename) {
      proxy_pass http://my-site;
      break;
    }
  }

}

Meine Zertifikate beziehe ich übrigens zumeist von StartSSL. Die Zertifikate kosten nichts und werden trotzdem von allen aktuellen Browsern akzeptiert. Nur den grünen Hintergrund bekommt man mit diesem Zertifikat nicht, was ich persönlich aber als unkritisch einstufe. Googles Chrome wird derzeit aber leider nicht für die Erstellung der Zertifikate unterstützt, mit der aktuellen Version von Apples Safari geht’s aber einwandfrei.

Kurz noch zur Erklärung, wieso nginx und thin:

  • nginx ziehe ich Apache vor, weil er deutlich schlanker und performanter ist. Für PHP-Setups verwende ich zumeist weiterhin den Apache, weil dieser sich deutlich einfacher mit PHP aufsetzen lässt, als dies mit nginx der Fall ist.
  • thin ist extrem einfach aufzusetzen, einfacher als Unicorn oder mongrel_cluster, die im Endeffekt genau das Gleiche tun. Und dank der Fähigkeit von thin, auch als UNIX-Socket zu laufen, ist er auch extrem schnell.

Von Fall zu Fall mag die Eignung der Alternativen eher gegeben sein, ich habe aber ziemlich gute Erfahrungen mit diesem Setup gemacht. Es ist einfach zu administrieren und äußerst performant.

Ein kleiner Tipp noch zum Schluss. Um sowohl thin als auch nginx in den aktuellen Versionen zu fahren, benutze ich folgende Wege der Installation:

nginx installiere ich über das eigens dafür bereitgestellte PPA, welches wie folgt in Ubuntu 10.04 LTS oder neuer eingebunden werden kann:

nginx=stable # use nginx=development for latest development version
sudo su -
add-apt-repository ppa:nginx/$nginx
apt-get update 
apt-get install nginx

thin installiere ich aus den Ruby-Gems heraus und nutze dann den komfortablen Installer, den die Jungs mitliefern:

sudo gem install thin
sudo thin install
sudo /usr/sbin/update-rc.d -f thin defaults

Um die Anwendungsumgebungen nicht händisch erstellen zu müssen, kann man das thin-Kommando benutzen:

sudo thin config -C /etc/thin/my-site.yml -c /var/www/my-site.de/www/htdocs --servers 2 -socket /tmp/thin.my-site.sock -e production

Dieser Befehl erstellt eine Konfigurationsdatei für thin, wie sie weiter oben zu sehen ist.