SSD Nodes Learn 🎉 VPS desde $5.50/mes
Guías Matt ConnorPor Matt Connor

mTLS con certificados de cliente en nginx

Proteja un panel con mTLS: cree una CA privada con openssl, emita certificados de cliente y configure nginx para rechazar conexiones sin uno válido.

Qué hace mTLS

Mutual TLS, normalmente escrito como mTLS, hace que nginx solicite un certificado a cada cliente y rechace la petición cuando falta ese certificado o no lo ha emitido una autoridad certificadora (CA) que usted controle. La comprobación se realiza durante el handshake de TLS (transport layer security), por lo que un cliente sin un certificado válido nunca llega a la aplicación. Esa es la ventaja: un panel de administración o un endpoint de métricas puede estar en Internet público sin una página de inicio de sesión ni nada que un bot pueda intentar adivinar.

La configuración es pequeña. Una CA privada creada con openssl, un certificado por persona y tres directivas en el bloque server de nginx. El trabajo que determina si esto seguirá funcionando durante un año es operativo, por lo que la mayor parte de esta guía cubre las vigencias, la revocación, los certificados por persona y qué hacer cuando se rechaza a un cliente y nadie puede ver el motivo.

Dos cadenas, no una

En una configuración mTLS hay dos cadenas de certificados que no tienen ninguna relación entre sí. Confundirlas es el primer error que comete casi todo el mundo.

La primera cadena es la del servidor. Su VPS presenta un certificado para admin.example.com emitido por una CA pública, como Let's Encrypt, y el navegador lo comprueba contra el almacén de confianza que incluye el sistema operativo. mTLS no cambia esa parte. Si certbot emite hoy ese certificado, manténgalo exactamente como está: consulte cómo emitir un certificado de Let's Encrypt para nginx con certbot.

La segunda cadena es la del cliente. Cree una CA propia pequeña, firme un certificado para cada persona que necesite acceder y configure nginx para que confíe en esa CA, y sólo en ella, al comprobar los clientes. Ningún almacén de raíces público conoce su CA ni necesita conocerla. La única entidad que debe confiar en ella es nginx, mediante el archivo ssl_client_certificate.

Por tanto, ssl_client_certificate nunca afecta al certificado que presenta nginx, y la cadena de Let's Encrypt nunca determina qué clientes pueden acceder. Apuntar ssl_client_certificate a fullchain.pem no hace lo que parece: esa directiva especifica de qué emisores puede proceder un certificado de cliente, que es el otro extremo de la conexión. Hacer que el propio servidor confíe en su CA para sus conexiones salientes es una tarea independiente, descrita en cómo añadir su propia CA al almacén de confianza de Ubuntu. Además, nginx no lee el almacén de confianza del sistema cuando verifica un cliente.

Cree su propia CA de clientes con openssl

Cree la CA en un equipo distinto del servidor web. nginx sólo necesita el certificado público de la CA. La clave privada de la CA firma los nuevos certificados de cliente, por lo que dejarla en un equipo expuesto a Internet significa que una intrusión permite al atacante generar clientes válidos cuando quiera.

mkdir -p ~/client-ca/certs ~/client-ca/newcerts ~/client-ca/private ~/client-ca/csr
cd ~/client-ca
chmod 700 private
touch index.txt
echo 1000 > serial
echo 1000 > crlnumber

index.txt, serial y crlnumber son la base de datos de la CA. openssl ca no se ejecuta sin ellos. También permiten realizar revocaciones más adelante, porque una lista de revocación contiene números de serie. Por tanto, la CA debe recordar qué número de serie asignó a cada cliente.

Escriba ~/client-ca/openssl.cnf. Establezca dir en la ruta real de ese directorio, porque openssl ca no expande ~.

[ ca ]
default_ca = client_ca

[ client_ca ]
dir               = /home/you/client-ca
database          = $dir/index.txt
new_certs_dir     = $dir/newcerts
certificate       = $dir/ca.crt
private_key       = $dir/private/ca.key
serial            = $dir/serial
crlnumber         = $dir/crlnumber
default_md        = sha256
default_days      = 365
default_crl_days  = 30
policy            = policy_loose
rand_serial       = no
unique_subject    = no
email_in_dn       = no

[ policy_loose ]
commonName              = supplied
countryName             = optional
stateOrProvinceName     = optional
organizationName        = optional
organizationalUnitName  = optional
emailAddress            = optional

