Things I learned today

Howto: SSL-verschlüsselter Zugriff einer Ruby on Rails-Applikation auf eine remote MySQL Datenbank

Heute hatte ich eine besonders interessante Aufgabe: Ich habe eine lokale Rails-Applikation, die auf eine remote MySQL-Datenbank über ein Netzwerk zugreifen muss. Da das öffentlich über das Netz geht, soll der Datentransfer natürlich verschlüsselt ablaufen. Damit das Ganze läuft muss man einige Dinge beachten.

  • Wie kann man mit Rails per SSL auf eine remote Datenbank zugreifen?
  • Was muss bei MySQL eingestellt werden, um SSL Zugriffe zu erlauben?
  • Wie werden die entsprechenden Zertifikate erstellt?
  • Wie muss die Firewall eingerichtet sein?

Eine nützliche Quelle für diese Aufgabe war diese Anleitung, in der beschrieben wird, wie man MySQL und rails konfiguriert. Ich habe noch eine weitere Quelle genutzt, die ebenfalls den remote Zugriff auf eine MySQL-Datenbank behandelt. Hier wird zusätzlich auf die IP-Tables-Konfiguration eingegangen. SSL wird dort allerdings nicht behandelt.

MySQL

Kümmern wir uns zuerst um MySQL. Am Anfang sollte man prüfen, ob die MySQL-Installation überhaupt SSL unterstützt. Dies kann man in der lokalen MySQL-Konsole testen:

mysql> SHOW VARIABLES LIKE 'have_ssl';
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| have_ssl      | YES   | 
+---------------+-------+

Steht an dieser Stelle YES oder DISABLED ist alles gut und man kann weiter machen. Ansonsten muss man zuerst eine MySQL-Version mit SSL-Unterstützung installieren, worauf ich hier jedoch nicht eingehe.

Damit MySQL überhaupt remote Zugriffe erlaubt, muss die Konfigurationsdatei /etc/mysql/my.cfg angepasst werden.
Bei dem Eintrag bind-address steht üblicherweise 127.0.0.1. Damit remote Zugriffe erlaubt werden, muss hier aber die öffentliche IP des Servers stehen.

bind-address  = 256.256.256.256 #hier die eigene ServerIP eintragen
#bind-address  = 127.0.0.1 # - alter Eintrag

Nun noch ein

/etc/init.d/mysql restart

und testen ob noch alles läuft.
Falls man mehrere Anwendungen hat, die auf die MySQL-Datenbank zugreifen, kann es durchaus sein, dass die ein oder andere mit den neuen Einstellungen nicht mehr läuft. Wenn man eine findet, die plötzlich nicht mehr geht, kann das diverse Ursachen haben. Z.B. falsche Rechte des users in der MySQL user table oder falsche Verbindungseinstellungen (Can’t connect to MySQL server on ‘127.0.0.1’ (111)).

Läuft (wieder) alles, werden nun die Zertifikate und Keys erzeugt. Dies ist recht gut in der MySQL Dokumentation erläutert.

Generieren des CA Zertifikats:

openssl genrsa 2048 > ca-key.pem
openssl req -new -x509 -nodes -days 1000 -key ca-key.pem -out ca-cert.pem

Erstellen des Server Zertifikats:

openssl req -newkey rsa:2048 -days 1000 -nodes -keyout server-key.pem -out server-req.pem
openssl x509 -req -in server-req.pem -days 1000 -CA ca-cert.pem -CAkey ca-key.pem -set_serial 01 -out server-cert.pem

Erstellen des Client Zertifikats:

openssl req -newkey rsa:2048 -days 1000 -nodes -keyout client-key.pem -out client-req.pem
openssl x509 -req -in client-req.pem -days 1000 -CA ca-cert.pem -CAkey ca-key.pem -set_serial 01 -out client-cert.pem

Es ist relativ egal, was man bei den ganzen Abfragen eingibt, zumindest sofern man die Zertifikate nur selbst benutzen möchte. Passwort sollte hier keins benutzt werden!

Nun kopiert man die Datei ca-cert.pem server-cert.pem und server-key.pem in das MySQL-Konfigurationsverzeichnis.

mkdir /etc/mysql/ssl
mv ca-cert.pem  server-cert.pem  server-key.pem /etc/mysql/ssl

Hier sollte man unbedingt darauf achten, dass die Files nur die notwendigen Rechte (chmod und chown) haben, also nur der MySQL-User Lesezugriff hat und sonst niemand. Jeder der den Key kennt, kann ansonsten aufgrund des fehlenden Passwort eine SSL-Verbindung zu MySQL aufbauen.

