MySQL mit SSL

Ziel: MySQL mit SSL im master-master setup

Die Geschichte des SSL mit MySQL ist eine Geschichte voller Mißverständnisse.
Es funktioniert. Ist aber schmerzhaft zu implementieren. Dieser Anleitung ist ergo leider strikt zu folgen. 🙁

Besondere Ärgernisse/Fallstricke:
– Es müssen pro Instanz und Replikationsuser je ein eigenes Zertifikat (also hier zwei mal zwei, wenn noch slaves dazu kommen jeweils noch ein weiteres) – selbes für clients (da könnte es aber, im Notfall, reichen ein einziges zu nutzen)
– Als Cipher funktionierte nur „DHE-RSA-AES256-SHA“ und nix besseres
– Fehlermeldungen sind, wenn überhaupt, allesamt verwirrend/nicht zielführend

 

Vorbedingungen:

Installation einer MySQL 5.1 oder höher.
Alle Datenbanken residieren ab /db

 

SSL Root-CA und Zertifikate:
Die Dateinamen lasse ich mit „inst01“ bzw. „inst02“ für Instanz 1/2 anfangen.

# Zuerst alle SSL-Keys: Root-CA, Instanzen und die Replikationsuser pro Instanz
openssl genrsa -out /db/mysql-ca.key 2048 && \
openssl genrsa -out /db/inst01.key 2048 && \
openssl genrsa -out /db/inst02.key 2048 && \
openssl genrsa -out /db/inst01_repl.key 2048 && \
openssl genrsa -out /db/inst02_repl.key 2048

# Nun die einzelnen Zertifikate, beginnend mit dem Root-CA-Zertifikat
openssl req -new -x509 -nodes -days 3650 -key /db/mysql-ca.key -out /db/mysql-ca.crt

# Instanz 01
openssl req -new -key /db/inst01.key -out inst01.csr
openssl x509 -req -in inst01.csr -days 3650 -CA /db/mysql-ca.crt -CAkey /db/mysql-ca.key -set_serial 01 -out /db/inst01.crt

# Instanz 02
openssl req -new -key /db/inst02.key -out inst02.csr
openssl x509 -req -in inst02.csr -days 3650 -CA /db/mysql-ca.crt -CAkey /db/mysql-ca.key -set_serial 01 -out /db/inst02.crt

# Instanz 01 Replikationsuser
openssl req -new -key /db/inst01_repl.key -out inst01_repl.csr
openssl x509 -req -in inst01_repl.csr -days 3650 -CA /db/mysql-ca.crt -CAkey /db/mysql-ca.key -set_serial 01 -out /db/inst01_repl.crt

# Instanz 02 Replikationsuser
openssl req -new -key /db/inst02_repl.key -out inst02_repl.csr
openssl x509 -req -in inst02_repl.csr -days 3650 -CA /db/mysql-ca.crt -CAkey /db/mysql-ca.key -set_serial 01 -out /db/inst02_repl.crt