[ client_ext ]
basicConstraints        = CA:FALSE
keyUsage                = critical, digitalSignature, keyEncipherment
extendedKeyUsage        = clientAuth
subjectKeyIdentifier    = hash
authorityKeyIdentifier  = keyid,issuer

Ahora cree la clave de la CA y su certificado autofirmado:

openssl genrsa -aes256 -out private/ca.key 4096
chmod 600 private/ca.key
openssl req -x509 -new -key private/ca.key -sha256 -days 3650 \
  -subj "/O=Example Ops/CN=Example Ops Client CA" \
  -addext "basicConstraints=critical,CA:TRUE,pathlen:0" \
  -addext "keyUsage=critical,keyCertSign,cRLSign" \
  -out ca.crt

-aes256 protege la clave de la CA con una frase de contraseña, por lo que cada operación de firma la solicita. Ese es precisamente su objetivo. Compruebe lo que ha creado:

openssl x509 -in ca.crt -noout -subject -dates -ext basicConstraints

El asunto debe identificar a su CA y la validez debe ser de diez años. La línea de extensión debe ser CA:TRUE, pathlen:0. pathlen:0 significa que esta CA puede firmar certificados finales, pero no puede firmar otra CA. Esto mantiene la cadena con exactamente un nivel y permite dejar ssl_verify_depth sin cambios.

Emitir un certificado de cliente por persona

Un certificado por persona. Nunca un certificado compartido por un equipo, porque un certificado compartido no se puede revocar sin bloquear el acceso de todos y no indica quién realizó la llamada.

openssl genrsa -out private/alice.key 2048
openssl req -new -key private/alice.key -out csr/alice.csr \
  -subj "/O=Example Ops/CN=alice"
openssl ca -config openssl.cnf -extensions client_ext \
  -days 365 -notext -in csr/alice.csr -out certs/alice.crt

openssl ca muestra el certificado que va a firmar, solicita la frase de contraseña de la CA, pide confirmación dos veces y después añade una línea a index.txt. Añada -batch cuando lo use en scripts. La sección client_ext es importante por una línea: extendedKeyUsage = clientAuth. Un certificado cuyo uso extendido de clave sólo contiene serverAuth se rechaza porque no es adecuado para la autenticación de clientes. Indique el propósito en lugar de confiar en que funcione.

Verifique el par con la CA antes de entregarlo:

openssl verify -CAfile ca.crt certs/alice.crt

Esto muestra certs/alice.crt: OK. Cualquier otra salida significa que el certificado y la CA no coinciden. Ninguna configuración de nginx puede corregirlo.

Agrupe la clave y el certificado en un solo archivo que pueda importar un navegador:

openssl pkcs12 -export -inkey private/alice.key -in certs/alice.crt \
  -name "alice at example ops" -out alice.p12

La exportación solicita una contraseña que protege el archivo durante la transferencia. Envíe el archivo y la contraseña por canales distintos y entregue a los usuarios el .p12 en lugar de un .key sin protección. Puede añadir -certfile ca.crt para incluir la CA en el paquete, pero nginx no lo necesita: nginx ya contiene ca.crt, por lo que un certificado firmado directamente por esa CA se verifica por sí solo.

OpenSSL 3, que Ubuntu 24.04 incluye, escribe archivos PKCS#12 con cifrado actual, y los navegadores y sistemas operativos en uso en agosto de 2026 pueden leerlos. Si un importador antiguo rechaza el archivo, vuelva a exportarlo con -legacy añadido. Esta opción usa los algoritmos antiguos que espera ese importador. Lea el mensaje que muestra el importador antes de usar esa opción.

Configurar nginx con ssl_client_certificate y ssl_verify_client

Copie el certificado de la CA, y sólo el certificado de la CA, al servidor.

scp ca.crt user@admin.example.com:/tmp/client-ca.crt
ssh user@admin.example.com \
  'sudo install -o root -g root -m 644 /tmp/client-ca.crt /etc/nginx/client-ca.crt'

El modo 644 es correcto en este caso. Un certificado de CA es información pública. La clave de la CA permanece en su estación de trabajo.

Después, añada tres directivas al bloque server que ya termina TLS:

server {
    listen 443 ssl;
    server_name admin.example.com;

    ssl_certificate     /etc/letsencrypt/live/admin.example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/admin.example.com/privkey.pem;

    ssl_client_certificate /etc/nginx/client-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-Forwarded-Proto $scheme;
    }
}

