mercredi 23 janvier 2013

Configuration SSL pour MySQL sous OpenSuse

L'environnent technique des serveurs est le suivant :
  • OS : OpenSuse 12.1
  • Base de données : MySQL Community Server 5.5.28
  • Librairie SSL : OpenSSL 1.0 

0°) Introduction.

Comme je l'ai expliqué dans mon précédent post (Serveurs physiques Versus Serveurs virtuels), nous avons migrer nos serveurs physiques vers des serveurs virtuels. Un des premiers dommages collatéral a été que l'ensemble de nos serveurs ne soient plus sur la même plage de réseau IP et que nous ne puissions plus installer une réseau privé (192.168.0.0/16) entre nos serveurs.

Bien sur, j'aurais pu mettre en place un réseau privé virtuel (VPN) pour faire transiter l'ensemble du trafic interne. Mais j'ai eu la flemme car la plus part des échanges sont fait à travers du ssh et le seul trafic en clair qui était concerné était la réplication de mes bases de données sous MySQL. Donc, je me suis lancé dans la mise en place de connexions sécurisé via SSL pour MySQL.
A priori (d'après ce que j'aivais pu lire) MySQL crypte par défaut l'échange des mots de passe lors de l'établissement d'une connexion. Par contre, une fois la connexion établie, les données transitaient en clair. Même si ces données ne revêtent pas un caractère hyper-sensible, certaines informations doivent demeurer confidentielles. Il devient alors nécessaire de crypter l'ensemble des échanges afin de pouvoir garantir la confidentialité des données échangées.

 

 1°) Dans le vif du sujet.

Pour réaliser cette mise en place, je me suis fortement inspiré de l'article "Sécurisez votre serveur MySQL sous Unix".

Même si on trouve pléthore d'articles sur le net, je vais quand même détailler les différentes étapes que effectuer car j'ai rencontré quelques difficultés qui n'ont pas été toujours simple à résoudre.

 

 1.1°) Création des certificats SSL.

Le premier travail consiste à créer les certificats qui vont être utilisés par MySQL pour le cryptage des communications entre les serveurs.
Il faut commencer par créer un premier certificat qui va nous servir à signer les 2 autres certificats (un certifcat serveur et un certificat client). C'est notre autorité de certification.  Le premier piège est qu'il est indispensable que le CN (Common Name) du certifcat de notre autorité de certification soit différent des autres certificats. Cela semble évident, mais lorsque on autosigne un certificat, on a tendance à donner le même CN. Du moins c'est ce que j'ai fait et j'ai eu énormément de mal ensuite à faire fonctionner la connexion SSL avec MySQL. Voici les lignes de commande pour la création des différents certificats :
diplodocus# cd /etc/ssl/certs
diplodocus# mkdir mysql
diplodocus# cd mysql
diplodocus# openssl req -new -x509 -keyout ca-key.pem -out ca-cert.pem -days 3600

Generating a 1024 bit RSA private key
.......................++++++
...++++++
writing new private key to 'ca-key.pem'
Enter PEM pass phrase:
Verifying - Enter PEM pass phrase:
-----
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Country Name (2 letter code) [AU]:FR
State or Province Name (full name) [Some-State]:Ile de France
Locality Name (eg, city) []:PARIS
Organization Name (eg, company) [Internet Widgits Pty Ltd]:NAOS Technologies
Organizational Unit Name (eg, section) []:MySQL
Common Name (eg, YOUR name) []:www.naos.com
 
<--- ATTENTION : il est très important que le common name du CA soit différent de ceux des certificats (Voir erreur sinon en bas de page).

Email Address []:alain.tomasian@naos.com

diplodocus# openssl req -new -keyout server-key.pem -out server-req.pem -days 3600
Generating a 1024 bit RSA private key
.....++++++
..........++++++
writing new private key to 'server-key.pem'
Enter PEM pass phrase:
Verifying - Enter PEM pass phrase:
-----
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Country Name (2 letter code) [AU]:FR
State or Province Name (full name) [Some-State]:Ile de France
Locality Name (eg, city) []:PARIS
Organization Name (eg, company) [Internet Widgits Pty Ltd]:NAOS Technologies
Organizational Unit Name (eg, section) []:MySQL
Common Name (eg, YOUR name) []:*.naos.com
Email Address []:alain.tomasian@naos.com

Please enter the following 'extra' attributes
to be sent with your certificate request
A challenge password []:
An optional company name []:NAOS Technologies


