Table des matières

MySQL et SSL

Vous avez rêvé … MySQL n'a rien de sécurisé.
On va installer SSL sur MySQL afin d'authentifier et chiffrer les échanges.

~~READMORE~~

Certificats

Auto-signé

Sur le serveur.

# cd /etc/mysql
# mkdir certs
# cd certs

Génération de clé privée:

# openssl genrsa 2048 > mysql-ca-key.pem

Certificat auto-signé:

# openssl req -new -x509 -nodes -days 3650 -key mysql-ca-key.pem -out mysql-ca-cert.pem
...
Country Name (2 letter code) [AU]:FR
State or Province Name (full name) [Some-State]:
Locality Name (eg, city) []:Paris
Organization Name (eg, company) [Internet Widgits Pty Ltd]:Eez.fr Corp.
Organizational Unit Name (eg, section) []:     
Common Name (e.g. server FQDN or YOUR name) []:mail.local.eez.fr

Maintenant, on a 2 fichiers:

mysql-ca-key.pem
mysql-ca-cert.pem

Le fichier “key” n'a pas être lisible par tous !

# chmod 0400 mysql-ca-key.pem

… qui va permettre de signer les autres certificats: serveur et clients.

Certificat du serveur

Créer les clefs pour le serveur:

# openssl req -newkey rsa:2048 -days 3650 -nodes -keyout mysql-server-key.pem -out mysql-server-req.pem

Transformer cette clé en RSA !? hein?

# openssl rsa -in mysql-server-key.pem -out mysql-server-key.pem
writing RSA key

Signer:

# openssl x509 -req -in mysql-server-req.pem -days 3650 -CA mysql-ca-cert.pem -CAkey mysql-ca-key.pem -CAcreateserial -out mysql-server-cert.pem

-CAcreateserial” , la premiere fois, permet de créer un fichier (“mysql-ca-cert.srl”) qui va attribuer un “serial number” a chaque certificat.
Le premier “serial” est créé aléatoirement , puis incrémenté avec chaque nouveau certificat.

Les droits:
Le fichier “key” n'a besoin d'être lisible que par “root” et “mysql”.

# chmod 0440 mysql-server-key.pem
# chown :mysql mysql-server-key.pem

Certificat des clients

Créer les clefs pour les clients …
Client 1:

# openssl req -newkey rsa:2048 -days 3650 -nodes -keyout mysql-client_1-key.pem -out mysql-client_1-req.pem
# openssl rsa -in mysql-client_1-key.pem -out mysql-client_1-key.pem

Client 2:

# openssl req -newkey rsa:2048 -days 3650 -nodes -keyout mysql-client_2-key.pem -out mysql-client_2-req.pem
# openssl rsa -in mysql-client_2-key.pem -out mysql-client_2-key.pem

etc…

Signer les 2 clients:

# openssl x509 -req -in mysql-client_1-req.pem -days 3650 -CA mysql-ca-cert.pem -CAkey mysql-ca-key.pem -out mysql-client_1-cert.pem
# openssl x509 -req -in mysql-client_2-req.pem -days 3650 -CA mysql-ca-cert.pem -CAkey mysql-ca-key.pem -out mysql-client_2-cert.pem

On peut toujours vérifier le contenu d'un certificat avec la commande:

# openssl x509 -in <CERTICAT> -noout -text

MySql serveur

conf

Dans /etc/mysql/conf.d/local.cnf :

[mysqld]
ssl-ca=/etc/mysql/certs/mysql-ca-cert.pem
ssl-cert=/etc/mysql/certs/mysql-server-cert.pem
ssl-key=/etc/mysql/certs/mysql-server-key.pem
#ssl-crl=/etc/mysql/certs/mysql-server-revocation.pem

FIXME : Tester la revocation !

Tester vite

Si besoin, il faut redémarrer avec la nouvelle conf:

# /etc/init.d/mysql restart

Dans les logs, vérifier qu'on a pas :

Apr 11 02:07:09 mail mysqld: SSL error: Unable to get private key from '/etc/mysql/certs/mysql-server-key.pem'

… parce que MySQL est en train d'ignorer votre configuration SSL: votre fichier étant illisible.

Pour moi, c'était parce que j'avais oublié la ligne “magique”:

# openssl rsa -in <server-key>.pem -out <server-key>.pem

Sinon, vérifier comme cela:

$ mysql -u root -p
mysql> show variables like '%ssl%';
+---------------+----------------------------------------+
| Variable_name | Value                                  |
+---------------+----------------------------------------+
| have_openssl  | YES                                    |
| have_ssl      | YES                                    |
| ssl_ca        | /etc/mysql/certs/mysql-ca-cert.pem     |
| ssl_capath    |                                        |
| ssl_cert      | /etc/mysql/certs/mysql-server-cert.pem |
| ssl_cipher    |                                        |
| ssl_key       | /etc/mysql/certs/mysql-server-key.pem  |
+---------------+----------------------------------------+
7 rows in set (0.00 sec)

Si “have_openssl” ou “have_ssl” sont sur “DISABLED”, alors le SSL n'est toujours pas activé ! Une erreur s'est glissé quelque part !

Client en SSL

Aprés avoir transmis les 3 fichiers qui vont bien, on peut déjà ce connecter en SSL.

$ mysql -h <HOST> -u <USER> -p <DATABASE> \
--ssl-ca=mysql-ca-cert.pem \
--ssl-cert=mysql-client_1-cert.pem \
--ssl-key=mysql-client_1-key.pem

… mais rien ne nous force, encore, a utiliser SSL.

mysql> status
...
SSL:                    Cipher in use is DHE-RSA-AES256-SHA
...

SSL seulement

GRANT

mysql> GRANT SELECT ON <DATABASE>.* TO <USER>@'%' IDENTIFIED BY '<PASSWORD>' REQUIRE SSL;
<DATABASE> le nom de la base de données
<USER> l'utilisateur
<PASSWORD> le mot de passe
$ mysql -h <HOST> -u <USER> -p <DATABASE>
Enter password: 
ERROR 1045 (28000): Access denied for user 'trouduc'@'mx0.local.eez.fr' (using password: YES)

Maintenant, on est obligé d'utiliser SSL pour ce connecter.

:!: Cette erreur “ERROR 1045 (28000)…” revient systématiquement en cas de problème SSL.
Les développeurs l'ont volontairement rendu peu parlante pour ne pas aider les vilains… Et effectivement, ça n'aide personne ! LOL

X509

Remplacer SSL par X509, mais ce n'est pas trés utile comme cela.

On va forcer une vérification du “Issuer” ou du “Subject” dans le certificat du client.

Pour extraire le “Issuer” :

$ openssl x509 -issuer -noout -in <CLIENT_CERT>.pem

Pour extraire le “Subject” :

$ openssl x509 -subject -noout -in <CLIENT_CERT>.pem

Exemple:

GRANT ALL PRIVILEGES ON <DATABASE>.* TO <USER>@'%' IDENTIFIED BY <PASSWORD> \
  REQUIRE SUBJECT '/C=FR/ST=Some-State/L=Paris/O=xxx/OU=xxx/CN=xxx/emailAddress=xxx' \
  AND ISSUER '/C=FR/ST=Some-State/L=Paris/O=xxx/OU=xxx/CN=xxx/emailAddress=xxx';

Sources