ssl_verify_depth 1 es el valor predeterminado de nginx e indica que el certificado del cliente debe estar firmado directamente por la CA de ese archivo. Auméntelo sólo si añade una CA intermedia. nginx también envía los nombres de sujeto de ssl_client_certificate al cliente durante el handshake. Así, el navegador sabe qué certificados puede ofrecer. Por eso se debe usar ssl_client_certificate en lugar de ssl_trusted_certificate. Ambos verifican de la misma forma, pero ssl_trusted_certificate no envía ninguna lista.

Ubuntu 24.04 incluye nginx 1.24, donde HTTP/2 se especifica en la línea listen como listen 443 ssl http2;. En nginx 1.25.1 y posteriores, esa forma está obsoleta y HTTP/2 usa su propia directiva, http2 on;. Ninguna de las dos opciones cambia la comprobación del certificado.

Recargue la configuración y lea el resultado:

sudo nginx -t && sudo systemctl reload nginx
curl -i https://admin.example.com/

nginx -t muestra syntax is ok y test is successful. La llamada curl no lleva ningún certificado, por lo que debería responder 400 Bad Request con el cuerpo No required SSL certificate was sent. nginx la rechaza en su propio control de acceso. Esto significa que la configuración está activa y que nunca se solicitó la aplicación. Ahora inténtelo correctamente:

curl --cert certs/alice.crt --key private/alice.key https://admin.example.com/

Debería devolver lo que sirva su aplicación.

Por qué la barrera debe estar en el bloque server

El certificado se intercambia durante el handshake TLS, antes de que nginx haya leído una línea de solicitud. Por tanto, en ese momento nginx no sabe en qué location terminará la solicitud. Colocar ssl_verify_client on; dentro de un location pide al cliente que renegocie en mitad de la conexión. TLS 1.3 eliminó la renegociación y HTTP/2 la prohíbe. En una pila actual, ese patrón falla en lugar de solicitar el certificado.

Defina el ámbito manualmente. Solicite el certificado en el nivel del servidor y, después, decida qué hacer en cada ubicación:

ssl_verify_client optional;

location /metrics {
    if ($ssl_client_verify != SUCCESS) { return 403; }
    proxy_pass http://127.0.0.1:9090;
}

location /healthz {
    proxy_pass http://127.0.0.1:8080;
}

$ssl_client_verify contiene SUCCESS, o NONE si el cliente no envió ninguno, o FAILED: seguido de un motivo. Con optional, nginx solicita un certificado y sólo lo verifica si recibe uno. Esto permite que la ruta pública /healthz anterior funcione mientras /metrics permanece cerrada. nginx sigue rechazando en ese punto un certificado enviado que no supera la verificación. Si prefiere inspeccionar personalmente un certificado que falla, use optional_no_ca. En ese caso, su propia prueba debe tratar cualquier valor distinto de SUCCESS como un rechazo.

nginx usa códigos de estado no estándar para este caso. error_page puede capturarlos para que un visitante rechazado reciba una explicación en lugar de un 400 sin contenido:

error_page 495 496 = @needcert;

location @needcert {
    default_type text/plain;
    return 200 "This host requires a client certificate. Ask ops for one.\n";
}

495 indica que el certificado del cliente no superó la verificación. 496 indica que el cliente no presentó ningún certificado. Mantenga esa página en texto sin formato, porque la persona que la lee no tiene sesión ni cuenta.

¿Cómo instalo el certificado de cliente en un navegador?

Firefox mantiene su propio almacén de certificados: Configuración, después Privacidad y seguridad, luego Ver certificados, la pestaña Sus certificados, Importar y, por último, seleccione el archivo .p12 e introduzca su contraseña.

Chrome y Edge usan el almacén del sistema operativo en Windows y macOS, por lo que al abrir el archivo .p12 se inicia el asistente de importación del sistema. En Linux, Chrome lee una base de datos NSS (network security services) independiente en el directorio personal del usuario. La herramienta de línea de comandos es la opción más fiable:

sudo apt install -y libnss3-tools
pk12util -d sql:$HOME/.pki/nssdb -i alice.p12

Después, cargue el sitio. El navegador preguntará qué certificado debe enviar. Chrome recuerda esa selección durante el resto de la sesión del navegador. Reinicie el navegador cuando quiera que vuelva a preguntar. El certificado sólo está disponible en un perfil de navegador de un equipo. Por tanto, un certificado importado en Firefox no está disponible en Chrome, y ninguno de los dos está disponible en el teléfono.