- On signe le certificat serveur avec la clé de l'autorité de certification que l'on vient de créer :
diplodocus# openssl x509 -req -in server-req.pem -days 3600 -CA ca-cert.pem -CAkey ca-key.pem -set_serial 01 -out server-cert.pem
Signature ok
subject=/C=FR/ST=Ile de France/L=PARIS/O=NAOS Technologies/OU=MySQL/CN=*.naos.com/emailAddress=alain.tomasian@naos.com
Getting CA Private Key
Enter pass phrase for ca-key.pem:
Generating a 1024 bit RSA private key
...............++++++
..............................++++++
writing new private key to 'client-key.pem'
Enter PEM pass phrase:
Verifying - Enter PEM pass phrase:
-----
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Country Name (2 letter code) [AU]:FR
State or Province Name (full name) [Some-State]:Ile de France
Locality Name (eg, city) []:PARIS
Organization Name (eg, company) [Internet Widgits Pty Ltd]:NAOS Technologies
Organizational Unit Name (eg, section) []:MySQL
Common Name (eg, YOUR name) []:*.naos.com
Email Address []:alain.tomasian@naos.com

Please enter the following 'extra' attributes
to be sent with your certificate request
A challenge password []:
An optional company name []:NAOS Technologies


- On signe le certificat client avec la clé de l'autorité de certification que l'on vient de créer :
diplodocus# openssl x509 -req -in client-req.pem -days 3600 -CA ca-cert.pem -CAkey ca-key.pem -set_serial 01 -out client-cert.pem
Signature ok
subject=/C=FR/ST=Ile de France/L=PARIS/O=NAOS Technologies/OU=MySQL/CN=*.naos.com/emailAddress=alain.tomasian@naos.com
Getting CA Private Key
Enter pass phrase for ca-key.pem:


- On supprime la pass phrase sur les deux certificats :
diplodocus# openssl rsa -in server-key.pem -out server-key.pem
Enter pass phrase for server-key.pem:
writing RSA key
diplodocus# openssl rsa -in client-key.pem -out client-key.pem 
Enter pass phrase for client-key.pem:
writing RSA key

Avant de continuer, on vérifie que les certificats sont ok en passant la commande :
diplodocus# openssl verify -CAfile ca-cert.pem server-cert.pem
server-cert.pem: C = FR, ST = Ile de France, L = PARIS, O = NAOS Technologies, OU = MySQL, CN = *.naos.com, emailAddress = alain.tomasian@naos.com
error 18 at 0 depth lookup:self signed certificate
OK

Si vous avez le résultat ci-dessus, c'est que vous avez un souci. Sinon vous devriez avoir :
diplodocus# openssl verify -CAfile ca-cert.pem server-cert.pem

server-cert.pem: OK
diplodocus# openssl verify -CAfile ca-cert.pem client-cert.pem

client-cert.pem: OK

 

1.2°) Configuration du serveur MySQL.

Voilà, les certificats sont créés, il nous reste maintenant à configurer MySQL pour qu'il puisse gérer les transactions cryptées.
Dans le fichier /etc/my.cf, ajoutons dans le bloc [mysqld] les lignes suivantes :
ssl
ssl-cipher=DHE-RSA-AES256-SHA
ssl-ca=/etc/ssl/certs/mysql/ca-cert.pem
ssl-cert=/etc/ssl/certs/mysql/server-cert.pem
ssl-key=/etc/ssl/certs/mysql/server-key.pem

et dans le bloc [client], ajoutons les lignes suivantes :
ssl-ca = /etc/ssl/certs/mysql/ca-cert.pem
On redémarre le serveur MySQL :
diplodocus# /etc/init.d/mysql restart
Restarting service MySQL
Shutting down service MySQL done
Starting service MySQL done

Si tous se passe bien, on devrait avoir le résultat suivant sous la console MySQL:
diplodocus# mysql -u root -p
Enter password:
Welcome to the MySQL monitor. Commands end with ; or \g.
Your MySQL connection id is 1
Server version: 5.5.28-log Source distribution
Copyright (c) 2000, 2011, Oracle and/or its affiliates. All rights reserved.
Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.
Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.
 

mysql> SHOW VARIABLES LIKE '%ssl%';
+-----------------+-----------------------------------------+
| Variable_name   | Value                                   |
+-----------------+-----------------------------------------+
| have_openssl    | YES
                                     |
| have_ssl        | YES
                                     |
