# Mysql 8, SSL con Let’s Encrypt ## Lo mejor es trabajar en un entorno seguro. La aparición de nuevas versiones de muchos softwares, que fuerzan al uso de una seguridad mayor, sobre todo en las comunicaciones entre nodos, habida cuenta del auge de los microservicios, y la amplitud de posibilidades para este tema, nos lleva a un camino ya conocido: la desactivación de la seguridad en cuanto surge algo que nos molesta, en lugar de aprender el camino del cómo hacerlo con el método seguro. En el anterior artículo [QGIS, Navicat, y otros problemas de conexión con MySQL 8](​​https://wiki.castris.com/books/tips-para-administradores-de-sitios-web/page/pagina-nueva) hablamos de una salida rápida, pero la realidad es que es mejor dedicar un tiempo y hacer la correcta. Aquí te explico cómo securizar con SSL de Let’s Encrypt un servidor MySQL 8.X, aunque estoy seguro de que te vale con algún matiz, para MariaDb o Percona. ### Instalando Let’s Encrypt en MySQL No es el alcance explicar cómo se obtiene un certificado Let’s Encrypt en un servidor pero te dejo algunos tips, al final del documento. Entendemos para este artículo que tenemos un servidor o un sistema de certificados para el hostname basado en Let’s Encrypt, ya está ya instalado y que se renueva regularmente. #### Ubicación de los certificados Let’s Encrypt Esto es sólo un ejemplo, para entenderlo. En realidad debes tener claro dónde está en tu servidor. ``` # ls -lisa /etc/letsencrypt/live// total 12 428663 4 drwxr-xr-x 2 root root 4096 sep 15 14:32 . 428659 4 drwx------ 3 root root 4096 mar 3 2022 .. 399891 0 lrwxrwxrwx 1 root root 54 sep 15 14:32 cert.pem -> ../../archive/mydomain.tld/cert5.pem 399898 0 lrwxrwxrwx 1 root root 55 sep 15 14:32 chain.pem -> ../../archive/mydomain.tld/chain5.pem 399930 0 lrwxrwxrwx 1 root root 59 sep 15 14:32 fullchain.pem -> ../../archive/mydomain.tld/fullchain5.pem 399892 0 lrwxrwxrwx 1 root root 57 sep 15 14:32 privkey.pem -> ../../archive/mydomain.tld/privkey5.pem ``` El problema es que estando en esa ubicación, mediante enlaces a los ficheros reales, correspondientes a cada una de las versiones (renovaciones) no conseguí que funcionaran. Así que vamos a solucionarlo #### Despliegue de los certificados Let’s Encrypt para MySQL ##### Configuración mysql Configurar el fichero `/etc/my.cnf`o el usado por su distribución. En mi caso para una Ubuntu 20.04 `/etc/mysql/mysql.conf.d/mysqld.cnf` ``` [mysqld] ssl_ca=/var/lib/mysql/chain.pem ssl_cert=/var/lib/mysql/cert.pem ssl_key=/var/lib/mysql/privkey.pem ``` ##### Crear los ficheros de los certificados **Notas y descargo de responsabilidad** - El dominio se refiere al hostname de la máquina - Las otras variables debe ser revisadas adaptándose a el servidor que estamos configurando - No es copiar y pegar, sino una guia que debemos comprender.ç - No soy responsable de nada de lo que te pase. - Por su puesto debes tener backup ``` # domain=mydomain.tld # cert_dir=/var/lib/mysql # user=mysql.mysql # cp /etc/letsencrypt/live/$domain/cert.pem $cert_dir # cp /etc/letsencrypt/live/$domain/privkey.pem $cert_dir # openssl x509 -in /etc/letsencrypt/live/$domain/chain.pem > $cert_dir/chain.pem # chown $user $cert_dir/*.pem # chmod 600 $cert_dir/*.pem # mysql --login-path=root@localhost --execute="ALTER INSTANCE RELOAD TLS" ``` Si todo ha ido bien, no saldrá ningún mensaje de error, y MySQL 8.0 ya está preparado para usar SSL en su trabajo. > Entendemos que tenemos y usamos un fichero ~/.my.cnf para acceder sin password como root a mysql ##### Comprobación MySQL ``` mysql > SHOW VARIABLES LIKE '%ssl%'; +-------------------------------------+----------------------------+ | Variable_name | Value | +-------------------------------------+----------------------------+ | admin_ssl_ca | | | admin_ssl_capath | | | admin_ssl_cert | | | admin_ssl_cipher | | | admin_ssl_crl | | | admin_ssl_crlpath | | | admin_ssl_key | | | have_openssl | YES | | have_ssl | YES | | mysqlx_ssl_ca | | | mysqlx_ssl_capath | | | mysqlx_ssl_cert | | | mysqlx_ssl_cipher | | | mysqlx_ssl_crl | | | mysqlx_ssl_crlpath | | | mysqlx_ssl_key | | | performance_schema_show_processlist | OFF | | ssl_ca | /var/lib/mysql/chain.pem | | ssl_capath | | | ssl_cert | /var/lib/mysql/cert.pem | | ssl_cipher | | | ssl_crl | | | ssl_crlpath | | | ssl_fips_mode | OFF | | ssl_key | /var/lib/mysql/privkey.pem | | ssl_session_cache_mode | ON | | ssl_session_cache_timeout | 300 | +-------------------------------------+----------------------------+ 27 rows in set (0,00 sec) ``` ##### Modificación del script de renovación de Let’s Encrypt Obtenemos el path de de los hooks de Let’s Encrypt. ``` ## Versión de certbot recomendada con snap, pero puede variar si usamos package de la distro o binario. Por eso buscamos # systemctl | grep cer snap-certbot-2344.mount loaded active mounted Mount unit for certbot, revision 2344 snap-certbot-2414.mount loaded active mounted Mount unit for certbot, revision 2414 snap.certbot.renew.timer loaded active waiting Timer renew for snap application certbot.renew # systemctl status snap.certbot.renew.timer ● snap.certbot.renew.timer - Timer renew for snap application certbot.renew Loaded: loaded (/etc/systemd/system/snap.certbot.renew.timer; enabled; vendor preset: enabled) Active: active (waiting) since Tue 2022-10-04 20:35:18 UTC; 1 weeks 2 days ago Trigger: Fri 2022-10-14 11:31:00 UTC; 1h 53min left Triggers: ● snap.certbot.renew.service Warning: journal has been rotated since unit was started, output may be incomplete. ``` Y después creamos el fichero `/etc/letsencrypt/renewal-hooks/deploy/mysqld-deploy.sh` que se encargará de ajustar tras la renovación los ficheros para MySQL. ``` #!/bin/sh domain=mydomain.tld cert_dir=/var/lib/mysql user=mysql.mysql cp /etc/letsencrypt/live/$domain/cert.pem $cert_dir cp /etc/letsencrypt/live/$domain/privkey.pem $cert_dir # Only keep 1st certificate (C=US/O=Let's Encrypt/CN=R3), that is, get rid # of 2nd certificate "ISRG Root X1" issued by "DST Root CA X3" which is expired. # https://letsencrypt.org/2020/12/21/extending-android-compatibility.html openssl x509 -in /etc/letsencrypt/live/$domain/chain.pem > $cert_dir/chain.pem chown $user $cert_dir/*.pem chmod 600 $cert_dir/*.pem mysql --login-path=root@localhost --execute="ALTER INSTANCE RELOAD TLS" ``` Y le damos permisos de ejecución ``` chmod 755 /etc/letsencrypt/renewal-hooks/deploy/mysqld-deploy.sh ``` ##### Comprobación ``` openssl s_client -starttls mysql -showcerts -connect mydomain.tld:3306 CONNECTED(00000003) depth=2 C = US, O = Internet Security Research Group, CN = ISRG Root X1 verify return:1 depth=1 C = US, O = Let's Encrypt, CN = R3 verify return:1 depth=0 CN = api.mydomain.tld verify return:1 --- Certificate chain 0 s:CN = api.mydomain.tld i:C = US, O = Let's Encrypt, CN = R3 -----BEGIN CERTIFICATE----- MIIFZjCCBE6gAwIBAgISBJPIsuc3JOh84ALWyVAXpFjxMA0GCSqGSIb3DQEBCwUA … … BpPXg5qzChYB5e2/wRhXRZb3IejNUDg8tQzU3hL6sJPcNtwnYAFyxDcg -----END CERTIFICATE----- 1 s:C = US, O = Let's Encrypt, CN = R3 i:C = US, O = Internet Security Research Group, CN = ISRG Root X1 -----BEGIN CERTIFICATE----- MIIFFjCCAv6gAwIBAgIRAJErCErPDBinU/bWLiWnX1owDQYJKoZIhvcNAQELBQAw … … MldlTTKB3zhThV1+XWYp6rjd5JW1zbVWEkLNxE7GJThEUG3szgBVGP7pSWTUTsqX nLRbwHOoq7hHwg== -----END CERTIFICATE----- --- Server certificate subject=CN = api.mydomain.tld issuer=C = US, O = Let's Encrypt, CN = R3 --- No client certificate CA names sent Requested Signature Algorithms: ECDSA+SHA256:ECDSA+SHA384:ECDSA+SHA512:Ed25519:Ed448:RSA-PSS+SHA256:RSA-PSS+SHA384:RSA-PSS+SHA512:RSA-PSS+SHA256:RSA-PSS+SHA384:RSA-PSS+SHA512:RSA+SHA256:RSA+SHA384:RSA+SHA512:ECDSA+SHA224:RSA+SHA224 Shared Requested Signature Algorithms: ECDSA+SHA256:ECDSA+SHA384:ECDSA+SHA512:Ed25519:Ed448:RSA-PSS+SHA256:RSA-PSS+SHA384:RSA-PSS+SHA512:RSA-PSS+SHA256:RSA-PSS+SHA384:RSA-PSS+SHA512:RSA+SHA256:RSA+SHA384:RSA+SHA512 Peer signing digest: SHA256 Peer signature type: RSA-PSS Server Temp Key: X25519, 253 bits --- SSL handshake has read 3415 bytes and written 468 bytes Verification: OK --- New, TLSv1.3, Cipher is TLS_AES_256_GCM_SHA384 Server public key is 2048 bit Secure Renegotiation IS NOT supported Compression: NONE Expansion: NONE No ALPN negotiated Early data was not sent Verify return code: 0 (ok) --- --- Post-Handshake New Session Ticket arrived: SSL-Session: Protocol : TLSv1.3 Cipher : TLS_AES_256_GCM_SHA384 Session-ID: AD8278545269458B04E87DF04C50140678DEE6C2E2A5BC9017329D1D170A80B1 Session-ID-ctx: Resumption PSK: DFB2E7716E7B0579E911AB32999D976CBA419B43C0F2A69FAB68D7C13DF7E52FF58A43E3709F62B7E80E6269707AD002 PSK identity: None PSK identity hint: None SRP username: None TLS session ticket lifetime hint: 300 (seconds) TLS session ticket: 0000 - 9b 98 52 5a 9f bc be 7c-b9 37 3a 3a e7 bf 49 7c ..RZ...|.7::..I| 0010 - c9 de 90 6b 1d 08 c8 9e-f7 dc c4 04 c6 e4 48 41 ...k..........HA … … 00b0 - 09 68 bb 27 18 d3 3e f6-2e e9 f4 6e ee 3f 49 26 .h.'..>....n.?I& 00c0 - 7c 55 be 6b 46 1b 3c b3-0d 72 d4 93 da 7e 6f c2 |U.kF.<..r...~o. Start Time: 1665740692 Timeout : 7200 (sec) Verify return code: 0 (ok) Extended master secret: no Max Early Data: 0 --- read R BLOCK --- Post-Handshake New Session Ticket arrived: SSL-Session: Protocol : TLSv1.3 Cipher : TLS_AES_256_GCM_SHA384 Session-ID: 2C2F40E7309289A3368A250BC710F04021E66B9D821DFA74694AACFE05E9E742 Session-ID-ctx: Resumption PSK: DE4C98072B6A8DC2A3636ADFBDF3612090C3C1A5BCB1CD267C1B688015F12FAE5946F2541FB0A428BEDE5483E1DA7A46 PSK identity: None PSK identity hint: None SRP username: None TLS session ticket lifetime hint: 300 (seconds) TLS session ticket: 0000 - 9b 98 52 5a 9f bc be 7c-b9 37 3a 3a e7 bf 49 7c ..RZ...|.7::..I| 0010 - 40 00 eb f6 2b 3e 84 fc-4a 7d 6e 00 e7 96 af ce @...+>..J}n..... … … 00b0 - 14 4a 48 3e 33 4b 19 b4-df 14 24 bb 28 bc 55 29 .JH>3K....$.(.U) 00c0 - 73 23 37 e2 3c e7 0b ea-ed 25 5e 3d 28 cc b5 0d s#7.<....%^=(... Start Time: 1665740692 Timeout : 7200 (sec) Verify return code: 0 (ok) Extended master secret: no Max Early Data: 0 --- read R BLOCK ``` ##### Enlaces externos - [Mysql :: Using Encrypted connections](https://dev.mysql.com/doc/refman/8.0/en/using-encrypted-connections.html) - [Myswl 8 :: Alter User](https://dev.mysql.com/doc/refman/8.0/en/alter-user.html) - [Certificado Let’s Encrypt para servidor sin servidor web (ElasticSearch)](https://wiki.castris.com/books/tips-para-administradores-de-sitios-web/page/certificado-lets-encrypt-para-servidor-sin-servidor-web-elasticsearch) - [MySQL And Lets Encrypt](https://krisrice.io/2022-01-28-mysql-lets-encrypt/) - [Cron job for let's encrypt renewal](https://serverfault.com/questions/790772/cron-job-for-lets-encrypt-renewal) ##### Aviso Esta documentación y su contenido, no implica que funcione en tu caso o determinados casos. También implica que tienes conocimientos sobre lo que trata, y que en cualquier caso tienes copias de seguridad. El contenido el contenido se entrega, tal y como está, sin que ello implique ningún obligación ni responsabilidad por parte de [Castris](https://castris.com) Si necesitas soporte profesional puedes contratar con Castris [soporte profesional](https://intranet.castris.com/cart.php?gid=18).