viernes 29 de agosto de 2008

Mysql con SSL. Reforzando la seguridad.

En esta entrada muestro configurar un servidor mysql y uno (o varios) clientes para realizar las conexiones mediante ssl.

NOTA: Aclaro que toda la información necesaria la saqué directamente de la documentación oficial de mysql.

REQUERIMIENTOS:
Para hacer todo necesitaremos:
1. Servidor mysql (compilado con soporte a yaSSL).
2. Cliente mysql (compilado con soporte a yaSSL).
3. OpenSSL.
4. Ganas de intentar.

MANOS A LA OBRA:
Básicamente son 3 pasos:
1. Crear los cerficados y claves privadas para:
- El CA (servidor mismo en este caso),
- Servidor.
- Cliente(s).
2. Ejecutar el servidor, indicandole los parámetros indicados.
3. Verificar y corregir bug que nos impediría la conexion con ssl.
4. Añadir las restricciones de seguridad por usuario:
- Nivel 1: Solo asegura la identidad del servidor.
- Nivel 2: Obliga al cliente a validar la identidad del servidor.
- Nivel 3: Asegura que cada usuario está certificado por el CA (sin importar quien sea), además de hacer lo que hace el nivel 1.
- Nivel 4: Asegura lo mismo que 1, y añade que cada usuario debe si o sí obtener su certificado y clave privada para poder conectar con el servidor.
5. Conectar el cliente al servidor.


Paso 1: Crear los cerficados y claves privadas.

Creando certificados:
CA,
mysqladmin@gaspar:~/mysql_certs$ openssl genrsa 2048 > ca-key.pem
mysqladmin@gaspar:~/mysql_certs$ openssl req -new -x509 -nodes -days 1000 -key ca-key.pem > ca-cert.pem

Y respondemos a todas las preguntas...
Con eso generamos la clave privada y el certificado del CA (entidad de certificación) que este caso seremos nosotros mismos. Todo esto se podría haber hecho en un solo paso como se muestra a continuación con el primer comando invocado.

-SERVER
mysqladmin@gaspar:~/mysql_certs$ openssl req -newkey rsa:2048 -days 1000 -nodes -keyout server key.pem > server-req.pem
mysqladmin@gaspar:~/mysql_certs$ openssl x509 -req -in server-req.pem -days 1000 -CA ca-cert.pem -CAkey ca-key.pem -set_serial 01 > server-cert.pem
Es importante entender que en el primer paso generamos la clave privada y el archivo con los datos de identidad del servidor (req), y que en el seguno paso lo que hacemos es firmar este archivo req con la clave privada del CA (y además agregamos el certificado de este), generando el certificado del servidor:
"server-cert.pem".

-CLIENTE

Un primer cliente.
mysqladmin@gaspar:~/mysql_certs$ openssl req -newkey rsa:2048 -days 1000  -nodes -keyout jaa-key.pem > jaa-req.pem
mysqladmin@gaspar:~/mysql_certs$ openssl x509 -req -in jaa-req.pem -days 1000 -CA ca-cert.pem -CAkey ca-key.pem -set_serial 01 > jaa-cert.pem
Un segundo cliente.
mysqladmin@gaspar:~/mysql_certs$ openssl req -newkey rsa:2048 -days 1000 -nodes -keyout pepe-key.pem > pepe-req.pem
mysqladmin@gaspar:~/mysql_certs$ openssl x509 -req -in pepe-req.pem -days 1000 -CA ca-cert.pem -CAkey ca-key.pem -set_serial 01 > pepe-cert.pem
Con esto generamos la clave privada y el certificado para dos clientes: jaa y pepe. Si necesitamos generar más simplemente volvemos a ejecutar estos dos pasos (pero obviamente cambiando los nombres de los archivos de salida!).

Paso 2: Ejecutar el servidor, indicandole los parámetros necesarios.

Este paso es sencillo, simplemente invocamos al servidor, pero debemos indicarle los parámetros necesarios para que este use su clave privada y certificado, además del certificado del CA para que pueda luego validar a los demás usuarios.
mysqladmin@gaspar:~/mysql_certs$ mysqld --ssl-ca=ca-cert.pem --ssl-cert=server-cert.pem --ssl-key=server-key.pem
Paso 3: Verificar y corregir bug que nos impedirá la conexión con ssl.

Bueno este bug a mi me ocurre en linux, y SOLO CON EL CLIENTE MYSQL, para la siguiente version:
jaa@dino-thunder:~$ mysql --version
mysql Ver 14.12 Distrib 5.0.38, for pc-linux-gnu (i486) using readline 5.2
el tema es que cuando intentemos conectar utilizando las opciones avanzadas de ssl no podremos conectar por un supuesto error de SSL:
"ERROR 2026 (HY000): SSL connection error"
entonces hacemos:
root@Patrulla-Delta:/home/jaa/mysql-cert/2# echo "192.168.1.1" >> /etc/hosts
Obviamente hay que reemplazar la direccion ip con la del servidor real.
Supongo que obteniendo las fuentes del último mysql client, y compilando el error debería desaparecer, una alternativa es compilar utilizando openssl ya que por defecto en mysql se usa yaSSL, esto por temas de compativilidad entre licencias.
Resalto que en windows no he tenido ningún problema al respecto.