| ssl_ca          | /etc/ssl/certs/mysql/ca-cert.pem        |
| ssl_capath      | /etc/ssl/certs/mysql                    |
| ssl_cert        | /etc/ssl/certs/mysql/server-cert.pem    |
| ssl_cipher      | DHE-RSA-AES256-SHA                      |
| ssl_key         | /etc/ssl/certs/mysql/server-key.pem     |
+-----------------+-----------------------------------------+
7 rows in set (0.01 sec)

 

1.3°) Tester la connexion SSL en local.

A présent, il faut tester la connexion ssl. D'abord, nous allons tester le fonctionnement en local. Pour cela nous avons besoin de créer un utilisateur qui ne peut se connecter qu'en utilisant une connexion sécurisée.
mysql> GRANT ALL PRIVILEGES ON *.* TO 'bidon'@'localhost' IDENTIFIED BY "xxxxxxxx" REQUIRE SSL;
Query OK, 0 rows affected (0.00 sec)
mysql> flush privileges ;
Query OK, 0 rows affected (0.00 sec)

Pour tester la connexion il suffit de passer la commande suivante, pour une connexion simple :
# mysql -u bidon -p
Enter password:
ERROR 1045 (28000): Access denied for user 'test'@'localhost' (using password: YES)

On peut remarquer que la connexion échoue. Maintenant en utilisant une connexion sécurisée :
# mysql -u bidon -p --ssl-ca=/etc/ssl/certs/mysql/ca-cert.pem
Enter password:
Welcome to the MySQL monitor. Commands end with ; or \g.
Your MySQL connection id is 24
Server version: 5.5.25-log Source distribution
Copyright (c) 2000, 2011, Oracle and/or its affiliates. All rights reserved.
Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.
Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

mysql> quit

Et ô miracle ça fonctionne. Maintenant, tentons la connexion sécurisée depuis un serveur distant.

 

 1.4°) Tester la connexion SSL depuis un serveur distant. 

Notre serveur local est nommé "diplodocus" et le serveur distant "triceratops". Sur diplodocus il faut autoriser un utilisateur à se connecter en SSL depuis triceratops :
mysql> GRANT ALL PRIVILEGES ON bidon.* TO 'bidon'@'triceratops' IDENTIFIED BY "xxxxxxxxx" REQUIRE SSL;
Query OK, 0 rows affected (0.00 sec)
mysql> flush privileges ;
Query OK, 0 rows affected (0.00 sec)

Il faut à présent copier l'ensemble des certificats situé dans le répertoire /etc/certs/mysql/ sur le même répertoire sur triceratops :
triceratops# cd /etc/ssl/certs/mysql
triceratops# scp root@diplodocus.naos.com:/etc/ssl/certs/mysql/client-key.pem .
triceratops# scp root@diplodocus.naos.com:/etc/ssl/certs/mysql/server-cert.pem .
triceratops# scp root@diplodocus.naos.com:/etc/ssl/certs/mysql/client-cert.pem .

Un fois ce travail effecuté, il suffit de tester la connexion distante :
triceratops# mysql -u bidon -p --ssl-ca=/etc/ssl/certs/mysql/ca-cert.pem -h diplodocus
Enter password:
Welcome to the MySQL monitor. Commands end with ; or \g.
Your MySQL connection id is 25
Server version: 5.5.25-log Source distribution
Copyright (c) 2000, 2011, Oracle and/or its affiliates. All rights reserved.
Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.
Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

mysql> quit
Bye


Et voilà, le tour est joué...

 

2°) Analyse du trafic réseau pour valider la connexion SSL.

Mais bon comment être sur maintenant que les échanges entre le client et le serveur sont bien crypté ?
Ben, il suffit de regarder ce qui se passe sur le réseau entre les deux machine. Pour cela, j'ai trouvé sur le net un outil permettant d'analyser les paquets liés au trafic réseau MySQL sur une interface : mysqlsniffer.
Voici les étapes pour installer, compiler et utiliser mysqlsniffer (inspiré de : Utilisation de mysqlsniffer) :
diplodocus# mkdir -p /usr/local/src/mysqlsniffer
diplodocus# cd /usr/local/src/mysqlsniffer
diplodocus# wget hackmysql.com/code/mysqlsniffer.tgz
diplodocus# tar xfvz mysqlsniffer.tgz
diplodocus# gcc -O2 -lpcap -o mysqlsniffer mysqlsniffer.c packet_handlers.c misc.c

