Die neue Generation der Rails Server und vereine.ch

Vor 3 Jahren haben wir unser Produkt Citrin Rails Server aufgeschaltet. Diese vorkonfigurierten virtuellen Server bieten eine einfache Möglichkeit schnell und flexibel eine Ruby on Rails Applikation zu veröffentlichen.

Nun ist es soweit die nächste Generation der Rails Server vorzustellen. Hier eine kurze Übersicht der Neuerungen:

  • Basierend auf dem aktuellen Ubuntu LTS (14.04)
  • Probleme beim Server Reboot behoben
  • CSF vorinstalliert um Brute-Force Attacken auf Firewallebene zu verhindern
  • citrin gem mit logrotate autoconfig
  • Privater gitlab-Account für alle Rails Server Kunden

Wir wollen unseren Kunden die Möglichkeit geben ihren Rails Server aktuell zu halten*. Deshalb stellen wir bestehenden Kunden auf Anfrage kostenlos einen Rails Server der neuen Generation zur Verfügung und betreiben diesen bis zu einem Monat parallel zum Alten um diesen Zeit für die Migration der Applikation(en) zu geben.

  • Ubuntu 12.04 erhält nur noch bis 2017 Updates

Ich werde hier nun die Migration eines solchen Rails Servers anhand der vereine.ch Applikation beschreiben. Die Vereine App wurde mit den Standard-Tools des ursprünglichen Citrin Rails Servers deployed: Apache+Passenger, MySQL und Subversion als VCS.

Schritt 1: Der neue Server

Zuerst brauche ich natürlich Zugang zu einem neuen Rails Server für die Migration. Dazu habe ich einen solchen bei Citrin bestellt. Die einzige aussergewöhnliche Anforderung war, dass ich statt der neusten Ruby Version 1.9.3 als default konfiguriert haben wollte damit Passenger unter der gleichen Version läuft wie auch mein ursprüngliches System. Dank RVM ist es problemlos möglich verschieden Versionen parallel zu installieren und diese auch nachträglich zu wechseln.

Citrin stellt mir nun also einen neuen Server i8rh01 zur Verfügung und hinterlegt dort meinen public key im .ssh/authorized_keys des rails-dev Users.

Schritt 2: Das Subversion Repository auf git migrieren

Wer nicht Subversion verwendet kann diesen Schritt getrost überspringen.

Der ursprüngliche Rails Server kam mit Helper-Scripts zum erstellen von Subversion repositories auf dem Server direkt. Für die neue Generation haben wir uns entschieden jedem Rails Server Kunden auf Anfrage einen Account auf unserem gitlab Server zur Verfügung zu stellen. Hier also eine kurze Anleitung wie man das Subversion Repository zu git migriert.

Der erste Schritt soll in einem ausgecheckten Repository Verzeichnis ausgeführt werden.

svn log -q | awk -F '|' '/^r/ {sub("^ ", ", $2); sub(" $", ", $2); print $2" = "$2" <"$2">"}' | sort -u > authors-transform.txt

Nun die Datei authors-transform.txt um die E-Mail Addressen in <> ergänzen.

Als nächstes das git-svn tool installieren. Hier am Beispiel von Ubuntu.

aptitude install git-svn

Nun kann das Subversion Repository einfach als git Repository geklont werden.

git svn clone file:///svn/vereine [^] --no-metadata -A authors-transform.txt --stdlayout ~/temp

Nun noch die Definition der ignorierten Files übernehmen.

cd ~/temp
git svn show-ignore > .gitignore
git add .gitignore
git commit -m 'Convert svn:ignore properties to .gitignore.'

Als nächstes kann man über das gitlab Webinterface ein Repo für vereine.ch anlegen. URL und Zugangsdaten gibt es individuell bei Citrin.

git remote add origin git@gitlab01.citrin.ch:citrin/vereine.git
git push -u origin master

Schritt 3: Deployment

Im einfachsten Fall checkt man das Repository einfach auf dem neuen Server aus:

cd /var/www/rails_apps/prod
git clone git@gitlab01.citrin.ch:citrin/vereine.git

Falls sich nichts am Setup der Versionsverwaltung geändert hat kann man auch einfach den alten Application Root kopieren.

cd /var/www/rails_apps/prod
rsync -av rhXY.citrin.ch:/var/www/rails_apps/prod/vereine/ vereine/

In allen fällen soll schlussendlich die Rails Applikation unter /var/www/rails_apps/prod/vereine/ ausgecheckt sein.

Dort noch bundle install ausführen

cd /var/www/rails_apps/prod/vereine
bundle install --path vendor/bundle

Capistrano
Für vereine.ch verwende ich Capistrano als deployment Tool. Hier sind die Anpassungen die für das Deployment auf den neuen Server nötig waren.

Editieren: config/deploy.rb und 2 Zeilen manuell anpassen.

set :repository, "git@gitlab01.citrin.ch:citrin/vereine.git"
set :scm, :git

Der Rest lässt sich mit String-Replace anpassen

:%s/rh05/i8rh01

Schritt 4: Apache Konfiguration kopieren

Die Konfiguration die vom citrin gem der ersten Generation erstellt wurde ist kompatibel also kann das VirtualHost file einfach kopiert werden.

scp root@rh05.citrin.ch:/etc/apache2/sites-available/prod.vereine.conf /etc/apache2/sites-available/prod.vereine.conf 
cd /etc/apache2/sites-enabled
ln -s ../sites-available/prod.vereine.conf

Da vereine.ch auch unter https verfügbar ist habe ich auch die dazugehörige Konfiguration kopiert.

scp rh05.citrin.ch:/etc/apache2/sites-enabled/prod.vereine.conf_443 /etc/apache2/sites-enabled/prod.vereine.conf_443
scp rh05.citrin.ch:/etc/ssl/certs/vereine.ch.crt /etc/ssl/certs/vereine.ch.crt
scp rh05.citrin.ch:/etc/ssl/certs/vereine.ch.ca /etc/ssl/certs/vereine.ch.ca
scp rh05.citrin.ch:/etc/ssl/private/vereine.ch.key /etc/ssl/private/vereine.ch.key

Zudem mussten noch ein paar Apache Mods aktiviert werden.

cd /etc/apache2/mods-enabled
ln -s ../mods-available/ssl.conf 
ln -s ../mods-available/ssl.load 
ln -s ../mods-available/socache_shmcb.load
ln -s ../mods-available/rewrite.load

Schritt 5: MySQL Datenbank kopieren

Auf dem alten Server die Daten dumpen.

mysqldump -u vereine_prod -p vereine_prod > vereine.sql
scp vereine.sql i8rh01.citrin.ch:/tmp/

Auf dem neuen Server eine Datenbank erstellen und die Daten einlesen.

citrin create_database vereine
vi config/database.yml 
mysql -u vereine_prod -p vereine_prod < /tmp/vereine.sql

Die vereine.ch Applikation hat noch persistente Daten ausserhalb der Datenbank im Filesystem die ich ebenfalls Synchronisiert habe.

rsync -av root@rh05.citrin.ch:/var/www/rails_apps/prod/vereine/shared/system/ /var/www/rails_apps/prod/vereine/shared/system/

Schritt 6: Logrotate einrichten

Zuletzt lasse ich noch logrotate einrichten um zu verhindern, dass die Application Logs den Server vollschreiben.

citrin setup_logrotate vereine

Die Standardkonfiguration würde also den production log in /var/www/rails_apps/prod/vereine/log/ konfigurieren. Da die Vereine Applikation aber mit capistrano deployed wird muss ich noch den Pfad anpassen.

Die Datei /etc/logrotate.d/rails-app-vereine-prod editieren und