Paso 4: Añadir las restricciones de seguridad por usuario.

MySql ofrece 4 niveles de seguridad configurables, para la mayor seguridad, debe aplicarse el nivel 4.

Antes de continuar informo algunas cosas simples:
  • ip del servidor: 192.168.1.1
  • host cliente: Patrulla-Delta.local
  • Todos los certificados fueron generados en el servidor pero deben copiarse al host cliente en algún lugar (en lo posible al hacer transferencias por red siempre utilizar scp!).
Nivel 1- Este nivel se aplica solo del lado cliente, y consiste que al conectar contra el servidor hagamos:

$ mysql --ssl-ca=ca-cert.pem -u usuario ...
con esto aseguramos que ningún host intermedio robe la identidad del host servidor, es decir aseguramos la identidad del servidor, ya que este está corriendo (según lo que hicimos en 2) utilizando su certificado y clave privada, por lo tanto ahora nuestro cliente validará la firma del certificado servidor con el certificado del CA que indicamos.

Nivel 2 - Este nivel lo que hace es obligar al cliente a que cumpla uno de los dos requerimientos:
1. Presentar el certificado del CA (con este el cliente valida al servidor).
2. Validar con un certificado firmado por el CA (y obviamente utilizando su clave privada).

mysql> create user pepe@'%' identified by 'lalala';
mysql> GRANT ALL PRIVILEGES ON *.* TO 'jaa'@'%' REQUIRE SSL;
mysql> flush privileges;
Entonces para conectar

$ mysql --ssl-ca=ca-cert.pem -u pepe -p
Enter password:
Welcome to the MySQL monitor. Commands end with ; or \g.
Your MySQL connection id is 71
Server version: xxxxx

Type 'help;' or '\h' for help. Type '\c' to clear the buffer.

mysql>
Y uno se pregunta: pero cual es la diferencia con el nivel anterior?
Bueno, la diferencia está en que ahora el servidor nos obliga a presentar el certificado del CA.
jaa@Patrulla-Delta:~/mysql-cert/2$ mysql -u pepe -p -h 192.168.1.1
Enter password:
ERROR 1045 (28000): Access denied for user 'pepe'@'Patrulla-Delta.local' (using password: YES)
jaa@Patrulla-Delta:~/mysql-cert/2$ mysql --ssl-ca=jaa-cert.pem -u pepe -p -h 192.168.1.1
Enter password:
ERROR 2026 (HY000): SSL connection error
jaa@Patrulla-Delta:~/mysql-cert/2$ mysql --ssl-cert=pepe-cert.pem --ssl-key=pepe-key.pem -u pepe -p -h 192.168.1.1
Enter password:
Welcome to the MySQL monitor. Commands end with ; or \g.
Your MySQL connection id is 77
Server version: xxxxx

Type 'help;' or '\h' for help. Type '\c' to clear the buffer.

mysql>
En el primer caso intento ingresar sin presentar el certificado del CA, entonces el servidor nos niega el acceso.
En el segundo caso intento ingresar presentando un certificado de CA erroneo, por lo cual también nos da una patada.
En el Tercer caso, presento un certificado firmado por el CA y logro el acceso.

Nivel 3 - Este se aplica del lado servidor. Lo que haremos será decirle al servidor que solo acepte al usuario "jaa" si presenta un certificado firmado por el CA. Pero como hacemos esto?, bueno no es tan complicado:
- En primer lugar obtenemos lo que se denomina issuer del certificado de uno de los clientes:
jaa@Patrulla-Delta:~/mysql-cert/2/jaa$ openssl x509 -in client-cert.pem -noout -issuer
issuer= /C=AR/ST=Capital Federal/L=Capital Federal/O=OpenOrange/OU=Programacion/CN=OpenOrange Dpto. Cert./emailAddress=info@openorange.com.ar
la linea que dice issuer= /C... corresponde con la información que necesitamos. Esta es la información acerca del CA, la cual para mi caso quedó como mostré.
Es interesante notar que sea cual sea el certificado de cliente o el servidor al que apliquemos el comando siempre obtendremos el mismo contenido (siempre y cuando hallan sido firmados nuestro CA!), por ejemplo si hacemos:
jaa@Patrulla-Delta:~/mysql-cert/2/jaa$ openssl x509 -in pepe-cert.pem -noout -issuer
issuer= /C=AR/ST=Capital Federal/L=Capital Federal/O=OpenOrange/OU=Programacion/CN=OpenOrange Dpto. Cert./emailAddress=info@openorange.com.ar
Entonces nuestro issuer sera:
issuer= /C=AR/ST=Capital Federal/L=Capital Federal/O=OpenOrange/OU=Programacion/CN=OpenOrange Dpto. Cert./emailAddress=info@openorange.com.ar
Ahora lo que hacemos es aplicar la restricción dentro del servidor mysql:

mysql> create user jaa@'%' identified by 'lalala';
mysql> GRANT ALL PRIVILEGES ON *.* TO 'jaa'@'%'
-> REQUIRE ISSUER '/C=AR/ST=Capital Federal/L=Capital Federal/O=OpenOrange/OU=Programacion/CN=OpenOrange Dpto. Cert./emailAddress=info@openorange.com.ar';
mysql> flush privileges;
En estas dos líneas cree el usuario, y seguido a esto le asigné la restricción.
ahora para conectar:

jaa@Patrulla-Delta:~/mysql-cert/2/jaa$ mysql --ssl-ca=ca-cert.pem --ssl-cert=jaa-cert.pem --ssl-key=jaa-key.pem -u jaa -h 192.168.1.1 -p
Enter password:
Welcome to the MySQL monitor. Commands end with ; or \g.
Your MySQL connection id is 71
Server version: xxxxx

Type 'help;' or '\h' for help. Type '\c' to clear the buffer.

mysql>
Y hacemos una prueba más, supongamos que como 'jaa' tenemos en nuestras manos la clave privada y certificado de 'pepe' (digamos que pepe confia en que no lo venderemos por ahí), entonces:
jaa@Patrulla-Delta:~/mysql-cert/2/jaa$ mysql --ssl-ca=ca-cert.pem --ssl-cert=pepe-cert.pem --ssl-key=pepe-key.pem -u jaa -h 192.168.1.1 -p
Enter password:
Welcome to the MySQL monitor. Commands end with ; or \g.
Your MySQL connection id is 71
Server version: xxxxx

Type 'help;' or '\h' for help. Type '\c' to clear the buffer.

mysql>
vemos que podemos utilizar el certificado y clave de pepe sin ningún problema para conectarnos con nuestro usuario. Esto es debido a que todos los usuarios de clientes que generamos comparten el issuer.

Nivel 4- Este es el nivel más seguro; en este le decimos al servidor que un cliente solo puede conectarse si presenta su propio certificado. En este caso utilizamos el "SUBJECT", el cual tiene los datos del usuario (cliente) mismo.
Primero obtenemos el subject del certificado:
jaa@Patrulla-Delta:~/mysql-cert/2$ openssl x509 -in jaa-cert.pem -noout -subject
subject= /C=AR/ST=Buenos Aires/L=Brandsen/O=JAATesting/OU=Programacion/CN=Jonatan Anauati/emailAddress=janauati@openorange.com.ar
Al igual que para el issuer, el subject es lo que viene despues de "subject= ".
Entonces vamos a mysql:
si el usuario no existía:
mysql> create user jaa@'%';
Las siguientes dos son alternativas, si el usuario quedo del ejemplo anterior, en otro
caso pueden ser saltadas.
mysql> revoke all privileges on *.* from test2@'%';
mysql> update mysql.user set password=NULL where User=jaa2@'%';
Query OK, 0 rows affected (0.00 sec)
mysql> GRANT ALL PRIVILEGES ON *.* TO 'jaa'@'%'
-> REQUIRE SUBJECT '/C=AR/ST=Buenos Aires/L=Brandsen/O=JAATesting/OU=Programacion/CN=Jonatan Anauati/emailAddress=janauati@openorange.com.ar';
mysql> flush privileges;
y ahora solo falta conectarnos:
jaa@Patrulla-Delta:~/mysql-cert/2/jaa$ mysql --ssl-ca=ca-cert.pem --ssl-cert=jaa-cert.pem --ssl-key=jaa-key.pem -u jaa -h 192.168.1.1
Enter password:
Welcome to the MySQL monitor. Commands end with ; or \g.
Your MySQL connection id is 71
Server version: xxxxx

Type 'help;' or '\h' for help. Type '\c' to clear the buffer.

mysql>
Para este caso, al usuario le quité el password, ya que unicamente el puede conectarse ya que requiere siempre presentar su certificado y firmar (y desencriptar) con su clave privada ante el servidor.

Si en este útimo caso intentamos conectar al servidor sin presentar nuestras credenciales estamos fritos, y además tampoco podemos usar el certificado y clave del usuario 'pepe'.

Con todo esto el servidor debería quedar funcionando.

Etiquetas: , , , , , , ,

0 comentarios:

Publicar un comentario en la entrada

Suscribirse a Enviar comentarios [Atom]

<< Página principal