SSD Nodes Learn 🎉 VPS da $5.50/mese
Guide Matt ConnorDi Matt Connor

mTLS con certificati client e nginx

Proteggi un pannello admin con mTLS: crea una CA privata con openssl, emetti certificati client e configura nginx per rifiutare chi ne è privo.

Cosa fa mTLS

Mutual TLS, generalmente indicato come mTLS, fa sì che nginx richieda un certificato a ogni client e rifiuti la richiesta se il certificato manca o non è stato emesso da una certificate authority (CA) sotto il tuo controllo. Il controllo avviene durante l’handshake TLS (transport layer security), quindi un chiamante privo di un certificato client valido non raggiunge mai l’applicazione. Questo è il vantaggio principale: un pannello di amministrazione o un endpoint per le metriche può essere esposto su Internet senza una pagina di login e senza lasciare nulla che un bot possa tentare di indovinare.

La configurazione è ridotta. Una CA privata creata con openssl, un certificato per ogni persona e tre direttive nel blocco server di nginx. La parte che determina se la soluzione continuerà a funzionare per un anno è operativa. Per questo, la maggior parte della guida riguarda la durata dei certificati, la revoca, i certificati individuali e le procedure da seguire quando un client viene rifiutato senza una causa visibile.

Due catene, non una

In una configurazione mTLS esistono due catene di certificati, completamente indipendenti. Confonderle è il primo errore che commette quasi chiunque.

La prima è la catena del server. Il tuo VPS presenta un certificato per admin.example.com, emesso da una CA pubblica come Let's Encrypt, e il browser lo verifica rispetto all'archivio delle CA radice incluso nel sistema operativo. mTLS non modifica questo aspetto. Se oggi certbot emette il certificato per te, lascialo esattamente così com'è: vedi emissione di un certificato Let's Encrypt per nginx con certbot.

La seconda è la catena del client. Crei una piccola CA privata, firmi un certificato per ogni persona che deve accedere e dici a nginx di considerare attendibile quella CA, e solo quella CA, durante la verifica dei client. Nessun archivio pubblico delle CA radice conosce la tua CA e non è necessario che la conosca. L'unico componente che deve considerarla attendibile è nginx, tramite il file ssl_client_certificate.

Di conseguenza, ssl_client_certificate non influisce mai sul certificato presentato da nginx e la catena Let's Encrypt non influisce mai sui client autorizzati ad accedere. Impostare ssl_client_certificate su fullchain.pem non produce l'effetto che potrebbe sembrare: questa direttiva indica da quali emittenti può provenire un certificato client, cioè dall'altra parte della connessione. Fare in modo che il server consideri attendibile la tua CA per le proprie connessioni in uscita è un'attività distinta, descritta in aggiunta della propria CA all'archivio delle CA attendibili di Ubuntu; inoltre, nginx non legge l'archivio di sistema delle CA attendibili quando verifica un client.

Crea una CA client personalizzata con openssl

Crea la CA in una posizione diversa dal server web. nginx ha bisogno soltanto del certificato pubblico della CA. La chiave privata della CA firma i nuovi certificati client. Se la lasci su un server esposto a Internet, una compromissione consente all'attaccante di generare a piacimento client validi per proprio conto.

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 e crlnumber costituiscono il database della CA. openssl ca non viene eseguito senza questi file. Sono necessari anche per poter revocare i certificati in seguito, perché una lista di revoca contiene i numeri seriali. La CA deve quindi ricordare a chi è stato assegnato ogni numero seriale.

Scrivi ~/client-ca/openssl.cnf. Imposta dir sul percorso reale di quella directory, perché openssl ca non espande ~.