Pruebas con curl --cert

Depure con curl porque informa de las operaciones que realizó.

curl -v --cert certs/alice.crt --key private/alice.key https://admin.example.com/

Puede concatenar el certificado y la clave en un único archivo PEM y pasarlo como --cert alice.pem. Si la clave tiene una frase de contraseña, curl la solicita. También acepta --cert alice.pem:passphrase, pero la frase queda en el historial del shell, así que use la solicitud interactiva.

Conviene realizar dos comprobaciones antes de culpar a nginx. Primero, el certificado y la clave deben formar un par:

openssl x509 -noout -pubkey -in certs/alice.crt | openssl sha256
openssl pkey -pubout -in private/alice.key | openssl sha256

Dos hashes idénticos indican que los archivos corresponden entre sí. Dos hashes diferentes indican que mezcló los archivos de dos personas, y ningún cliente le indicará esa causa.

Segundo, el servidor debería solicitar su CA:

openssl s_client -connect admin.example.com:443 -servername admin.example.com </dev/null

Busque el bloque Acceptable client certificate CA names en la salida y el subject de su CA dentro de él. Si falta por completo, nginx no está solicitando un certificado en el server block que respondió. Por tanto, las directivas se aplicaron en otro, normalmente el servidor predeterminado.

Pasar el CN del cliente a la aplicación

El certificado indica quién realizó la llamada, pero la aplicación situada detrás del proxy no puede ver la capa TLS. Por tanto, nginx debe pasarle ese nombre.

map $ssl_client_s_dn $client_cn {
    default              "";
    "~,?CN=(?<cn>[^,]+)" $cn;
}

$ssl_client_s_dn contiene el nombre distinguido del sujeto en formato RFC 2253, que tiene un aspecto similar a CN=alice,O=Example Ops. El mapa extrae el campo CN y lo introduce en $client_cn. Mantenga el CN como un nombre de usuario simple, porque una coma dentro de un CN se escapa en ese formato y la expresión regular anterior no gestiona ese escape.

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-Forwarded-Proto    $scheme;
    proxy_set_header X-Client-Cert-CN     $client_cn;
    proxy_set_header X-Client-Cert-Serial $ssl_client_serial;
}

proxy_set_header sustituye cualquier cabecera con ese nombre que haya enviado el cliente. Así, nadie puede falsificar X-Client-Cert-CN desde esta ubicación. Hay dos condiciones que mantienen esta garantía. nginx hereda proxy_set_header del nivel exterior sólo cuando el nivel interior no define ninguna directiva propia. Por tanto, una segunda ubicación con una línea proxy_set_header hace que se pierdan silenciosamente todas las cabeceras establecidas por encima, incluida esta. Además, la aplicación debe ser inaccesible salvo a través de nginx. Esto significa enlazarla a 127.0.0.1 en lugar de 0.0.0.0, porque una aplicación en un puerto público leerá la cabecera falsificada directamente desde Internet. La configuración del lado del proxy se explica en una configuración de proxy inverso de nginx explicada línea por línea. Si la aplicación necesita el certificado completo en lugar de un nombre, $ssl_client_escaped_cert lo contiene codificado en URL y seguro para usarlo dentro de una cabecera.

¿Cómo revoco un único certificado de cliente?

Alguien deja la organización o se pierde un portátil. Revoque ese certificado y los demás seguirán funcionando. Ese es precisamente el motivo para emitir un certificado por persona.

cd ~/client-ca
openssl ca -config openssl.cnf -revoke certs/alice.crt
openssl ca -config openssl.cnf -gencrl -out crl.pem

El primer comando cambia la línea de ese número de serie en index.txt de V a R. El segundo genera una lista de revocación de certificados (CRL), un archivo firmado que contiene los números de serie revocados. Distribúyalo y configure nginx para que lo use con ssl_crl /etc/nginx/client-ca.crl; junto a las demás directivas.

scp crl.pem user@admin.example.com:/tmp/client-ca.crl
ssh user@admin.example.com 'sudo install -m 644 /tmp/client-ca.crl /etc/nginx/client-ca.crl && sudo nginx -t && sudo systemctl reload nginx'