/var/www/rails_apps/prod/vereine/log/production.log {

ersetzen mit

/var/www/rails_apps/prod/vereine/shared/log/production.log {

Einführung von AdminExile für Joomla-Applikationen

Es kann vorkommen, dass die Admin-Oberfläche des Joomlas von Hackern mit Bruteforce-Attacken angegriffen wird. Um zu verhindern, dass der Angreifer beliebig viele Passwörter ausprobieren kann, gibt es eine Extension namens AdminExile. Diese bringt, eine zusätzliche Authentifizierung mit, welche beim Aufruf der Admin-Oberfläche aktiv ist.

Absicherung der Admin-Oberfläche

AdminExile fügt einen Schlüssel hinzu, welcher beim Aufruf der Admin-Oberfläche mitgegeben werden muss. Dies kann wie folgt aussehen: mydomain.ch/administrator?aeShee9Quohzu8uiBaeb
Wenn nun ein Unbefugter auf mydomain.ch/administrator zugreiffen will, kommt er nicht auf die Admin-Oberfläche. Das Verhalten ist dabei konfigurierbar. Man kann auf die Hauptseite oder eine beliebige andere Seite weiterleiten, sowie eine 404-Seite anzeigen lassen. So hat der Angreiffer das Gefühl, dass diese Seite nicht existiert.

Schlüssel erhalten

Was ist jedoch wenn man den Schlüssel für die Admin-Oberfläche vergessen hat? Ganz einfach: Man geht auf mydomain.ch/administrator?email=benutzername, wobei “benutzername” der eigene Benutzer ist. Man bekommt anschliessend eine E-Mail an die dem Benutzer hinterlegte E-Mail-Adresse mit dem richtigen Link zur Admin-Oberfläche.

Konfiguration

Man kann diverse Einstellungen für den Bruteforce-Schutz vornehmen:

Name Beschreibung Empfehlung
Redirection Destination Man kann die Anzeige bei invalidem Zugriff selber auswählen. Zur Verfügung stehen:

  • {HOME} => Verweist auf die Hauptseite.
  • {404} => Zeigt eine eigene 404-Seite an um dem Angreifer zu zeigen, dass diese nicht existiert. Sie ist eine Nachbildung der 404-Seite von Apache.
  • Eigene Url => Eine beliebige eigene URL, wie zum Beispiel eine statische 404-Seite.
{HOME}
Max Attempts Die Anzahl von Versuchen bevor eine IP-Adresse geblockt wird. 3
Penalty Die Anzahl von Minuten die eine IP-Adresse geblockt wird (auch wenn sie einen Zugriff auf /administrator mit gültigen Schlüssel macht) 5
Penalty Multiplier Die Nummer mit der Penalty vergrössert wird bei jedem weiteren fehlerhaften Zugriff. Zum Beispiel bei Multiplier = 2 und Penalty = 20:

  1. Fehlerhafter Versuch: 20 Minuten
  2. Fehlerhafter Versuch: 40 Minuten (2 * 20 Minuten)
  3. Fehlerhafter Versuch: 80 Minuten (2 * 40 Minuten)
2
Email Admin Der Administrator der bei fehlerhaften Anmeldversuchen informiert wird.

Wenn alles schief geht

Es kann natürlich vorkommen, dass man die eigene IP blockiert hat und nicht mehr auf die Admin-Oberfläche zugreifen kann. In einem solchen Fall: Keine Panik. Das Problem kann in drei Schritten behoben werden:

  1. adminexile.php in /plugins/system/adminexile umbenennen, zum Beispiel nach Xadminexile.php. Ab jetzt wird die Admin-Oberfläche nicht mehr durch AdminExile geschützt.
  2. Nun kann man in die Admin-Oberfläche gehen und AdminExile im Plugin-Manager deaktivieren.
  3. Wenn man jetzt Xadminexile.php zurück umbenennt nach adminexile.php kann man sich selbst von den Blockierten IP-Adressen löschen oder anderweitig das Problem beheben.

Weitere Informationen

Einführung bei Citrin Kunden
Extension Beschreibung auf joomla.org
Dokumentation von AdminExile

Verbesserte Ruby Performance

Für Ruby 1.9.3 sind diverse Performance Tweaks bekannt die jedoch in der standard RVM Version nicht vorhanden sind. Dieser Blogpost wird aufzeigen wie man diese auf einem Citrin Rails Server anwendet.

Die Optimierungen zusammengefasst

  • GCC Flags für Binary Optimization
  • Falcon patch
  • Ruby 1.9.3 Environment Variabeln

Die Auswirkungen und Performance Messungen findet man in den Quellen. Hier wird nur die Anwendung auf einem Rails Server gezeigt.

Als Beispiel nehmen wir das auf einem Rails Server Standard gehostete vereine.ch. Hier haben die Tweaks einen Performance Gewinn von 20%-30% über die 5 meist genutzten URLs gebracht.

Ruby ruby-1.9.3-p327 mit Optimierungen installieren

Zuerst kommt das Update auf die aktuelle RVM Version.

rvm get head --auto
rvm reload

Nun kann man Datei ~/.rvmrc erstellen mit dem Inhalt

rvm_configure_env=(CFLAGS="-march=native -O3")

Nun noch automake installieren und danach die neue Ruby Version mit dem Patch kompilieren.

aptitude install automake
rvm install 1.9.3-p327 --patch falcon

Um einen reibungslosen Übergang für die bestehenden Apps zu garantieren kann man das gemset der alten Ruby version in die neue kopieren.

rvm gemset copy ruby-1.9.2-p290 ruby-1.9.3-p327

Nun noch das Passenger Apache Modul unter der neuen Version kompilieren.

 rvm use ruby-1.9.3-p327
 passenger-install-apache2-module 

Danach noch den Passenger auf die richtige Ruby version konfigurieren:
In /etc/apache2/mods-enabled/passenger.load und /etc/apache2/mods-enabled/passenger.conf z.B.

   PassengerRoot /usr/local/rvm/gems/ruby-1.9.3-p327/gems/passenger-3.0.9
# PassengerRoot /usr/local/rvm/gems/ruby-1.9.2-p290/gems/passenger-3.0.9

Nach dem Webserver Neustart verwenden alle Rails Applikationen das neue Ruby.

service apache2 restart

Ruby Umgebungsvariabeln

Mit einem besseren Memory Allocator kann nochmal mehr Performance rausgeholt werden.

apt-get install libtcmalloc-minimal0
export LD_PRELOAD=/usr/lib/libtcmalloc_minimal.so.0.1.0

Zudem kann man in Ruby 1.9.3 das Verhalten des GC und mehr über Umgebungsvariabeln optimieren

export RUBY_HEAP_MIN_SLOTS=800000
export RUBY_HEAP_FREE_MIN=100000
export RUBY_HEAP_SLOTS_INCREMENT=300000
export RUBY_GC_MALLOC_LIMIT=79000000
export RUBY_FREE_MIN=200000
export RUBY_HEAP_SLOTS_GROWTH_FACTOR=1.8

Mit einem Wrapperscript kann man diese auch für die vom Passenger gestarteten Ruby Prozesse verfügbar machen.

vi /usr/local/my_ruby_wrapper_script

Das Script soll so aussehen.

#!/bin/sh
export RUBY_HEAP_MIN_SLOTS=800000
export RUBY_HEAP_FREE_MIN=100000
export RUBY_HEAP_SLOTS_INCREMENT=300000
export RUBY_GC_MALLOC_LIMIT=79000000
export RUBY_FREE_MIN=200000
export RUBY_HEAP_SLOTS_GROWTH_FACTOR=1.8
export LD_PRELOAD=/usr/lib/libtcmalloc_minimal.so.0.1.0
exec "/usr/local/rvm/wrappers/ruby-1.9.3-p327/ruby" "$@"

Nun noch ausführbar machen…

chmod +x /usr/local/my_ruby_wrapper_script

und in der Passenger Konfiguration angeben indem man in /etc/apache2/mods-enabled/passenger.conf

   PassengerRoot /usr/local/rvm/gems/ruby-1.9.3-p327/gems/passenger-3.0.9

ersetzt mit

   PassengerRuby /usr/local/my_ruby_wrapper_script

« Ältere Einträge