[ 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

Ora crea la chiave della CA e il relativo certificato autofirmato:

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 protegge la chiave della CA con una passphrase, quindi ogni operazione di firma la richiede. È proprio questo lo scopo della passphrase. Verifica ciò che hai creato:

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

Il subject deve identificare la tua CA e la validità deve essere di dieci anni. La riga dell'estensione deve essere CA:TRUE, pathlen:0. pathlen:0 indica che questa CA può firmare certificati finali, ma non può firmare un'altra CA. In questo modo la catena ha esattamente un livello e puoi lasciare ssl_verify_depth invariato.

Emettere un certificato client per persona

Un certificato per persona. Mai un certificato condiviso da un team, perché un certificato condiviso non può essere revocato senza impedire l'accesso a tutti e non consente di sapere chi ha effettuato la chiamata.

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 stampa il certificato che sta per firmare, richiede la passphrase della CA, chiede due volte la conferma, quindi aggiunge una riga a index.txt. Quando si esegue lo script, aggiungere -batch. La sezione client_ext è importante per una riga: extendedKeyUsage = clientAuth. Un certificato che contiene un elenco di utilizzi estesi della chiave limitato a serverAuth viene rifiutato perché non è idoneo all'autenticazione client. Specificare quindi lo scopo invece di lasciarlo implicito.

Verificare la coppia rispetto alla CA prima di consegnarla:

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

Questo comando stampa certs/alice.crt: OK. Qualsiasi altro output indica che il certificato e la CA non corrispondono; nessuna configurazione di nginx può risolvere il problema.

Raggruppare la chiave e il certificato in un unico file che un browser possa importare:

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

L'esportazione richiede una password, che protegge il file durante il trasferimento. Inviare il file e la password tramite canali diversi e consegnare il .p12 invece di un semplice .key. È possibile aggiungere -certfile ca.crt per includere la CA nel bundle, ma nginx non ne ha bisogno: nginx contiene già ca.crt, quindi un certificato firmato direttamente da quella CA viene verificato autonomamente.

OpenSSL 3, incluso in Ubuntu 24.04, scrive i file PKCS#12 usando la crittografia attuale e, ad agosto 2026, i browser e i sistemi operativi in uso li leggono. Se un vecchio strumento di importazione rifiuta il file, riesportarlo aggiungendo -legacy, che reintroduce gli algoritmi precedenti richiesti da quello strumento. Leggere il messaggio restituito dallo strumento di importazione prima di usare quel flag.

Configura nginx con ssl_client_certificate e ssl_verify_client

Copia sul server il certificato della CA, e soltanto il certificato della CA.

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'

La modalità 644 è corretta in questo caso. Un certificato CA è un'informazione pubblica. La chiave della CA resta sulla workstation.

Aggiungi quindi tre direttive al server block che gestisce già la terminazione 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 è il valore predefinito di nginx e indica che il certificato client deve essere firmato direttamente dalla CA contenuta in quel file. Aumentalo solo se aggiungi una CA intermedia. Durante l'handshake, nginx invia inoltre al client i nomi dei subject presenti in ssl_client_certificate. In questo modo il browser sa quali certificati offrire. Per questo è preferibile usare ssl_client_certificate invece di ssl_trusted_certificate: la verifica avviene nello stesso modo, ma non viene inviato alcun elenco.

Ubuntu 24.04 include nginx 1.24, in cui HTTP/2 viene specificato nella riga listen tramite listen 443 ssl http2;. In nginx 1.25.1 e versioni successive questa forma è deprecata e HTTP/2 usa una direttiva separata, http2 on;. Nessuna delle due opzioni modifica la verifica del certificato.

Ricarica la configurazione e leggi il risultato:

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

nginx -t stampa syntax is ok e test is successful. La richiesta curl non contiene alcun certificato, quindi dovrebbe restituire 400 Bad Request con il corpo No required SSL certificate was sent. nginx rifiuta quindi la richiesta al proprio livello di controllo: la configurazione è attiva e l'applicazione non è mai stata contattata. Ora prova con un certificato:

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

Dovrebbe restituire ciò che espone la tua applicazione.

Perché la richiesta del certificato appartiene al blocco server

Il certificato viene scambiato durante l'handshake TLS, prima che nginx abbia letto una riga della richiesta. In quel momento nginx non sa in quale location finirà la richiesta. Inserire ssl_verify_client on; dentro un location chiede al client di rinegoziare nel corso della connessione. TLS 1.3 ha rimosso la rinegoziazione e HTTP/2 la vieta, quindi su uno stack attuale questo schema fallisce invece di inviare una richiesta.

Definisci direttamente l'ambito. Richiedi un certificato a livello server, quindi decidi per ogni location:

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, oppure NONE quando il client non ha inviato nulla, oppure FAILED: seguito da una motivazione. Con optional, nginx richiede un certificato e lo verifica soltanto se ne riceve uno. Questo consente di mantenere accessibile il percorso pubblico /healthz definito sopra, mentre /metrics resta chiuso. Se un certificato viene inviato ma non supera la verifica, nginx lo rifiuta comunque in quel punto. Se invece vuoi analizzare personalmente un certificato non valido, usa optional_no_ca; in questo caso il tuo controllo deve considerare un rifiuto qualsiasi valore diverso da SUCCESS.

nginx usa codici di stato non standard per questo caso. error_page può intercettarli, così un visitatore rifiutato riceve una spiegazione invece di un semplice errore 400:

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 che il certificato del client non ha superato la verifica. 496 indica che il client non ha presentato alcun certificato. Mantieni questa pagina in formato testo semplice, perché chi la legge non dispone di una sessione né di un account.

Come si installa il certificato client in un browser?

Firefox utilizza un archivio di certificati separato: apri Impostazioni, quindi Privacy e sicurezza, Visualizza certificati, la scheda I tuoi certificati, Importa, seleziona il file .p12 e inserisci la relativa password.

Chrome ed Edge utilizzano l'archivio del sistema operativo su Windows e macOS. L'apertura del file .p12 avvia la procedura guidata di importazione del sistema. Su Linux, Chrome legge un database NSS (network security services) separato nella home directory. In questo caso, il metodo più affidabile è usare lo strumento dalla riga di comando:

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

Dopo l'importazione, apri il sito. Il browser chiede quale certificato inviare. Chrome memorizza questa scelta per il resto della sessione del browser. Riavvia il browser quando vuoi che la richiesta venga visualizzata di nuovo. Il certificato è disponibile in un solo profilo del browser su un solo computer. Di conseguenza, un certificato importato in Firefox non è disponibile in Chrome, e nessuno dei due è disponibile sul telefono.

Test con curl --cert

Esegui il debug con curl, perché mostra le operazioni eseguite.

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

Puoi concatenare il certificato e la chiave in un unico file PEM e passarlo con --cert alice.pem. Se la chiave ha una passphrase, curl la richiede. Accetta anche --cert alice.pem:passphrase, ma in questo caso la passphrase rimane nella cronologia della shell; usa quindi la richiesta interattiva.

Prima di attribuire il problema a nginx, esegui due controlli. Il certificato e la chiave devono formare una coppia:

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

Due hash identici indicano che i file appartengono alla stessa coppia. Due hash diversi indicano che hai associato i file sbagliati e nessun client ti segnalerà direttamente questa causa.

In secondo luogo, il server dovrebbe richiedere il certificato della tua CA:

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

Nell'output cerca il blocco Acceptable client certificate CA names e il subject della tua CA al suo interno. Se il blocco manca completamente, nginx non sta richiedendo un certificato nel server block che ha risposto. Le tue direttive sono quindi finite in un server block diverso, spesso quello predefinito.

Passaggio del CN del client all'applicazione

Il certificato indica chi ha effettuato la richiesta, ma l'applicazione dietro il proxy non può vedere il livello TLS. nginx deve quindi passarle questo nome.

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

$ssl_client_s_dn contiene il subject distinguished name nel formato RFC 2253, che ha un aspetto simile a CN=alice,O=Example Ops. La map estrae il campo CN e lo inserisce in $client_cn. Mantenete il CN come nome utente semplice, perché in questo formato una virgola all'interno di un CN viene sottoposta a escape e la piccola espressione regolare precedente non gestisce questo caso.

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 sostituisce qualsiasi header con quel nome inviato dal client. In questo modo nessuno può falsificare X-Client-Cert-CN tramite questa location. Due condizioni garantiscono questo comportamento. nginx eredita proxy_set_header dal livello esterno solo quando il livello interno non ne definisce alcuno. Di conseguenza, una seconda location con una sola direttiva proxy_set_header perde silenziosamente tutti gli header impostati a un livello superiore, incluso questo. Inoltre, l'applicazione deve essere raggiungibile esclusivamente tramite nginx. Questo significa associarla a 127.0.0.1 invece che a 0.0.0.0, perché un'applicazione in ascolto su una porta pubblica leggerà l'header falsificato direttamente da Internet. Il lato proxy di questa configurazione è descritto in una configurazione di reverse proxy nginx spiegata riga per riga. Se l'applicazione richiede il certificato completo invece del solo nome, $ssl_client_escaped_cert lo trasporta con codifica URL e in forma sicura all'interno di un header.

Come revoco il certificato di un singolo client?

Se una persona lascia l'organizzazione o un laptop viene smarrito, revochi quel certificato e tutti gli altri continuano a funzionare. Questo è il motivo principale per cui si emette un certificato per ogni persona.

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

Il primo comando modifica la riga relativa a quel seriale in index.txt, sostituendo V con R. Il secondo genera una certificate revocation list (CRL), cioè un file firmato che contiene i numeri seriali revocati. Copia la CRL sul server e indica il relativo percorso a nginx con ssl_crl /etc/nginx/client-ca.crl;, accanto alle altre direttive.

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'

Qui si trova il problema che può bloccare tutti gli utenti. Una CRL contiene una data nextUpdate, impostata da default_crl_days, che nella configurazione precedente è 30. Dopo questa data, OpenSSL considera l'elenco obsoleto e la verifica fallisce per tutti i certificati client con CRL has expired, non soltanto per quello revocato. nginx legge il file quando carica la configurazione, quindi la sostituzione della CRL sul disco non produce effetti fino al reload. Rigenera la CRL ed esegui il reload secondo una pianificazione che resti ampiamente entro questa finestra: con 30 giorni, una volta alla settimana. Controlla le date prima di copiare il file:

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

Per pochi utenti esiste un'opzione più semplice. La CA è sotto il tuo controllo, quindi nginx può rifiutare direttamente un seriale senza usare la gestione delle CRL:

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

Abbina questa configurazione a if ($revoked) { return 403; } nella location. Non ha una data di scadenza da dimenticare. Inoltre non viene distribuita, quindi qualsiasi altro sistema che si fida della tua CA non ne è a conoscenza. Per un solo nginx davanti a una sola applicazione, è la soluzione semplice e corretta. Passa alla CRL quando esiste più di un punto di accesso.

Quanto devono durare i certificati client?

Assegna ai certificati client una validità di un anno, oppure inferiore se puoi gestire il lavoro di riemissione. La scadenza è il problema più difficile da rilevare, perché il titolare non riceve alcun avviso in anticipo. Una mattina apre il pannello, nginx rifiuta la connessione e il browser descrive il rifiuto con parole proprie, che raramente includono il termine «scaduto». Mantieni la CA valida per dieci anni e annota la relativa data di scadenza in un luogo che controllerai davvero, perché quando scade il certificato della CA tutti i certificati firmati da quella CA smettono di essere verificati nello stesso giorno.

Due comandi consentono di evitare questo problema:

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

La prima colonna di index.txt indica lo stato: V per i certificati validi, R per quelli revocati e E per quelli scaduti. La seconda colonna indica la scadenza nel formato YYMMDDHHMMSSZ, mentre la quarta contiene il seriale. Questo file è l'unico registro dei titolari e dei certificati assegnati, quindi esegui il backup insieme alla chiave della CA e tratta entrambi come dati riservati.

Il rinnovo consiste nell'ottenere un nuovo certificato, non nell'estendere quello esistente. Genera una nuova chiave e una CSR (certificate signing request), firma la CSR, consegna il nuovo certificato e revoca quello precedente dopo che l'utente ha confermato che il nuovo certificato funziona.

Cosa protegge mTLS e cosa invece non protegge

mTLS elimina l'accesso non autenticato. Uno scanner che individua il tuo hostname viene rifiutato durante l'handshake. Non invia quindi alcuna richiesta HTTP, non visualizza un modulo di accesso e non può provare una password rubata. Il credential stuffing non ha credenziali su cui operare. Una vulnerabilità nel flusso di autenticazione dell'applicazione non è raggiungibile da chi non possiede un certificato. mTLS elimina inoltre il secret condiviso che gli utenti incollano nelle chat, perché una chiave privata è un file che non si copia accidentalmente con facilità.

Non protegge invece da un client compromesso. Un malware presente su un laptop dispone del file della chiave e della passphrase non appena il proprietario la inserisce. Per il server, l'attaccante appare esattamente come un utente legittimo, perché un certificato dimostra il possesso di un file, non la presenza di una persona. La password .p12 e la cifratura completa del disco restano quindi importanti.

mTLS non fornisce nemmeno l'autorizzazione. Ogni certificato valido può raggiungere tutto ciò che espone quel server block, a meno che tu non controlli $client_cn e agisca in base al suo valore. Per impostazione predefinita, due titolari di certificati hanno lo stesso livello di accesso.

Inoltre, mTLS protegge soltanto il percorso che passa da nginx. Se l'applicazione è in ascolto anche su una porta pubblica, mTLS davanti all'applicazione è solo una protezione apparente: associa l'applicazione a 127.0.0.1 e mantieni chiuso nel firewall il relativo accesso. L'altra porta di ingresso allo stesso sistema è SSH e richiede la stessa attenzione, descritta in protezione dell'accesso SSH sul tuo VPS.

Resta un'ultima limitazione, che si manifesta il giorno in cui abiliti mTLS. Tutto ciò che non può presentare un certificato smette di funzionare: un monitor di uptime, un webhook di un provider di pagamenti, un lettore RSS o un'app mobile senza un certificate store che puoi raggiungere. Decidi come gestire questi casi prima di impostare ssl_verify_client on, perché il malfunzionamento è totale e, dal loro lato, silenzioso.

Leggi ciò che segnala il client quando viene rifiutato

Il messaggio visualizzato da un client rifiutato dipende dal browser, dalla versione di curl e dalla libreria TLS sottostante. Leggi quindi ciò che stampa il tuo client, invece di confrontarlo con un messaggio riportato altrove. Il dettaglio utile si trova sul server.

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

Un certificato rifiutato lascia una riga contenente client SSL certificate verify error, seguita dal motivo indicato da OpenSSL. È questo il dato su cui intervenire. Di solito la causa rientra in uno di pochi casi. Il certificato proviene da una CA diversa da quella indicata nel file nominato in ssl_client_certificate. Il certificato non rientra nel periodo di validità. La CRL sul server ha superato nextUpdate e ora rifiuta tutti i client, non soltanto uno.

Se il browser non propone mai alcun certificato, il problema si verifica prima della fase di verifica. Durante l'handshake nginx invia i nomi degli emittenti accettabili. Il browser non ha trovato nel proprio archivio alcun certificato corrispondente e quindi non ha potuto proporne uno. Importa nuovamente .p12 nel profilo con cui stai effettivamente navigando.

È utile considerare anche un altro caso. Se hai eseguito il test con un certificato client self-signed isolato, invece che con un certificato firmato dalla tua CA, la verifica non può riuscire. nginx controlla infatti la firma rispetto al file della CA, mentre un certificato self-signed non è incluso in quel file. La procedura per creare il certificato è la stessa descritta in generare un certificato self-signed su Ubuntu. mTLS richiede soltanto il passaggio aggiuntivo in cui la tua CA lo firma.

FAQ

Devo ancora usare un certificato Let's Encrypt se uso mTLS?

Sì. I due certificati non sono correlati. Il server presenta il proprio certificato affinché il browser consideri attendibile il nome host; questo certificato deve comunque provenire da una CA già riconosciuta dal browser. La CA client è una catena privata separata, usata solo per verificare chi si sta connettendo. L'impostazione ssl_client_certificate non modifica il certificato presentato da nginx e non deve puntare alla catena Let's Encrypt.

Perché il browser non mi chiede mai di scegliere un certificato?

Durante l'handshake nginx invia un elenco degli emittenti accettabili, ricavato dal file indicato in ssl_client_certificate. Il browser propone solo i certificati il cui emittente compare in quell'elenco. Se non viene mostrata alcuna richiesta, nel browser non è presente un certificato della CA: l'importazione è stata eseguita in un profilo diverso oppure il certificato è stato firmato da una CA diversa da quella installata sul server. Esegui openssl s_client -connect admin.example.com:443 e nell'output cerca i nomi delle CA dei certificati client accettabili, per verificare quale CA richiede effettivamente il server.

Posso richiedere un certificato client solo per un URL?

Non con ssl_verify_client on all'interno di un location. Il certificato viene scambiato durante l'handshake, prima che nginx conosca il percorso della richiesta, e la rinegoziazione necessaria per aggirare il problema non è più disponibile in TLS 1.3 ed è vietata in HTTP/2. Imposta ssl_verify_client optional; nel blocco server, quindi in ogni location protetta verifica $ssl_client_verify e restituisci 403 quando non è SUCCESS.

Come posso revocare l'accesso di una persona?

Revoca quel certificato con openssl ca -revoke, rigenera l'elenco con openssl ca -gencrl, copialo sul server e ricarica nginx affinché legga il nuovo file. Gli altri utenti non vengono interessati. Questo funziona solo se ogni persona possiede il proprio certificato e non ne viene utilizzato uno condiviso. Controlla la data nextUpdate della CRL, perché una CRL scaduta fa fallire la verifica per tutti i client, non solo per quelli revocati.

mTLS sostituisce una pagina di login?

Per l'accesso al servizio, sì: senza un certificato nessuna richiesta raggiunge l'applicazione, quindi non c'è alcun modulo da attaccare né alcuna password da indovinare. Per l'identità all'interno dell'applicazione, no. Un certificato dimostra che il chiamante possiede un file di chiavi; quindi un laptop rubato rappresenta un utente valido. Inoltra il CN al backend, mantieni gli account e i permessi già previsti dall'applicazione e considera il certificato come il controllo di accesso posto davanti a essi.

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