Este es el problema que bloquea el acceso de todos. Una CRL contiene una fecha nextUpdate, establecida mediante default_crl_days, que es 30 en la configuración anterior. Cuando pasa esa fecha, OpenSSL considera que la lista está obsoleta y la verificación falla para todos los certificados de cliente con CRL has expired, no sólo para el certificado revocado. nginx lee el archivo cuando carga su configuración, por lo que una CRL nueva en el disco no tiene efecto hasta recargar la configuración. Genérela y recargue nginx con una frecuencia que quede claramente dentro del periodo de validez: una vez por semana para un periodo de 30 días. Compruebe las fechas antes de copiarla:

openssl crl -in crl.pem -noout -lastupdate -nextupdate

Para un número reducido de usuarios existe una opción más sencilla. La CA es suya, por lo que nginx puede rechazar directamente un número de serie y omitir el mecanismo de CRL:

map $ssl_client_serial $revoked {
    default 0;
    "1002"  1;
}

Combine esto con if ($revoked) { return 403; } en la ubicación. No tiene una fecha de caducidad que pueda olvidar. Tampoco se distribuye, por lo que cualquier otro sistema que confíe en su CA no sabrá nada de esta restricción. Para un único nginx delante de una sola aplicación, es una solución sencilla y adecuada. Cambie a una CRL cuando haya más de un punto de acceso.

¿Cuánto tiempo deben ser válidos los certificados de cliente?

Asigne a los certificados de cliente una validez de un año, o menor si puede asumir el trabajo de volver a emitirlos. La caducidad es el fallo silencioso en este caso, porque nada avisa al titular con antelación. Una mañana abre el panel, nginx rechaza la conexión y el navegador describe el rechazo con sus propios términos, que rara vez incluyen la palabra «caducado». Mantenga la CA con una validez de diez años y anote su fecha de caducidad en un lugar que vaya a consultar, porque cuando caduca el certificado de la CA, todos los certificados emitidos por ella dejan de validarse el mismo día.

Dos comandos le permiten anticiparse:

openssl x509 -in certs/alice.crt -noout -subject -serial -enddate
awk -F'\t' '{print $1, $2, $4}' ~/client-ca/index.txt

La primera columna de index.txt indica el estado: V para válido, R para revocado y E para caducado. La segunda columna muestra la fecha de caducidad en formato YYMMDDHHMMSSZ y la cuarta contiene el número de serie. Ese archivo es el único registro de quién tiene cada certificado, así que haga una copia de seguridad junto con la clave de la CA y trate ambos como secretos.

La renovación consiste en emitir un certificado nuevo, no en ampliar el anterior. Genere una clave y una CSR (solicitud de firma de certificado) nuevas, fírmela, entréguela y revoque el certificado antiguo cuando la persona confirme que el nuevo funciona.

Contra qué protege mTLS y contra qué no

Elimina el acceso no autenticado. Un escáner que encuentra el nombre de host recibe un rechazo durante el handshake. Por tanto, nunca envía una solicitud HTTP, nunca ve un formulario de inicio de sesión y nunca puede probar una contraseña robada contra él. El credential stuffing no tiene credenciales que probar. Una vulnerabilidad en el flujo de inicio de sesión de la aplicación no es accesible para nadie que no tenga un certificado. También elimina el secreto compartido que las personas pegan en los chats, porque una clave privada es un archivo difícil de copiar por accidente.

No protege contra un cliente comprometido. El malware de un portátil tiene el archivo de clave y obtiene la frase de contraseña en cuanto el propietario la introduce. Para el servidor, ese atacante es indistinguible de un usuario legítimo, porque un certificado demuestra la posesión de un archivo, no la presencia de una persona. La contraseña .p12 y el cifrado de disco completo siguen siendo importantes.

Tampoco es autorización. Todo certificado válido puede acceder a todo lo que sirva ese bloque de servidor, salvo que compruebe $client_cn y actúe según su valor. De forma predeterminada, dos titulares de certificados tienen el mismo acceso.

Además, sólo protege la ruta que pasa por nginx. Si la aplicación también escucha en un puerto público, mTLS delante de ella es una medida inútil: vincule la aplicación a 127.0.0.1 y mantenga cerrado en el firewall su puerto. La otra puerta de entrada al mismo sistema es SSH y merece la misma atención, como se explica en reforzar el acceso SSH en su VPS.

Hay un último límite, que se hace evidente el día que lo activa. Todo lo que no pueda presentar un certificado deja de funcionar: un monitor de disponibilidad, un webhook de un proveedor de pagos, un lector RSS o una aplicación móvil cuyo almacén de certificados no pueda configurar. Decida qué hacer con esos clientes antes de establecer ssl_verify_client on, porque el fallo es total y, en su extremo, silencioso.