# Prosa: Löschen CSRs, chmod & chgrp
rm *.csr
chmod 440 /db/*.key
chmod 444 /db/*.crt
chgrp mysql /db/*.key /db/*.crt

Als CNs für die Zertifikate habe ich folgendes benutzt:
* CA = mysql-ca
* Instance01 = inst01
* Instance01 replication = inst01_replication
* Instance02 = inst02
* Instance02 replication = inst02_replication

Man kann da angeben was man möchte, es muss nur unterschiedlich sein! Meiner Meinung nach was Sinnvolles was sich hinterher auch zuordnen lässt. 😉

Dazu kommen dann jetzt noch für jeden Client nochmal Zertifikate die mit der erstellten mysql-ca signiert sind.

 

Konfig der MySQL (/etc/my.cnf):

Wichtig, für SSL, hier sind die folgenden Zeilen:

ssl
ssl-cert = /db/inst01.crt
ssl-key = /db/inst01.key
ssl-ca = /db/mysql-ca.crt
ssl-cipher = DHE-RSA-AES256-SHA

Vollständig nutzbare Konfig:

[mysqld]
port = 3306
socket = /var/run/mysqld/mysqld.sock
ssl
ssl-cert = /db/inst01.crt
ssl-key = /db/inst01.key
ssl-ca = /db/mysql-ca.crt
ssl-cipher = DHE-RSA-AES256-SHA
datadir = /db/
symbolic-links = 0
general_log = 1
general-log_file = /var/log/mysql/mysql01.log
log_error = /var/log/mysql/mysql01-error.log
slow-query-log = 1
slow-query-log-file = /var/log/mysql/mysql01-slow.log
log-queries-not-using-indexes
innodb=1
local-infile=0
# Master-Master instanz 01
auto-increment-increment = 2
auto-increment-offset = 1
# Master-Master instanz 02
#auto-increment-increment = 2
#auto-increment-offset = 2
# Replication
server-id = 1
log-bin=/db/mysql-bin
binlog_format = mixed
binlog_do_db = YOURDATABASETOREPLICATE
expire_logs_days=31
sync_binlog=1
log-slave-updates

# Fuer jeden client sein eigenes Zertifikat, auf dem jeweiligen Server selbst reicht/funktioniert das Zertifikat der Instanz!
[client]
socket = /var/run/mysqld/mysqld.sock
ssl-cert=/db/inst01.crt
ssl-key=/db/inst01.key
ssl-ca=/db/mysql-ca.crt

Entsprechend dann für die zweite Instanz. Z.B.: „cat /db/inst01_my.cnf | sed ’s/01/02/‘ > /db/inst02_my.cnf“.
Und anschließend, bei der zweitzen Instanz, die server-id und auto_increment_offset auf „2“ setzen!

Anschließend kann die MySQL ge(re)startet werden.

 

Replikationsuser und Replikation via SSL:

Login in beide MySQL-Instanzen als root

#Instanz01

mysql> create user ‚replication’@’YOURLOCALNET‘ identified by ‚REPLICATIONPASSWORD‘;
mysql> GRANT REPLICATION SLAVE ON *.* to ‚replication’@’YOURLOCALNET‘ identified by ‚REPLICATIONPASSWORD‘ REQUIRE SSL;
mysql> flush privileges;
mysql> show variables like ‚%ssl%‘;
mysql> SHOW MASTER STATUS;
mysql> slave stop;
# Take the values for MASTER_LOG_FILE and MASTER_LOG_POS from the other machine „SHOW MASTER STATUS“ output
mysql> CHANGE MASTER TO MASTER_HOST = ‚IPOFMASTERHOST2‘, MASTER_USER = ‚replication‘, MASTER_PASSWORD = ‚REPLICATIONPASSWORD‘, MASTER_SSL = 1, MASTER_SSL_CERT = ‚/db/inst01_repl.crt‘, MASTER_SSL_KEY = ‚/db/inst01_repl.key‘, MASTER_SSL_CA = ‚/db/mysql-ca.key‘, MASTER_SSL_CAPATH = “, MASTER_LOG_FILE = ‚mysql-bin.000001‘, MASTER_LOG_POS = 107;
mysql> slave start;
mysql> show slave status;

#Instanz02

mysql> create user ‚replication’@’YOURLOCALNET‘ identified by ‚REPLICATIONPASSWORD‘;
mysql> GRANT REPLICATION SLAVE ON *.* to ‚replication’@’YOURLOCALNET‘ identified by ‚REPLICATIONPASSWORD‘ REQUIRE SSL;
mysql> flush privileges;
mysql> show variables like ‚%ssl%‘;
mysql> SHOW MASTER STATUS;
mysql> slave stop;
# Take the values for MASTER_LOG_FILE and MASTER_LOG_POS from the other machine „SHOW MASTER STATUS“ output
mysql> CHANGE MASTER TO MASTER_HOST = ‚IPOFMASTERHOST1‘, MASTER_USER = ‚replication‘, MASTER_PASSWORD = ‚REPLICATIONPASSWORD‘, MASTER_SSL = 1, MASTER_SSL_CERT = ‚/db/inst02_repl.crt‘, MASTER_SSL_KEY = ‚/db/inst02_repl.key‘, MASTER_SSL_CA = ‚/db/mysql-ca.key‘, MASTER_SSL_CAPATH = “, MASTER_LOG_FILE = ‚mysql-bin.000001‘, MASTER_LOG_POS = 107;
mysql> slave start;
mysql> show slave status;

Restart der beiden Instanzen damit die Replikation auch wirklich via SSL aktiviert wird.

 

Anschließend relogin auf eine der Instanzen sollte beim „show variables like ‚%ssl%'“ folgendes Zeigen:

mysql> show variables like ‚%ssl%‘;

+---------------+------------------------+
| Variable_name | Value                  |
+---------------+------------------------+
| have_openssl  | YES                    |
| have_ssl      | YES                    |
| ssl_ca        | /db/mysql-ca.crt       |
| ssl_capath    |                        |
| ssl_cert      | /db/inst01.crt         |
| ssl_cipher    | DHE-RSA-AES256-SHA     |
| ssl_key       | /db/inst01.key         |
+---------------+------------------------+
7 rows in set (0.00 sec)

mysql>

Wichtig hierbei ist einzig die Zeile „ssl_cipher“. Wenn hier „DHE-RSA-AES256-SHA“ angezeigt wird ist die connection SSL gesichert! „have_ssl“ ist völlig uninterssant und besagt nur das SSL möglich wäre, nicht das es auch benutzt wird!

Dies sollte optimaler weise auch von den Clients aus angezeigt werden.

 

Das Umstellen einer vorhandenen Infrastruktur auf SSL sollte, wenn richtig gemacht, sogar ohne große downtime funktionieren!

 

Ja… das wars eigentlich… Fertig. 🙂