Configuración de TLS Mutuo (mTLS)
TLS Mutuo (mTLS) extiende TLS estándar requiriendo que tanto cliente como servidor se autentiquen entre sí usando certificados. A diferencia de HTTPS tradicional donde solo el servidor prueba su identidad, mTLS proporciona autenticación bidireccional, asegurando que solo clientes confiados accedan a servicios protegidos. Esta guía cubre configuración de CA, creación de certificado de cliente, configuración a través de Nginx/HAProxy/Apache, mecanismos de revocación, rotación de certificados y estrategias de solución de problemas.
Tabla de Contenidos
- Descripción General de mTLS
- Configuración de Autoridad de Certificación
- Creación de Certificado de Servidor
- Creación de Certificado de Cliente
- Configuración de mTLS de Nginx
- Configuración de mTLS de HAProxy
- Configuración de mTLS de Apache
- Revocación de Certificado
- Rotación de Certificado
- Prueba de mTLS
- Solución de Problemas
Descripción General de mTLS
Flujo de autenticación de mTLS:
- El cliente conecta y envía su certificado
- El servidor valida certificado de cliente contra CA
- El servidor presenta su certificado
- El cliente valida certificado de servidor
- Ambas partes verifican cadena de certificados
Beneficios:
- Previene acceso de cliente no autorizado
- Detecta credenciales de cliente comprometidas
- Elimina necesidad de autenticación de contraseña
- Apropiado para autenticación servicio-a-servicio
- Sin secretos compartidos para robar
Casos de uso:
- Comunicación de API interna
- Service mesh (Istio, Linkerd)
- Autenticación de microservicios
- Acceso de administración/panel
- Autenticación de dispositivo IoT
Configuración de Autoridad de Certificación
Cree una CA privada para certificados internos:
# Create CA directory
mkdir -p ~/ca/{private,certs,csr}
cd ~/ca
# Generate CA private key (4096-bit RSA)
openssl genrsa -out private/ca.key 4096
# Generate CA certificate (valid 10 years)
openssl req -new -x509 -days 3650 -key private/ca.key -out certs/ca.crt \
-subj "/C=US/ST=State/L=City/O=Organization/CN=Internal-CA"
Verifique certificado de CA:
openssl x509 -in certs/ca.crt -text -noout
Cree configuración de CA para firma automatizada:
cat > ~/ca/ca.conf <<'EOF'
[ ca ]
default_ca = CA_default
[ CA_default ]
dir = /root/ca
certs = $dir/certs
crl_dir = $dir/crl
new_certs_dir = $dir/certs
database = $dir/index.txt
serial = $dir/serial
RANDFILE = $dir/private/.rand
default_crl_days = 30
default_crl_extensions = crl_ext
default_days = 365
default_md = sha256
name_opt = ca_default
cert_opt = ca_default
preserve = no
policy = policy_strict
unique_subject = no
[ policy_strict ]
countryName = match
stateOrProvinceName = match
organizationName = match
organizationalUnitName = optional
commonName = supplied
emailAddress = optional
[ policy_loose ]
countryName = optional
stateOrProvinceName = optional
localityName = optional
organizationName = optional
organizationalUnitName = optional
commonName = supplied
emailAddress = optional
[ req ]
default_bits = 4096
distinguished_name = req_distinguished_name
string_mask = utf8only
default_md = sha256
x509_extensions = v3_ca
[ req_distinguished_name ]
countryName = Country Name
stateOrProvinceName = State or Province Name
localityName = Locality Name
organizationName = Organization Name
organizationalUnitName = Organizational Unit Name
commonName = Common Name
emailAddress = Email Address
[ v3_ca ]
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid:always,issuer
basicConstraints = critical, CA:true
keyUsage = critical, digitalSignature, cRLSign, keyCertSign
EOF
Inicialice base de datos de CA:
cd ~/ca
touch index.txt
echo 1000 > serial
Creación de Certificado de Servidor
Cree certificado de servidor firmado por CA:
# Generate server private key
openssl genrsa -out ~/ca/private/server.key 4096
# Create server CSR (Certificate Signing Request)
openssl req -new -key ~/ca/private/server.key -out ~/ca/csr/server.csr \
-subj "/C=US/ST=State/L=City/O=Organization/CN=api.example.com"
# Sign server certificate with CA
openssl ca -config ~/ca/ca.conf -extensions v3_ca -days 365 \
-notext -md sha256 -in ~/ca/csr/server.csr \
-out ~/ca/certs/server.crt
# Create combined certificate and key
cat ~/ca/certs/server.crt ~/ca/private/server.key > ~/ca/certs/server.pem
Agregue extensiones de certificado para mTLS:
# Create server config with SANs
cat > ~/ca/server.conf <<'EOF'
[ req ]
default_bits = 4096
distinguished_name = req_distinguished_name
req_extensions = v3_req
[ req_distinguished_name ]
countryName = Country Name
stateOrProvinceName = State or Province Name
commonName = Common Name
[ v3_req ]
subjectAltName = @alt_names
[ alt_names ]
DNS.1 = api.example.com
DNS.2 = *.api.example.com
DNS.3 = api.internal
IP.1 = 192.168.1.100
IP.2 = 10.0.0.1
EOF
# Generate CSR with extensions
openssl req -new -key ~/ca/private/server.key -out ~/ca/csr/server.csr \
-config ~/ca/server.conf \
-subj "/C=US/ST=State/L=City/O=Organization/CN=api.example.com"
Creación de Certificado de Cliente
Cree certificados de cliente para autenticación:
# Generate client private key
openssl genrsa -out ~/ca/private/client1.key 4096
# Create client CSR
openssl req -new -key ~/ca/private/client1.key -out ~/ca/csr/client1.csr \
-subj "/C=US/ST=State/L=City/O=Organization/CN=app-service-1"
# Sign client certificate
openssl ca -config ~/ca/ca.conf -days 365 -notext -md sha256 \
-in ~/ca/csr/client1.csr -out ~/ca/certs/client1.crt
# Create PKCS12 format (for some applications)
openssl pkcs12 -export -in ~/ca/certs/client1.crt \
-inkey ~/ca/private/client1.key -out ~/ca/certs/client1.p12 \
-name "App Service 1" -passout pass:password
Cree múltiples certificados de cliente:
# Batch create certificates
for client in app1 app2 app3 app4; do
openssl genrsa -out ~/ca/private/$client.key 4096
openssl req -new -key ~/ca/private/$client.key -out ~/ca/csr/$client.csr \
-subj "/C=US/ST=State/L=City/O=Organization/CN=$client"
openssl ca -config ~/ca/ca.conf -days 365 -notext -md sha256 \
-in ~/ca/csr/$client.csr -out ~/ca/certs/$client.crt
done
Configuración de mTLS de Nginx
Configure Nginx para requerir y validar certificados de cliente:
upstream backend {
server 192.168.1.100:8000;
server 192.168.1.101:8000;
keepalive 32;
}
server {
listen 443 ssl http2;
server_name api.example.com;
# Server certificate and key
ssl_certificate /etc/nginx/ssl/server.crt;
ssl_certificate_key /etc/nginx/ssl/server.key;
# Client certificate validation
ssl_client_certificate /etc/nginx/ssl/ca.crt;
ssl_verify_client on;
ssl_verify_depth 2;
ssl_client_session_cache shared:SSL:10m;
# TLS Configuration
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;
# Pass client certificate to backend
proxy_set_header SSL-Client-Cert $ssl_client_cert;
proxy_set_header SSL-Client-Verify $ssl_client_verify;
proxy_set_header SSL-Client-Subject $ssl_client_s_dn;
location / {
# Optional: Allow access only to valid certs
if ($ssl_client_verify != SUCCESS) {
return 403;
}
proxy_pass http://backend;
proxy_http_version 1.1;
proxy_set_header Connection "";
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
# Optional client certificate section
server {
listen 8443 ssl;
server_name api.example.com;
# Require valid client cert for admin
ssl_client_certificate /etc/nginx/ssl/ca.crt;
ssl_verify_client optional;
location /admin {
if ($ssl_client_verify != SUCCESS) {
return 401;
}
# Admin only content
}
}
Configuración de mTLS de HAProxy
Configure HAProxy para mTLS:
global
log stdout local0
tune.ssl.default-dh-param 2048
frontend https_in
bind *:443 ssl crt /etc/haproxy/ssl/server.pem \
ca-file /etc/haproxy/ssl/ca.crt \
verify optional
mode http
option httpclose
option forwardfor
# Pass client certificate info
http-request set-header X-SSL-Client-Cert %{+Q}[ssl_c_der,hex]
http-request set-header X-SSL-Client-Verify %[ssl_c_verify]
http-request set-header X-SSL-Client-Subject %{+Q}[ssl_c_s_dn]
http-request set-header X-SSL-Client-CN %{+Q}[ssl_c_s_dn(cn)]
# Deny requests without valid client cert
http-request deny if !{ ssl_c_verify eq 0 }
default_backend backend_servers
backend backend_servers
balance roundrobin
mode http
server srv1 192.168.1.100:8000 check
server srv2 192.168.1.101:8000 check
HAProxy avanzado con validación de certificado:
frontend https_in
bind *:443 ssl crt /etc/haproxy/ssl/server.pem \
ca-file /etc/haproxy/ssl/ca.crt \
verify required
http-request set-header X-Client-CN %{+Q}[ssl_c_s_dn(cn)]
acl allowed_clients src -f /etc/haproxy/allowed_clients.txt
acl client_cert_valid ssl_c_verify eq 0
acl cert_cn_admin ssl_c_s_dn(cn) -i admin
acl cert_cn_readonly ssl_c_s_dn(cn) -i readonly
use_backend backend_admin if client_cert_valid cert_cn_admin
use_backend backend_readonly if client_cert_valid cert_cn_readonly
default_backend backend_servers
backend backend_admin
balance roundrobin
server srv1 192.168.1.100:8001 check
backend backend_readonly
balance roundrobin
server srv2 192.168.1.101:8001 check
backend backend_servers
balance roundrobin
server srv1 192.168.1.100:8000 check
Configuración de mTLS de Apache
Configure Apache para mTLS:
<VirtualHost *:443>
ServerName api.example.com
# Server certificate
SSLCertificateFile /etc/apache2/ssl/server.crt
SSLCertificateKeyFile /etc/apache2/ssl/server.key
SSLCertificateChainFile /etc/apache2/ssl/ca.crt
# Client certificate validation
SSLVerifyClient require
SSLVerifyDepth 2
SSLCACertificateFile /etc/apache2/ssl/ca.crt
# TLS Configuration
SSLProtocol TLSv1.2 TLSv1.3
SSLCipherSuite HIGH:!aNULL:!MD5
SSLHonorCipherOrder on
# Pass client info to backend
SSLOptions +StdEnvVars
RequestHeader set X-SSL-Client-Cert %{SSL_CLIENT_CERT}e
RequestHeader set X-SSL-Client-Verify %{SSL_CLIENT_VERIFY}e
RequestHeader set X-SSL-Client-Subject %{SSL_CLIENT_S_DN}e
RequestHeader set X-SSL-Client-CN %{SSL_CLIENT_S_DN_CN}e
<Location />
ProxyPass http://192.168.1.100:8000/
ProxyPassReverse http://192.168.1.100:8000/
</Location>
</VirtualHost>
# Optional: Different policies for different paths
<VirtualHost *:443>
ServerName api.example.com
# Admin requires certificate
<Location /admin>
SSLVerifyClient require
</Location>
# Public API optional certificate
<Location /public>
SSLVerifyClient optional
</Location>
</VirtualHost>
Revocación de Certificado
Implemente CRL (Certificate Revocation List) u OCSP:
Configuración de CRL
# Create CRL file
openssl ca -config ~/ca/ca.conf -gencrl -out ~/ca/crl/ca.crl
# Revoke a client certificate
openssl ca -config ~/ca/ca.conf -revoke ~/ca/certs/client1.crt
# Update CRL
openssl ca -config ~/ca/ca.conf -gencrl -out ~/ca/crl/ca.crl
# Verify revocation
openssl crl -in ~/ca/crl/ca.crl -text -noout
Configure Nginx con CRL:
ssl_crl /etc/nginx/ssl/ca.crl;
ssl_verify_client on;
ssl_verify_depth 2;
OCSP Stapling
Habilite OCSP stapling para certificados de cliente:
ssl_stapling on;
ssl_stapling_verify on;
ssl_stapling_responder "http://ocsp.example.com";
Rotación de Certificado
Implemente rotación de certificado automatizada:
#!/bin/bash
# Certificate rotation script
CERT_DIR="/etc/nginx/ssl"
CA_DIR="${HOME}/ca"
# Rotate certificates
for app in client1 client2 client3; do
# Check if expiration is within 30 days
EXPIRATION=$(openssl x509 -in ${CA_DIR}/certs/${app}.crt -noout -enddate | cut -d= -f2)
DAYS_LEFT=$(( ($(date -d "$EXPIRATION" +%s) - $(date +%s)) / 86400 ))
if [ "$DAYS_LEFT" -lt 30 ]; then
echo "Rotating certificate for $app (expires in $DAYS_LEFT days)"
# Generate new key and CSR
openssl genrsa -out ${CA_DIR}/private/${app}-new.key 4096
openssl req -new -key ${CA_DIR}/private/${app}-new.key \
-out ${CA_DIR}/csr/${app}-new.csr \
-subj "/C=US/ST=State/L=City/O=Organization/CN=$app"
# Sign new certificate
openssl ca -config ${CA_DIR}/ca.conf -days 365 -notext -md sha256 \
-in ${CA_DIR}/csr/${app}-new.csr \
-out ${CA_DIR}/certs/${app}-new.crt
# Backup old cert and key
mv ${CERT_DIR}/${app}.key ${CERT_DIR}/${app}.key.bak
mv ${CERT_DIR}/${app}.crt ${CERT_DIR}/${app}.crt.bak
# Install new cert and key
cp ${CA_DIR}/private/${app}-new.key ${CERT_DIR}/${app}.key
cp ${CA_DIR}/certs/${app}-new.crt ${CERT_DIR}/${app}.crt
# Reload web server
systemctl reload nginx
fi
done
Programe rotación:
# Add to crontab
0 2 * * 0 /usr/local/bin/rotate-certs.sh
Prueba de mTLS
Pruebe configuración mTLS con curl:
# Test without client certificate (should fail)
curl -v https://api.example.com/ 2>&1 | grep -i certificate
# Test with client certificate
curl -v --cert ~/ca/certs/client1.crt \
--key ~/ca/private/client1.key \
--cacert ~/ca/certs/ca.crt \
https://api.example.com/
# Verify certificate chain
openssl s_client -connect api.example.com:443 \
-cert ~/ca/certs/client1.crt \
-key ~/ca/private/client1.key \
-CAfile ~/ca/certs/ca.crt
Pruebe con diferentes clientes:
# Test with client1
curl --cert ~/ca/certs/client1.crt --key ~/ca/private/client1.key \
--cacert ~/ca/certs/ca.crt https://api.example.com/api/protected
# Test with client2
curl --cert ~/ca/certs/client2.crt --key ~/ca/private/client2.key \
--cacert ~/ca/certs/ca.crt https://api.example.com/api/protected
Pruebas con Python:
import requests
from requests.auth import HTTPClientCertAuth
response = requests.get(
'https://api.example.com/api/protected',
cert=('/root/ca/certs/client1.crt', '/root/ca/private/client1.key'),
verify='/root/ca/certs/ca.crt'
)
print(response.status_code)
Solución de Problemas
Verifique detalles del certificado:
# Check server certificate
openssl x509 -in /etc/nginx/ssl/server.crt -text -noout
# Check client certificate
openssl x509 -in ~/ca/certs/client1.crt -text -noout
# Verify certificate chain
openssl verify -CAfile ~/ca/certs/ca.crt ~/ca/certs/client1.crt
Pruebe validez del certificado:
# Check expiration
openssl x509 -in /etc/nginx/ssl/server.crt -noout -dates
# Check if revoked
openssl crl -in ~/ca/crl/ca.crl -text -noout | grep -i client1
Depure problemas de conexión:
# Verbose SSL handshake
openssl s_client -connect api.example.com:443 -showcerts \
-cert ~/ca/certs/client1.crt -key ~/ca/private/client1.key
# Check Nginx logs
tail -f /var/log/nginx/error.log | grep -i ssl
# tcpdump TLS handshake
sudo tcpdump -i eth0 -A "tcp port 443" | head -100
Conclusión
mTLS proporciona autenticación mutua robusta entre clientes y servidores, esencial para asegurar comunicación de servicio interno. La configuración apropiada de CA, generación de certificados, configuración a través de proxies y gestión del ciclo de vida aseguran implementaciones de mTLS seguras y mantenibles. La rotación de certificados regular, monitoreo de revocación y pruebas exhaustivas mantienen la posición de seguridad y previenen fallos de autenticación.