Diese Dateien müssen nun in der MySQL-Konfigurationsdatei angegeben werden. Also noch einmal im Lieblingseditor /etc/mysql/my.cnf aufrufen und folgendes eintragen:

ssl-ca=/etc/mysql/ssl/ca-cert.pem
ssl-cert=/etc/mysql/ssl/server-cert.pem
ssl-key=/etc/mysql/ssl/server-key.pem

Anschließend darf man noch ein weiteres mal MySQL neustarten:

/etc/init.d/mysql restart

Ob alles geklappt hat und MySQL die angegeben Dateien auch findet und lesen kann, kann man so überprüfen:

mysql> show variables like '%ssl%';
+---------------+--------------------------------+
| Variable_name | Value                          |
+---------------+--------------------------------+
| have_openssl  | YES                            | 
| have_ssl      | YES                            | 
| ssl_ca        | /etc/mysql/ssl/ca-cert.pem     | 
| ssl_capath    |                                | 
| ssl_cert      | /etc/mysql/ssl/server-cert.pem | 
| ssl_cipher    |                                | 
| ssl_key       | /etc/mysql/ssl/server-key.pem  | 
+---------------+--------------------------------+

Firewall

Damit man das Ganze nun testen kann, muss man noch bei der Firewall, die man hoffentlich hat, den MySQL-Port 3306 öffnen. Bei iptables geht das so:

$iptables -I INPUT -i eth0 -p TCP -s $remoteip --sport 1024:65535 --dport 3306 -m state --state NEW,ESTABLISHED -j ACCEPT
$iptables -I OUTPUT -o eth0 -p TCP --sport 3306 -d $remoteip --dport 1024:65535 -m state --state ESTABLISHED -j ACCEPT

Statt der Variablen $remoteip trägt man einfach die IP ein, von der der Zugriff erlaubt sein soll. Mit diesen Zeilen kann dann auf den Port 3306 nur von der spezifizierten IP aus zugegriffen werden.

Test der Verbindung

Zum Testen der Verbindung muss man erst einen User anlegen, der auf die gewünschte Datenbank zugreifen darf. Also startet man als root den mysql client und legt einen neuen User an:

grant all privileges on datenbank-name.* to 'username'@'remote-ip' require ssl;
set password for 'username'@'remote-ip' = password('Passwort');
flush privileges;

Bei diesen Befehlen muss der eigene Datenbankname eingetragen werden, sowie der Benutzername, und die IP von der der Zugriff erlaubt sein soll. Ebenfalls sollte man sich natürlich ein vernünftiges Passwort einfallen lassen 🙂

Nun kann man die MySQL-Verbindung testen. Ein einfacher Test kann per telnet vorgenommen werden.

telnet example.com 3306

Oder aber man testet es gleich mit einem MySQL-Client. Bevor das geht, muss man allerdings die Dateien ca-cert.pem client-cert.pem und client-key.pem auf den Client kopieren. Nun kann man die Verbindung von Client aus testen:

mysql -uUsername -pPasswort -hHostname --ssl-ca=ca-cert.pem

Hier nimmt man den Username und das Passwort, dass man zuvor gewählt hat und gibt den Host an auf den die Datenbank läuft. Wenn alles glatt läuft, sollte man nun Zugriff auf die Datenbank haben.

Rails konfigurieren

Anschließend muss man nur noch in Rails die neuen Verbindungdaten einstellen und man ist fertig.
Also kopiert man die Dateien ca-cert.pem client-cert.pem und client-key.pem in den db Ordner des Rails-Projektes und editiert die database.yml wie folgt:

  host: remote-ip
  adapter: mysql
  encoding: utf8
  reconnect: false
  database: Datenbank-name
  pool: 5
  username: Username
  password: Passwort
  sslca: db/ca-cert.pem
  sslkey: db/client-key.pem
  sslcert: db/client-cert.pem

Und schon sollte die Rails Applikation vollen SSL-verschlüsselten Zugriff auf die entfernte My-SQL Datenbank haben. 😀

Ich bin bei dieser Anleitung den beiden oben angegeben Quellen gefolgt. Mir ist noch nicht ganz klar, ob die Client-Zertifikatdateien in dieser Konfiguration überhaupt benötigt werden. Wenn nicht, freu ich mich natürlich über Kommentare.

Quellen:
How Do I Enable Remote Access To MySQL Database Server?
Connecting to MySQL using SSL encryption in Ruby on Rails
MySQL Reference Manual: 5.5.6. Using SSL for Secure Connections

Post a Comment

Your email is kept private. Required fields are marked *