mTLS no nginx: certificados de cliente com CA privada
Proteja um painel com mTLS no nginx: crie uma CA privada com openssl, emita certificados de cliente e recuse conexoes sem certificado valido.
O que o mTLS faz
O TLS mútuo, normalmente escrito como mTLS, faz com que o nginx peça um certificado a cada cliente e recuse o pedido quando esse certificado está ausente ou não foi emitido por uma autoridade de certificação (CA) que controla. A verificação ocorre durante o handshake do TLS (segurança da camada de transporte). Assim, um cliente sem um certificado válido nunca chega à aplicação. Essa é a principal vantagem: um painel de administração ou um endpoint de métricas pode ficar na internet pública sem uma página de login e sem nada para um bot tentar adivinhar.
A configuração é pequena. Inclui uma CA privada criada com openssl, um certificado por pessoa e três diretivas no bloco de servidor do nginx. O que determina se esta configuração continuará a funcionar durante um ano é operacional. Por isso, a maior parte deste guia aborda períodos de validade, revogação, certificados individuais e o que fazer quando um cliente é recusado e ninguém consegue perceber o motivo.
Duas cadeias, não uma
Existem duas cadeias de certificados numa configuração mTLS, e não têm qualquer relação entre si. Misturá-las é o primeiro erro que quase toda a gente comete.
A primeira cadeia é a do servidor. O seu VPS apresenta um certificado para admin.example.com emitido por uma CA pública, como a Let's Encrypt, e o browser valida-o comparando-o com o repositório de raízes incluído no sistema operativo. O mTLS não altera esta parte. Se o certbot emitir hoje esse certificado por si, mantenha-o exatamente como está: consulte emitir um certificado Let's Encrypt para o nginx com o certbot.
A segunda cadeia é a do cliente. Cria uma CA própria, assina um certificado para cada pessoa que precisa de acesso e configura o nginx para confiar nessa CA, e apenas nessa CA, ao validar os clientes. Nenhum repositório público de raízes conhece a sua CA, e nenhum precisa de a conhecer. A única entidade que tem de confiar nela é o nginx, através do ficheiro ssl_client_certificate.
Por isso, ssl_client_certificate nunca afeta o certificado que o nginx apresenta, e a cadeia da Let's Encrypt nunca determina quais os clientes autorizados a entrar. Apontar ssl_client_certificate para fullchain.pem não faz o que parece: essa diretiva identifica os emissores de onde pode vir um certificado de cliente, que é a outra extremidade da ligação. Fazer o próprio servidor confiar na sua CA para ligações de saída é uma tarefa separada, descrita em adicionar a sua própria CA ao repositório de confiança do Ubuntu, e o repositório de confiança do sistema não é o que o nginx lê ao validar um cliente.
Crie a sua própria CA de clientes com openssl
Crie a CA num local diferente do servidor web. O nginx precisa apenas do certificado público da CA. A chave privada da CA assina novos certificados de cliente. Por isso, deixá-la num servidor exposto à Internet significa que uma intrusão permite ao atacante criar clientes válidos para si próprio quando quiser.
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 > crlnumberindex.txt, serial e crlnumber são a base de dados da CA. openssl ca recusa executar sem estes ficheiros. Eles também permitem a revogação posterior, porque uma lista de revogação identifica números de série. Por isso, a CA tem de registar qual foi o número de série atribuído a cada cliente.
Crie ~/client-ca/openssl.cnf. Defina dir com o caminho real desse diretório, porque openssl ca não 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,issuerAgora, crie a chave da CA e o respetivo certificado autoassinado:
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 a chave da CA com uma frase-passe, pelo que cada operação de assinatura a solicita. Esse é o objetivo. Verifique o que criou:
openssl x509 -in ca.crt -noout -subject -dates -ext basicConstraintsO subject deve identificar a sua CA e a validade deve ser de dez anos. A linha de extensão deve ser CA:TRUE, pathlen:0. pathlen:0 significa que esta CA pode assinar certificados finais, mas não pode assinar outra CA. Isto mantém a cadeia exatamente com um nível e permite deixar ssl_verify_depth inalterado.
Emitir um certificado de cliente por pessoa
Um certificado por pessoa. Nunca use um certificado partilhado por uma equipa, porque não é possível revogar um certificado partilhado sem bloquear toda a equipa, e ele não informa quem fez a chamada.
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.crtopenssl ca imprime o certificado que está prestes a assinar, pede a passphrase da CA, pede duas confirmações e acrescenta uma linha a index.txt. Adicione -batch quando o executar num script. A secção client_ext é importante por causa de uma linha: extendedKeyUsage = clientAuth. Um certificado que contenha uma lista de usos de chave estendidos apenas com serverAuth é rejeitado por não ser adequado para autenticação de cliente. Indique a finalidade em vez de esperar que ela seja inferida.
Verifique o par em relação à CA antes de o entregar:
openssl verify -CAfile ca.crt certs/alice.crtIsto imprime certs/alice.crt: OK. Qualquer outra saída significa que o certificado e a CA não correspondem. Nenhuma configuração do nginx poderá corrigir isso.
Agrupe a chave e o certificado num único ficheiro que um navegador possa importar:
openssl pkcs12 -export -inkey private/alice.key -in certs/alice.crt \
-name "alice at example ops" -out alice.p12A exportação pede uma palavra-passe que protege o ficheiro durante o transporte. Envie o ficheiro e a palavra-passe por canais diferentes e entregue às pessoas o .p12, não um .key isolado. Pode adicionar -certfile ca.crt para incluir a CA no pacote, mas o nginx não precisa disso: o nginx já contém ca.crt, portanto um certificado assinado diretamente por essa CA é validado por si só.
O OpenSSL 3, incluído no Ubuntu 24.04, grava ficheiros PKCS#12 com encriptação atual, e os navegadores e sistemas operativos em uso em agosto de 2026 conseguem lê-los. Se um importador antigo recusar o ficheiro, exporte-o novamente com -legacy, que recorre aos algoritmos antigos esperados por esse importador. Leia a mensagem apresentada pelo importador antes de usar essa opção.
Configurar o nginx com ssl_client_certificate e ssl_verify_client
Copie o certificado da CA, e apenas o certificado da CA, para o 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'O modo 644 está correto neste caso. Um certificado da CA é informação pública. A chave da CA permanece na sua estação de trabalho.
Em seguida, adicione três diretivas ao bloco do servidor que já termina o 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 é o valor predefinido do nginx e indica que o certificado do cliente deve ser assinado diretamente pela CA nesse ficheiro. Aumente-o apenas se adicionar uma CA intermédia. Durante o handshake, o nginx também envia ao cliente os nomes dos sujeitos de ssl_client_certificate. É assim que um navegador sabe quais dos seus certificados deve oferecer. Por esse motivo, use ssl_client_certificate em vez de ssl_trusted_certificate. Ambos verificam o certificado da mesma forma, mas ssl_trusted_certificate não envia nenhuma lista.
O Ubuntu 24.04 inclui o nginx 1.24, no qual o HTTP/2 é configurado na linha listen como listen 443 ssl http2;. No nginx 1.25.1 e posterior, essa forma está obsoleta e o HTTP/2 usa a sua própria diretiva, http2 on;. Nenhuma das opções altera a verificação do certificado.
Recarregue a configuração e leia o resultado:
sudo nginx -t && sudo systemctl reload nginx
curl -i https://admin.example.com/nginx -t imprime syntax is ok e test is successful. A chamada curl não envia nenhum certificado, por isso deve retornar 400 Bad Request com o corpo No required SSL certificate was sent. Isso significa que o nginx recusou a ligação no seu próprio controlo de acesso, portanto a configuração está ativa e a aplicação nem sequer foi consultada. Agora tente corretamente:
curl --cert certs/alice.crt --key private/alice.key https://admin.example.com/Isso deve retornar o conteúdo servido pela sua aplicação.
Por que o controlo deve ficar no bloco server
O certificado é trocado durante o handshake TLS, antes de o nginx ler uma linha de pedido. Nesse momento, o nginx ainda não sabe em que location o pedido vai entrar. Colocar ssl_verify_client on; dentro de um location pede ao cliente para renegociar a ligação. O TLS 1.3 removeu a renegociação e o HTTP/2 proíbe-a. Numa stack atual, esse padrão falha em vez de apresentar um pedido.
Faça o controlo de escopo diretamente. Peça um certificado ao nível do servidor e depois decida por localização:
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 contém SUCCESS, ou NONE quando o cliente não enviou nada, ou FAILED: seguido de um motivo. Com optional, o nginx pede um certificado e só o valida se receber um. É isso que permite que o caminho público /healthz acima funcione, enquanto /metrics permanece fechado. Um certificado enviado que falhe a validação continua a ser recusado pelo nginx nesse ponto. Se preferir inspecionar pessoalmente um certificado que falhou, isso corresponde a optional_no_ca. Nesse caso, o seu próprio teste tem de tratar qualquer valor diferente de SUCCESS como uma recusa.
O nginx tem códigos de estado não padrão para este caso. O error_page pode capturá-los, para que um visitante recusado receba uma explicação em vez de um 400 vazio:
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 significa que o certificado do cliente falhou na validação. 496 significa que o cliente não apresentou um certificado. Mantenha essa página em texto simples, porque a pessoa que a lê não tem sessão nem conta.
Como instalo o certificado do cliente num navegador?
O Firefox mantém o seu próprio armazenamento de certificados: Definições, depois Privacidade e segurança, depois Ver certificados, depois o separador Os seus certificados, depois Importar. Selecione o .p12 e introduza a palavra-passe.
O Chrome e o Edge usam o armazenamento do sistema operativo no Windows e no macOS. Por isso, abrir o ficheiro .p12 inicia o assistente de importação do sistema. No Linux, o Chrome lê uma base de dados NSS (serviços de segurança de rede) separada no seu diretório pessoal. A ferramenta de linha de comandos é a opção mais fiável:
sudo apt install -y libnss3-tools
pk12util -d sql:$HOME/.pki/nssdb -i alice.p12Carregue o site depois. O navegador pergunta qual certificado deve enviar. O Chrome memoriza essa escolha durante o resto da sessão do navegador. Reinicie o navegador quando quiser que a pergunta seja apresentada novamente. O certificado fica disponível apenas num perfil de navegador num computador. Por isso, um certificado importado no Firefox não fica disponível no Chrome, e nenhum dos dois fica disponível no telemóvel.
Teste com curl --cert
Faça a depuração com curl, porque ele informa o que executou.
curl -v --cert certs/alice.crt --key private/alice.key https://admin.example.com/Pode concatenar o certificado e a chave num único ficheiro PEM e passá-lo como --cert alice.pem. Se a chave tiver uma frase-passe, o curl pede-a. Também aceita --cert alice.pem:passphrase, mas a frase-passe fica no histórico da shell, por isso prefira o pedido interativo.
Vale a pena executar duas verificações antes de culpar o nginx. Primeiro, o certificado e a chave têm de formar um par:
openssl x509 -noout -pubkey -in certs/alice.crt | openssl sha256
openssl pkey -pubout -in private/alice.key | openssl sha256Dois hashes idênticos significam que os ficheiros pertencem ao mesmo par. Dois hashes diferentes significam que misturou os ficheiros de duas pessoas, e nenhum cliente indicará essa causa por si.
Segundo, o servidor deve pedir o seu CA:
openssl s_client -connect admin.example.com:443 -servername admin.example.com </dev/nullProcure o bloco Acceptable client certificate CA names na saída e o subject do seu CA dentro dele. Se esse bloco estiver totalmente ausente, o nginx não está a pedir um certificado no server block que respondeu. Nesse caso, as suas diretivas foram aplicadas noutro server block, muitas vezes no servidor predefinido.
Passando o CN do cliente para a aplicação
O certificado identifica quem fez a chamada, mas a aplicação atrás do proxy não consegue ver a camada TLS. Por isso, o nginx tem de transmitir esse nome.
map $ssl_client_s_dn $client_cn {
default "";
"~,?CN=(?<cn>[^,]+)" $cn;
}$ssl_client_s_dn contém o nome distinto do sujeito no formato RFC 2253, que se parece com CN=alice,O=Example Ops. O map extrai o campo CN para $client_cn. Mantenha o CN como um nome de utilizador simples, porque uma vírgula dentro de um CN é escapada nesse formato e a expressão regular curta acima não trata esse 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 substitui qualquer cabeçalho com esse nome enviado pelo cliente, para que ninguém possa falsificar X-Client-Cert-CN através desta localização. Duas condições garantem isso. O nginx herda proxy_set_header do nível externo apenas quando o nível interno não define nenhum valor próprio. Assim, uma segunda localização com uma linha proxy_set_header elimina silenciosamente todos os cabeçalhos definidos acima dela, incluindo este. Além disso, a aplicação tem de estar inacessível, exceto através do nginx. Isso significa associá-la a 127.0.0.1 em vez de 0.0.0.0, porque uma aplicação numa porta pública lerá o cabeçalho falsificado diretamente da Internet. O lado do proxy é explicado em uma configuração de reverse proxy nginx explicada linha a linha. Se a aplicação precisar do certificado completo em vez de apenas um nome, $ssl_client_escaped_cert transporta-o codificado em URL e seguro dentro de um cabeçalho.
Como revogar um certificado de cliente?
Quando alguém sai da organização ou um portátil desaparece, revogue apenas esse certificado. Todos os outros continuam a funcionar. Essa é precisamente a razão para emitir um certificado por pessoa.
cd ~/client-ca
openssl ca -config openssl.cnf -revoke certs/alice.crt
openssl ca -config openssl.cnf -gencrl -out crl.pemO primeiro comando altera a linha correspondente a esse número de série em index.txt, passando-a de V para R. O segundo gera uma lista de revogação de certificados (CRL), um ficheiro assinado que identifica os números de série revogados. Transfira esse ficheiro e indique-o ao nginx com ssl_crl /etc/nginx/client-ca.crl;, junto das restantes diretivas.
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'Aqui existe uma armadilha que bloqueia todos os clientes. Uma CRL contém uma data nextUpdate, definida por default_crl_days, que é 30 na configuração acima. Depois dessa data, o OpenSSL considera a lista obsoleta e a verificação falha para todos os certificados de cliente com CRL has expired, não apenas para o certificado revogado. O nginx lê o ficheiro quando carrega a configuração. Por isso, atualizar a CRL no disco não produz efeito até fazer reload. Gere a CRL novamente e faça reload segundo um calendário que fique confortavelmente dentro desse prazo: semanalmente para um prazo de 30 dias. Confirme as datas antes de copiar o ficheiro:
openssl crl -in crl.pem -noout -lastupdate -nextupdatePara poucos utilizadores, existe uma opção mais simples. A CA é sua, portanto o nginx pode recusar diretamente um número de série e dispensar a infraestrutura da CRL:
map $ssl_client_serial $revoked {
default 0;
"1002" 1;
}Combine essa diretiva com if ($revoked) { return 403; } na location. Ela não tem uma data de expiração que possa ser esquecida. Também não é distribuída, portanto qualquer outro sistema que confie na sua CA não tem conhecimento dessa revogação. Para um único nginx à frente de uma única aplicação, esta é a opção simples e adequada. Passe a usar uma CRL quando existir mais de um ponto de entrada.
Qual deve ser a validade dos certificados de cliente?
Defina a validade dos certificados de cliente para 1 ano, ou menos se conseguir lidar com o trabalho de reemissão. A expiração é a falha silenciosa neste caso, porque nada avisa o titular antecipadamente. Numa manhã, a pessoa abre o painel, o nginx recusa a ligação e o navegador descreve a recusa com as suas próprias palavras, que raramente incluem a palavra expirado. Mantenha a CA com validade de 10 anos e registe a sua data de expiração num local que irá realmente consultar, porque, quando o certificado da CA expirar, todos os certificados emitidos por ela deixarão de ser validados no mesmo dia.
Dois comandos ajudam a antecipar esse problema:
openssl x509 -in certs/alice.crt -noout -subject -serial -enddate
awk -F'\t' '{print $1, $2, $4}' ~/client-ca/index.txtA primeira coluna de index.txt indica o estado: V para válido, R para revogado e E para expirado. A segunda coluna contém a data de expiração no formato YYMMDDHHMMSSZ e a quarta contém o número de série. Esse ficheiro é o seu único registo de quem possui cada certificado. Faça uma cópia de segurança juntamente com a chave da CA e trate ambos como segredos.
A renovação cria um certificado novo. Não é uma extensão do certificado existente. Gere uma chave e uma CSR (certificate signing request) novas, assine-a, entregue o novo certificado e revogue o antigo assim que a pessoa confirmar que o novo funciona.
Contra o que o mTLS protege e contra o que não protege
O mTLS elimina o acesso não autenticado. Um scanner que encontre o seu hostname é recusado durante o handshake. Por isso, nunca envia um pedido HTTP, nunca vê um formulário de início de sessão e nunca consegue tentar uma palavra-passe roubada nesse formulário. O credential stuffing deixa de ter credenciais para testar. Uma vulnerabilidade no fluxo de início de sessão da aplicação fica inacessível a qualquer pessoa sem um certificado. O mTLS também elimina o segredo partilhado que as pessoas colam no chat, porque uma chave privada é um ficheiro difícil de copiar por acidente.
O mTLS não faz nada contra um cliente comprometido. O malware num portátil tem o ficheiro da chave e obtém a passphrase no momento em que o proprietário a introduz. Para o servidor, esse atacante parece exatamente um utilizador legítimo, porque um certificado prova a posse de um ficheiro, não a presença de uma pessoa. A palavra-passe .p12 e a encriptação integral do disco continuam a ser importantes.
O mTLS também não é autorização. Todos os certificados válidos têm acesso a tudo o que o server block disponibiliza, a menos que verifique $client_cn e atue com base nesse valor. Por predefinição, dois titulares de certificados têm acesso idêntico.
Além disso, o mTLS protege apenas o caminho através do nginx. Se a aplicação também escutar numa porta pública, o mTLS à sua frente é apenas decorativo. Faça a aplicação escutar em 127.0.0.1 e mantenha a firewall fechada nessa porta. A outra porta de entrada no mesmo sistema é o SSH, que merece a mesma atenção, abordada em proteger o acesso SSH ao seu VPS.
Existe ainda uma última limitação, que se manifesta no dia em que ativa esta configuração. Tudo o que não conseguir apresentar um certificado deixa de funcionar: um monitor de disponibilidade, um webhook de um fornecedor de pagamentos, um leitor RSS ou uma aplicação móvel sem um armazenamento de certificados acessível. Decida o que fazer com esses clientes antes de configurar ssl_verify_client on, porque a falha é total e, do lado deles, silenciosa.
Quando um cliente é recusado, leia o que o cliente comunica
A mensagem apresentada por um cliente recusado depende do navegador, da versão do curl e da biblioteca TLS subjacente. Por isso, leia o que o seu próprio cliente apresenta, em vez de comparar com uma mensagem registada noutro local. O detalhe útil está no servidor.
sudo tail -n 50 /var/log/nginx/error.logUm certificado rejeitado deixa uma linha que contém client SSL certificate verify error, seguida do motivo indicado pelo OpenSSL. Esse motivo é o facto que deve orientar a ação. Normalmente, trata-se de uma destas situações. O certificado foi emitido por uma CA diferente da indicada no ficheiro nomeado em ssl_client_certificate. O certificado está fora do período de validade. A CRL no servidor ultrapassou o seu nextUpdate e, por isso, passou a falhar para todos os clientes, e não apenas para um.
Quando o navegador nunca apresenta um certificado, o problema ocorre antes da verificação. Durante o handshake, o nginx envia os nomes dos emissores aceitáveis. O navegador não encontrou no seu repositório nenhum certificado correspondente e, por isso, não teve nada para apresentar. Importe novamente o .p12 para o perfil com que está realmente a navegar.
Há ainda um caso que vale a pena mencionar. Se testou com um certificado de cliente autoassinado isolado, em vez de um certificado assinado pela sua CA, a verificação não pode ser concluída. O nginx valida a assinatura com base no ficheiro da CA, e um certificado autoassinado não está incluído nesse ficheiro. O processo de criação do certificado é igual ao descrito em gerar um certificado autoassinado no Ubuntu. O mTLS precisa apenas do passo adicional em que a sua CA o assina.
FAQ
Ainda preciso de um certificado Let's Encrypt se usar mTLS?
Sim. Os dois certificados não têm relação entre si. O servidor apresenta o seu próprio certificado para que o navegador confie no nome do host, e esse certificado ainda tem de ser emitido por uma CA que o navegador já conheça. A CA de cliente é uma cadeia privada separada, usada apenas para verificar quem está a ligar-se. Definir ssl_client_certificate não altera o certificado que o nginx apresenta, e essa diretiva não deve apontar para a sua cadeia Let's Encrypt.
Por que motivo o meu navegador nunca me pede para escolher um certificado?
O nginx envia, durante o handshake, uma lista das entidades emissoras aceites, criada a partir do ficheiro em ssl_client_certificate. Um navegador só disponibiliza certificados cuja entidade emissora aparece nessa lista. Portanto, a ausência do pedido significa que o navegador não tem nenhum certificado da sua CA: a importação foi feita noutro perfil do navegador ou o certificado foi assinado por uma CA diferente da instalada no servidor. Execute openssl s_client -connect admin.example.com:443 e procure no resultado os nomes das CAs de certificados de cliente aceites para ver qual CA o servidor está efetivamente a solicitar.
Posso exigir um certificado de cliente apenas num URL?
Não com ssl_verify_client on dentro de um location. O certificado é trocado durante o handshake, antes de o nginx conhecer o caminho do pedido, e a renegociação que permitiria contornar isto foi removida do TLS 1.3 e é proibida no HTTP/2. Defina ssl_verify_client optional; no bloco do servidor. Depois, em cada location protegida, teste $ssl_client_verify e devolva 403 quando o valor não for SUCCESS.
Como revogo o acesso de uma pessoa?
Revogue esse certificado com openssl ca -revoke, volte a gerar a lista com openssl ca -gencrl, copie-a para o servidor e recarregue o nginx para que leia o novo ficheiro. As restantes pessoas não são afetadas. Isto só funciona se cada pessoa tiver o seu próprio certificado, em vez de partilhar um certificado. Monitorize a data nextUpdate da CRL, porque uma CRL expirada faz a verificação falhar para todos os clientes, e não apenas para os revogados.
O mTLS substitui uma página de login?
Para controlar o acesso, sim: sem um certificado, nada chega à aplicação. Assim, não há nenhum formulário para atacar nem nenhuma palavra-passe para adivinhar. Para a identidade dentro da aplicação, não. Um certificado prova que o cliente possui um ficheiro de chave, portanto um portátil roubado continua a ser um cliente válido. Encaminhe o CN para o backend, mantenha as contas e permissões que a aplicação já utiliza e trate o certificado como a barreira de acesso à frente delas.