--- title: "Cómo configurar TLS mutuo (mTLS) con Nginx en Ubuntu 24.04" description: "Crea tu propia CA con OpenSSL, emite certificados de servidor y de cliente y configura Nginx en Ubuntu 24.04 para exigir mTLS, con pruebas en curl y revocación." category: "Proxy y Equilibrio de Carga" language: es source: https://cubepath.com/es/docs/proxy-y-equilibrio-de-carga/configuracion-de-tls-mutuo-mtls updated: 2026-09-25 --- # Cómo configurar TLS mutuo (mTLS) con Nginx en Ubuntu 24.04 En TLS normal solo el servidor demuestra su identidad con un certificado. Con TLS mutuo (mTLS) el cliente también presenta un certificado, y el servidor rechaza la conexión si no está firmado por una autoridad de certificación (CA) en la que confía. Es una forma robusta de proteger APIs internas, paneles de administración o la comunicación entre servicios sin depender de contraseñas ni tokens. En esta guía crearás una CA privada con OpenSSL, emitirás un certificado de servidor y otro de cliente, configurarás Nginx en Ubuntu 24.04 para exigirlos y aprenderás a bloquear un certificado comprometido. ## Requisitos previos - Un servidor con Ubuntu 24.04 LTS con Nginx instalado (`sudo apt install nginx`), por ejemplo un VPS de CubePath. - Un usuario no root con privilegios `sudo`. - Un nombre DNS para el servicio, en los ejemplos `api.your_domain`, que resuelva a la IP del servidor. Para pruebas puedes añadirlo al `/etc/hosts` de tu equipo. - El puerto 443/TCP abierto: `sudo ufw allow 443/tcp`. - Una aplicación escuchando en `127.0.0.1:8080` a la que Nginx hará de proxy. Si aún no la tienes, puedes probar con `python3 -m http.server 8080 --bind 127.0.0.1`. ## Paso 1: Crear la autoridad de certificación La CA es el par de clave y certificado con el que firmarás todos los demás certificados. Quien tenga su clave privada puede emitir certificados de cliente válidos, así que guárdala en un directorio al que solo accede root y, en producción, en una máquina distinta del servidor. ```bash sudo -i mkdir -p /root/mtls-ca chmod 700 /root/mtls-ca cd /root/mtls-ca ``` `sudo -i` abre una shell de root. A partir de aquí, hasta el paso 4, los comandos se ejecutan como root dentro de `/root/mtls-ca`. Genera la clave de la CA (ECDSA P-256) y un certificado autofirmado válido 10 años: ```bash openssl ecparam -name prime256v1 -genkey -noout -out ca.key chmod 600 ca.key openssl req -x509 -new -key ca.key -sha256 -days 3650 -subj "/O=Your Company/CN=Your Company Internal CA" -out ca.crt ``` Con `-x509`, OpenSSL aplica la sección `v3_ca` de `/etc/ssl/openssl.cnf`, que marca el certificado como CA. Compruébalo: ```bash openssl x509 -in ca.crt -noout -subject -ext basicConstraints ``` ```output subject=O = Your Company, CN = Your Company Internal CA X509v3 Basic Constraints: critical CA:TRUE ``` ## Paso 2: Emitir el certificado del servidor Los clientes modernos ignoran el campo CN y validan el nombre con la extensión Subject Alternative Name (SAN), así que el certificado del servidor debe incluirla. Crea un archivo de extensiones: ```bash nano server.ext ``` ```ini basicConstraints = critical, CA:FALSE keyUsage = critical, digitalSignature extendedKeyUsage = serverAuth subjectAltName = DNS:api.your_domain ``` Genera la clave, la solicitud de firma (CSR) y firma el certificado con la CA: ```bash openssl ecparam -name prime256v1 -genkey -noout -out server.key openssl req -new -key server.key -subj "/CN=api.your_domain" -out server.csr openssl x509 -req -in server.csr -CA ca.crt -CAkey ca.key -CAcreateserial -days 397 -sha256 -extfile server.ext -out server.crt ``` `-CAcreateserial` crea el archivo `ca.srl`, donde OpenSSL lleva la cuenta de números de serie. Verifica que el certificado encadena con la CA: ```bash openssl verify -CAfile ca.crt server.crt ``` ```output server.crt: OK ``` > Nota: si el servicio es público, puedes usar un certificado de Let's Encrypt para el servidor y reservar la CA privada solo para los clientes. Nginx valida cada lado por separado. ## Paso 3: Emitir un certificado de cliente Cada cliente (una persona, un servicio, un equipo) recibe su propio certificado. El CN identifica al cliente y la extensión `clientAuth` limita su uso a autenticación de cliente: ```bash nano client.ext ``` ```ini basicConstraints = critical, CA:FALSE keyUsage = critical, digitalSignature extendedKeyUsage = clientAuth ``` Emite el certificado para un cliente llamado `billing-service`, válido un año: ```bash openssl ecparam -name prime256v1 -genkey -noout -out billing-service.key openssl req -new -key billing-service.key -subj "/O=Your Company/CN=billing-service" -out billing-service.csr openssl x509 -req -in billing-service.csr -CA ca.crt -CAkey ca.key -CAserial ca.srl -days 365 -sha256 -extfile client.ext -out billing-service.crt openssl verify -CAfile ca.crt billing-service.crt ``` ```output billing-service.crt: OK ``` Los navegadores y algunas herramientas importan el certificado de cliente en formato PKCS#12, un único archivo con certificado y clave protegido por contraseña: ```bash openssl pkcs12 -export -in billing-service.crt -inkey billing-service.key -certfile ca.crt -name billing-service -out billing-service.p12 ``` OpenSSL te pedirá una contraseña de exportación: usa una robusta y entrégala al cliente por un canal distinto al del archivo. ## Paso 4: Instalar los certificados en Nginx Nginx necesita el certificado y la clave del servidor, y **solo el certificado** de la CA para verificar a los clientes. La clave `ca.key` nunca debe copiarse a `/etc/nginx`: ```bash install -d -m 750 -o root -g www-data /etc/nginx/mtls install -m 644 ca.crt server.crt /etc/nginx/mtls/ install -m 640 -g www-data server.key /etc/nginx/mtls/ exit ``` El `exit` final cierra la shell de root que abriste en el paso 1. ## Paso 5: Configurar mTLS en Nginx Crea el sitio: ```bash sudo nano /etc/nginx/sites-available/api.your_domain ``` ```nginx server { listen 443 ssl; listen [::]:443 ssl; server_name api.your_domain; ssl_certificate /etc/nginx/mtls/server.crt; ssl_certificate_key /etc/nginx/mtls/server.key; ssl_protocols TLSv1.2 TLSv1.3; ssl_client_certificate /etc/nginx/mtls/ca.crt; ssl_verify_client on; ssl_verify_depth 1; location / { proxy_pass http://127.0.0.1:8080; proxy_set_header Host $host; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Client-DN $ssl_client_s_dn; proxy_set_header X-Client-Serial $ssl_client_serial; proxy_set_header X-Client-Verify $ssl_client_verify; } } ``` - `ssl_client_certificate`: la CA con la que se validan los certificados de cliente. Nginx solo acepta certificados firmados por ella. - `ssl_verify_client on`: exige certificado. Sin él, Nginx responde `400 Bad Request`. - `ssl_verify_depth 1`: tus certificados de cliente están firmados directamente por la CA, sin intermedias. - Las cabeceras `X-Client-*` pasan a la aplicación la identidad del cliente para que pueda autorizar por CN. Asegúrate de que la aplicación solo es accesible desde Nginx, o cualquiera podría enviar esas cabeceras falsificadas. Activa el sitio, comprueba la configuración y recarga: ```bash sudo ln -s /etc/nginx/sites-available/api.your_domain /etc/nginx/sites-enabled/ sudo nginx -t sudo systemctl reload nginx ``` ```output nginx: the configuration file /etc/nginx/nginx.conf syntax is ok nginx: configuration file /etc/nginx/nginx.conf test is successful ``` ## Paso 6: Probar la conexión Copia a tu equipo `ca.crt`, `billing-service.crt` y `billing-service.key` (por ejemplo con `scp`). Primero intenta conectar sin certificado de cliente: ```bash curl --cacert ca.crt https://api.your_domain/ ``` ```output