Cuando se rechaza un cliente, lea lo que informa el cliente

El mensaje que muestra un cliente rechazado depende del navegador, de la versión de curl y de la biblioteca TLS subyacente. Por eso, lea lo que imprime su propio cliente en lugar de compararlo con un mensaje anotado en otro lugar. El detalle útil está en el servidor.

sudo tail -n 50 /var/log/nginx/error.log

Un certificado rechazado deja una línea que contiene client SSL certificate verify error seguida del motivo indicado por OpenSSL. Ese motivo es el dato sobre el que debe actuar. Normalmente se debe a una de varias causas. El certificado procede de una CA distinta de la especificada en el archivo indicado en ssl_client_certificate. El certificado está fuera de su periodo de validez. La CRL del servidor ha superado su nextUpdate, por lo que ahora rechaza a todos los clientes en lugar de a uno solo.

Cuando el navegador no ofrece ningún certificado, el problema se produce antes de la verificación. nginx envía los nombres de los emisores aceptables durante el handshake, y el navegador no encuentra en su almacén ningún certificado que coincida. Por eso no tiene ninguno que ofrecer. Vuelva a importar .p12 en el perfil con el que realmente navega.

Conviene mencionar otro caso. Si realizó la prueba con un certificado de cliente autofirmado aislado, en lugar de uno firmado por su CA, la verificación no puede completarse porque nginx comprueba la firma con el archivo de la CA y el certificado autofirmado no está incluido en él. El procedimiento para crear el certificado es el mismo que se describe en generar un certificado autofirmado en Ubuntu. mTLS sólo necesita el paso adicional en el que su CA lo firma.

FAQ

¿Sigo necesitando un certificado de Let's Encrypt si uso mTLS?

Sí. Los dos certificados no están relacionados. El servidor presenta su propio certificado para que el navegador confíe en el nombre de host, y ese certificado todavía debe proceder de una CA que el navegador ya conozca. La CA de cliente es una cadena privada independiente que sólo se usa para comprobar quién se está conectando. Configurar ssl_client_certificate no cambia nada del certificado que presenta nginx, y no debe apuntar a la cadena de Let's Encrypt.

¿Por qué mi navegador nunca me pide que elija un certificado?

nginx envía durante el handshake una lista de emisores aceptables, generada a partir del archivo de ssl_client_certificate. El navegador sólo ofrece certificados cuyo emisor aparece en esa lista. Por tanto, si no aparece ninguna solicitud, el navegador no tiene ningún certificado de su CA: la importación se hizo en otro perfil del navegador o el certificado fue firmado por una CA diferente de la instalada en el servidor. Ejecute openssl s_client -connect admin.example.com:443 y busque en la salida los nombres de las CA de certificados de cliente aceptables para comprobar qué CA está solicitando realmente el servidor.

¿Puedo exigir un certificado de cliente sólo en una URL?

No con ssl_verify_client on dentro de un location. El certificado se intercambia durante el handshake, antes de que nginx conozca la ruta de la petición, y la renegociación que permitiría evitarlo ya no existe en TLS 1.3 y está prohibida en HTTP/2. Configure ssl_verify_client optional; en el bloque server; después, en cada location protegida, compruebe $ssl_client_verify y devuelva 403 cuando no sea SUCCESS.

¿Cómo revoco el acceso de una persona?

Revoque ese certificado con openssl ca -revoke, regenere la lista con openssl ca -gencrl, cópiela al servidor y recargue nginx para que lea el archivo nuevo. El resto de usuarios no se ve afectado. Esto sólo funciona si cada persona tiene su propio certificado y no uno compartido. Compruebe la fecha nextUpdate de la CRL, porque una CRL caducada hace que falle la verificación para todos los clientes, no sólo para los certificados revocados.

¿mTLS sustituye una página de inicio de sesión?

Para acceder, sí: sin un certificado, nada llega a la aplicación, por lo que no hay ningún formulario que atacar ni ninguna contraseña que adivinar. Para la identidad dentro de la aplicación, no. Un certificado demuestra que el cliente posee un archivo de clave, por lo que un portátil robado corresponde a un usuario válido. Transmita el CN al backend, conserve las cuentas y los permisos que la aplicación ya utiliza y trate el certificado como la barrera de acceso situada delante de ellos.

#tls#mtls#nginx#openssl#access-control