mysqlsniffer.c:26:18: fatal error: pcap.h: No such file or directory

Là, on se rend compte qu'il manque une librairie :
# zypper install libpcap-devel
# gcc -O2 -lpcap -o mysqlsniffer mysqlsniffer.c packet_handlers.c misc.c
# ls -lt

total 136
-rwxr-xr-x 1 root root 35223 Jan 22 15:50 mysqlsniffer

..............

Ensuite il faut lancer la commande et se connecter à la base distante depuis un autre console:# ./mysqlsniffer -p 3306 eth0
mysqlsniffer listening for MySQL on interface eth0 port 3306
.....................
::FRAGMENT START::
::FRAGMENT END::

......................

Les
::FRAGMENT START:: et ::FRAGMENT END:: montre que la connexion est cryptée, sinon on aurait :# ./mysqlsniffer -p 3306 eth0
mysqlsniffer listening for MySQL on interface eth0 port 3306
server > xx.xx.xx.xx.57842: ID 0 len 78 Handshake <proto 10 ver 5.5.28-log thd 18>
xx.xx.xx.xx.57842 > server: ID 1 len 80 Handshake (new auth) <user bidon db (null) max pkt 16777216>
server > xx.xx.xx.xx.57842: ID 2 len 7 OK <fields 0 affected rows 0 insert id 0 warnings 0>
xx.xx.xx.xx.57842 > server: ID 0 len 33 COM_QUERY: select @@version_comment limit 1
server > xx.xx.xx.xx.57842: ID 1 len 1 1 Fields
ID 2 len 39 Field: ..@@version_comment <type var string (253) size 19>
ID 3 len 5 End <warnings 0>
ID 4 len 20 || Source distribution ||
ID 5 len 5 End <warnings 0>
xx.xx.xx.xx.57842 > server: ID 0 len 18 COM_QUERY: SELECT DATABASE()
server > xx.xx.xx.xx.57842: ID 1 len 1 1 Fields
ID 2 len 32 Field: ..DATABASE() <type var string (253) size 34>
ID 3 len 5 End <warnings 0>
ID 4 len 1 || NULL ||
ID 5 len 5 End <warnings 0>
xx.xx.xx.xx.57842 > server: ID 0 len 5 COM_INIT_DB: bidon
server > xx.xx.xx.xx.57842: ID 1 len 7 OK <fields 0 affected rows 0 insert id 0 warnings 0>
server > xx.xx.xx.xx.44498: ID 0 len 72 Handshake <proto 10 ver 5.5.8 thd 72804>
xx.xx.xx.xx.44498 > server: ID 1 len 85 Handshake (new auth) <user bidon db bidon max pkt 1073741824>
server > xx.xx.xx.xx.44498: ID 2 len 7 OK <fields 0 affected rows 0 insert id 0 warnings 0>
xx.xx.xx.xx.44498 > server: ID 0 len 1 COM_STATISTICS
server > xx.xx.xx.xx.44498: ID 1 len 143 Uptime: 4611470 Threads: 1 Questions: 16883155 Slow queries: 1 Opens: 183 Flush tables: 1 Open tables: 152 Queries per second avg: 3.661
xx.xx.xx.xx.44498 > server: ID 0 len 1 COM_QUIT
xx.xx.xx.xx.57842 > server: ID 0 len 12 COM_QUERY: show tables
server > xx.xx.xx.xx.57842: ID 1 len 1 1 Fields
ID 2 len 86 Field: information_schema.TABLE_NAMES.Tables_in_bidon <type var string (509) size 64>
ID 3 len 5 End <warnings 0>
ID 4 len 5 End <warnings 0>
xx.xx.xx.xx.57842 > server: ID 0 len 1 COM_QUIT


Voilà, l'affaire est dans le sac.

 

3°) Conclusions

Nous voilà maintenant paré, pour mettre en place la réplication entre serveurs MySQL situés sur  réseaux différents. Il reste quand même évident que ce travail vous assure la confidentialité des données échangées, mais n'assure en aucun cas la sécurisation du serveur de base de données qu'est MySQL : c'est un autre sujet.... qui est lui lié à la mise en place d'un pare-feu et d'une vrai politique de sécurité réseau.

Quelques liens intéressant :
- Connexion sécurisée à MySQL (chicoree.fr).
- Sécurisez votre serveur MySQL sous Unix ( developpez.com).

Aucun commentaire:

Publier un commentaire