SQL Server e NTLM: quando Kerberos non trova il nome giusto

Nel precedente articolo abbiamo parlato di NTLM, Kerberos, IAKerb e LocalKDC, cioè del percorso con cui Microsoft sta cercando di ridurre sempre di più la dipendenza storica da NTLM negli ambienti Windows.

Sotto l’articolo è arrivato un commento molto interessante:

“… Utilizzando le hardening configurations di Defender avevo poco tempo fa usato le GPO “Network Security: Restrict NTLM – etc etc”, finendo per scoprire amaramente che i servizi SQL usano massicciamente NTLM, bloccando così ogni autenticazione dei client…”

Il commento centra perfettamente il problema. Perché NTLM, in molte infrastrutture, non è semplicemente un vecchio protocollo ancora abilitato. È spesso il paracadute invisibile che permette ad applicazioni, servizi, client e configurazioni non perfette di continuare a funzionare. Il caso SQL Server è uno dei più interessanti, perché mette insieme diversi elementi che negli ambienti Active Directory troviamo continuamente:

  • Windows Authentication
  • Kerberos
  • NTLM fallback
  • Service Principal Name
  • account di servizio
  • accessi tramite nome DNS
  • accessi tramite indirizzo IP
  • policy di hardening

E soprattutto mette davanti a una domanda molto pratica: SQL Server usa NTLM perché è il comportamento normale o perché Kerberos non è configurato correttamente?

La risposta breve è: dipende.

La risposta utile è: SQL Server, in un ambiente Active Directory, prova a usare Kerberos quando può. Se però il client non riesce a costruire o risolvere correttamente lo SPN del servizio SQL, Windows Authentication può ricadere su NTLM.

Quindi non sempre è “SQL Server che usa NTLM”. Molto spesso è Kerberos che non ha il nome giusto, sull’account giusto, nel posto giusto. Sì, anche qui il colpevole torna a essere un SPN. Active Directory riesce sempre a riportarci a casa.

Obiettivo del lab

In questo articolo costruiremo un lab per dimostrare quattro cose:

  1. SQL Server può funzionare correttamente con Kerberos.
  2. Lo stesso SQL Server può usare NTLM se il client si collega in un modo non compatibile con gli SPN configurati.
  3. Le policy “Restrict NTLM” possono bloccare le connessioni SQL che dipendono da NTLM.
  4. La soluzione non è “non bloccare mai NTLM”, ma capire quali percorsi dipendono da NTLM e correggerli.

Nel nostro lab vedremo questo comportamento:

tcp:SQL01.lab.local,1433 -> KERBEROS

tcp:sql-prod.lab.local,1433 -> KERBEROS

tcp:192.168.148.173,1433 -> NTLM

Dopo aver applicato il blocco NTLM in ingresso su SQL Server, la connessione via IP non riuscirà più a completare l’autenticazione, mentre la connessione via nome DNS continuerà a funzionare con Kerberos. Questo è il punto più importante dell’intero articolo: SQL Server non è incompatibile con il blocco di NTLM. Sono alcuni percorsi di connessione a dipendere da NTLM.

Ambiente di laboratorio

Il lab è composto da tre macchine:

Macchina

Ruolo

Nome

DC01

Domain Controller, DNS, Group Policy

DC01.lab.local

SQL01

SQL Server

SQL01.lab.local

VM01 / CL01

Client di test

VM01.lab.local

Il dominio utilizzato è:

LAB.LOCAL

Gli account principali sono:

LAB\svc-sql

LAB\sql.user

LAB\administrator

LAB\svc-sql è l’account di servizio utilizzato dal Database Engine di SQL Server.

LAB\sql.user è l’utente usato dal client per testare Windows Authentication.

LAB\administrator viene usato solo per attività amministrative, installazioni e configurazioni.

Configurazione iniziale di SQL Server

Durante l’installazione di SQL Server abbiamo configurato il Database Engine con un account di servizio dedicato:

SQL Server Database Engine: LAB\svc-sql

Startup Type: Automatic

Per il lab SQL Server Agent non è rilevante. Può rimanere disabilitato o configurato con un account built-in. La parte importante è che il Database Engine sia eseguito come LAB\svc-sql, perché il SPN MSSQLSvc dovrà essere associato proprio a quell’account.

Su SQL01 possiamo verificarlo con PowerShell:

Output atteso:

Figura 1 – Istanza SQL Server

Questo controllo è fondamentale. Se SQL Server è eseguito come LAB\svc-sql, gli SPN SQL devono essere registrati su LAB\svc-sql. Se invece SQL Server gira come un altro account, gli SPN devono essere registrati su quell’altro account. Sembra banale, ma è uno degli errori più frequenti: SPN corretto, ma account sbagliato. Kerberos, su queste cose, non perdona. È elegante, ma permaloso.

Abilitare TCP/IP e porta statica 1433

Per rendere il lab semplice e leggibile, configuriamo SQL Server sulla porta TCP 1433.

Da SQL Server Configuration Manager:

SQL Server Network Configuration > Protocols for MSSQLSERVER > TCP/IP = Enabled

Nelle proprietà di TCP/IP, nella sezione IPAll, abbiamo impostato:

TCP Dynamic Ports: vuoto

TCP Port: 1433

Dopo la modifica è necessario riavviare il servizio SQL Server:

Poi apriamo la porta sul firewall di SQL01:

Verifichiamo che SQL Server sia in ascolto:

Output atteso:

Figura 2 – Verifiche SQL Server

Se SQL Server non ascolta sulla porta 1433, ogni test successivo diventa inutile. Prima la rete, poi Kerberos. Anche se Kerberos ha sempre l’aria di voler entrare in scena prima.

Creare l’utente di test

Sul Domain Controller abbiamo creato un utente dedicato:

Verifica:

Figura 3 – Verifica utente

L’utente viene poi aggiunto a SQL Server come login Windows. Da SQL Server Management Studio, collegandosi localmente a SQL01 come amministratore eseguiamo:

Output atteso:

LAB\sql.user WINDOWS_LOGIN 0

Verifica degli SPN SQL

Sul Domain Controller abbiamo verificato gli SPN registrati sull’account LAB\svc-sql:

Nel nostro lab l’output mostra già:

Figura 4 – Verifica SPN

Cerchiamo eventuali SPN SQL:

E verifichiamo l’assenza di duplicati:

Output:

Figura 5 – Verifica SPN

Questa è una configurazione sana per la connessione a SQL01.lab.local. Gli SPN importanti sono:

MSSQLSvc/SQL01.lab.local:1433

MSSQLSvc/SQL01.lab.local

Il primo è quello rilevante per la connessione TCP sulla porta 1433. Il secondo può essere usato in altri scenari di connessione. La cosa importante è che siano registrati sull’account corretto, cioè LAB\svc-sql.

Primo test: connessione via nome DNS

Dal client VM01, autenticato come LAB\sql.user, apriamo SQL Server Management Studio e ci colleghiamo a:

tcp:SQL01.lab.local,1433

Configurazione:

Authentication: Windows Authentication

Encrypt: Mandatory

Trust Server Certificate: selezionato

In laboratorio selezioniamo “Trust Server Certificate” per evitare che il test Kerberos/NTLM si trasformi in un secondo articolo sui certificati TLS di SQL Server. Sarebbe interessante, ma non oggi.

Figura 6 – Connessione tramite SSMS

Una volta connessi, eseguiamo:

Output:

Figura 7 – Esecuzione query

Questo è il comportamento desiderato. Il client si collega al nome DNS SQL01.lab.local. Kerberos costruisce uno SPN coerente con il servizio SQL. Lo SPN è registrato sull’account corretto. SQL Server autentica il client con Kerberos.

Secondo test: connessione tramite alias DNS

Creiamo un alias DNS:

sql-prod.lab.local -> SQL01.lab.local

Dal Domain Controller:

Dal client:

Figura 8 – Flushdns e test di connettività

Provando a collegarci a:

tcp:sql-prod.lab.local,1433

Nel nostro lab la connessione continua ad usare Kerberos:

auth_scheme = KERBEROS

Figura 9 – Esecuzione query

Questo è un punto importante da sottolineare: non tutti gli alias DNS producono automaticamente fallback NTLM. A seconda del provider, del driver, del comportamento di canonicalizzazione del nome e della configurazione DNS/SPN, il client può riuscire comunque a ottenere un ticket Kerberos valido per il servizio reale.

Quindi l’alias DNS non va trattato come “sempre rotto”. Va verificato. Questa è una delle lezioni più importanti dell’articolo: non bisogna ragionare per supposizioni, ma guardare auth_scheme e capire bene che sta succedendo.

Terzo test: connessione tramite indirizzo IP

A questo punto dobbiamo verificare il caso più interessante: l’accesso tramite indirizzo IP. Dal client VM01, sempre come LAB\sql.user, abbiamo aperto SSMS e ci siamo collegati a:

tcp:192.168.148.173,1433

Eseguendo la stessa query:

Figura 10 – Esecuzione query

Questo è il punto centrale del lab. SQL Server funziona. Windows Authentication funziona. L’utente accede. Ma l’autenticazione non è Kerberos. È NTLM. Perché?

Perché il client si è collegato a un indirizzo IP, non a un nome DNS coerente con lo SPN del servizio.

Kerberos lavora con nomi di servizio. In questo caso il client non sta chiedendo un ticket per MSSQLSvc/SQL01.lab.local:1433, ma finisce in un percorso in cui l’identità del servizio non viene risolta come ci aspettiamo.

Risultato: fallback NTLM. E finché NTLM è consentito, nessuno se ne accorge. L’applicazione funziona. L’utente lavora.

Verifica lato client con klist

Dopo la connessione via IP, sul client possiamo eseguire:

Figura 11 – Elenco ticket kerberos

Questo conferma che, in quella sessione, non è stato ottenuto alcun ticket Kerberos per il servizio SQL. Se la connessione fosse stata Kerberos, avremmo visto un ticket per un servizio MSSQLSvc. Invece SQL Server stesso ci ha già detto la cosa più importante:

auth_scheme = NTLM

klist è il controllo di supporto. La prova applicativa più diretta è la DMV sys.dm_exec_connections.

Abilitare l’audit NTLM

Prima di bloccare NTLM, bisogna sempre fare audit. Su SQL01 abbiamo abilitato il log operativo NTLM:

Poi abbiamo configurato la policy:

Computer Configuration > Windows Settings > Security Settings > Local Policies > Security Options > Network security: Restrict NTLM: Audit Incoming NTLM Traffic

Valore:

Enable auditing for all accounts

Figura 12 – GPO

Dopo aver applicato la policy (su tutte le VM):

possiamo ripetere la connessione via IP da VM01 a SQL01. Sempre SQL01, possiamo leggere il log:

Qui è presente un evento molto chiaro:

Figura 13 – Eventi 4022

Quindi SQL Server ha accettato una connessione Windows Authentication usando NTLM. A questo punto non stiamo più “immaginando” una dipendenza da NTLM. La stiamo vedendo.

Applicare Restrict NTLM

Ora arriva la parte delicata. Creiamo una GPO e la linkiamo solo alla OU dove si trova SQL01, ad esempio:

OU=Servers,OU=SQL-Lab,DC=lab,DC=local

La policy configurata è:

Computer Configuration > Windows Settings > Security Settings > Local Policies > Security Options > Network security: Restrict NTLM: Incoming NTLM traffic

Valore:

Deny all domain accounts

Figura 14 – GPO

In laboratorio, se serve un test ancora più netto, si può usare:

Deny all accounts

ma in produzione questa impostazione va trattata con molta attenzione.

Dopo aver applicato la GPO con il comando:

possiamo anche verificare il valore effettivo della relativa chiave di registro:

Figura 15 – Verifica chiave di registro

Valori utili:

0x1 = Deny all domain accounts

0x2 = Deny all accounts

Questa verifica è importante perché, se il blocco NTLM non produce l’effetto atteso, bisogna prima capire se la policy è realmente arrivata su SQL01.

Test dopo il blocco NTLM: accesso via IP

Dopo il blocco NTLM abbiamo riprovato la connessione via IP:

tcp:192.168.148.173,1433

Risultato: la connessione non completa più il pre-login handshake e termina in timeout.

Il messaggio mostrato da SSMS è: Connection Timeout Expired.

The timeout period elapsed while attempting to consume the pre-login handshake acknowledgement. Oppure qualcosa del genere:

Figura 16 – Connessione fallita SSMS/NTLM

Questo messaggio non dice in modo elegante “NTLM è stato bloccato”.

Ma il contesto lo chiarisce:

  • prima, la stessa connessione via IP funzionava;
  • sys.dm_exec_connections mostrava auth_scheme = NTLM;
  • il log NTLM mostrava sqlservr, sql.user, NTLMv2, STATUS_SUCCESS;
  • dopo il blocco NTLM, la connessione via IP non riesce più;
  • la connessione via nome DNS continua invece a funzionare con Kerberos.

Test dopo il blocco NTLM: accesso via DNS

Dopo aver bloccato NTLM, verifichiamo nuovamente la connessione corretta:

tcp:SQL01.lab.local,1433

Usando la stessa query di prima otteniamo:

Figura 17 – Esecuzione query

Quindi SQL Server continua a funzionare. Il problema non è SQL Server. Il problema è il percorso di autenticazione usato dal client. Quando il client usa il nome DNS corretto, Kerberos trova il SPN e la connessione funziona. Quando il client usa l’indirizzo IP, Kerberos non riesce a costruire un’identità di servizio coerente e il sistema ricade su NTLM. Se NTLM viene bloccato, quel percorso smette di funzionare. Questo è esattamente il tipo di situazione che può emergere applicando hardening configuration o policy Restrict NTLM in ambienti reali.

Quindi: comportamento di default o misconfiguration?

Torniamo alla domanda iniziale. SQL Server “usa massicciamente NTLM” per comportamento di default?

La risposta più corretta è: SQL Server con Windows Authentication può usare Kerberos o NTLM. In ambiente Active Directory, Kerberos viene tentato quando le condizioni sono corrette. Se però il client non riesce a ottenere un token Kerberos valido, si usa NTLM.

Quindi, se SQL Server usa NTLM in modo massiccio, non bisogna concludere subito: SQL Server usa NTLM di default ma bisogna chiedersi: Perché Kerberos non viene usato?

Le cause più frequenti sono:

  • SPN mancante
  • SPN registrato sull’account sbagliato
  • SPN duplicato
  • servizio SQL eseguito con un account diverso da quello atteso
  • named instance con porta dinamica
  • alias DNS non coperto da SPN
  • client che si collegano tramite indirizzo IP
  • stringhe di connessione non coerenti
  • driver o applicazioni legacy
  • connessioni locali confuse con connessioni remote
  • uso di account locali o scenari non domain-based

Nel nostro lab il caso più evidente è l’accesso tramite indirizzo IP.

Lo stesso server SQL, con lo stesso utente e la stessa porta, usa Kerberos quando viene raggiunto tramite nome DNS e NTLM quando viene raggiunto tramite IP.

Come sistemare

La soluzione non è semplicemente “riabilitare NTLM”. Piuttosto capire quali connessioni dipendono da NTLM e correggerle dove possibile.

Nel caso SQL Server, il percorso tipico è:

  1. Identificare le connessioni che usano NTLM.
  2. Verificare il nome con cui i client raggiungono SQL Server.
  3. Verificare l’account di servizio del Database Engine.
  4. Verificare gli SPN MSSQLSvc.
  5. Verificare duplicati SPN.
  6. Stabilizzare le porte, soprattutto per named instance.
  7. Evitare connessioni via IP.
  8. Usare nomi DNS coerenti.
  9. Registrare gli SPN necessari.
  10. Testare auth_scheme = KERBEROS.
  11. Solo dopo applicare blocchi NTLM più restrittivi.

Per registrare uno SPN SQL Server sulla porta 1433:

Per il nome breve:

Per un alias applicativo, ad esempio sql-prod.lab.local:

Poi verificare:

setspn -S è preferibile perché controlla i duplicati prima di registrare il SPN. Questo è importante: gli SPN duplicati sono uno dei modi più rapidi per trasformare Kerberos in un’esperienza mistica.

Query utili per la diagnosi

La query principale è:

Per vedere tutte le connessioni:

Per filtrare le connessioni NTLM:

Per filtrare le connessioni Kerberos:

Queste query sono molto utili perché evitano di ragionare “a sensazione”. Con SQL Server possiamo vedere direttamente quale schema di autenticazione viene usato dalla sessione.

Comandi utili lato client

Svuotare la cache Kerberos:

Visualizzare i ticket:

Richiedere esplicitamente un ticket per SQL Server:

Testare la connettività TCP:

Testare la connettività verso IP:

Comandi utili lato server SQL

Verificare l’account del servizio SQL:

Verificare la porta in ascolto:

Abilitare il log operativo NTLM:

Leggere gli eventi NTLM:

Filtrare gli eventi relativi a SQL:

Verificare la policy NTLM applicata:

Checklist pratica prima di bloccare NTLM

Prima di applicare Deny all domain accounts o Deny all accounts, conviene seguire questa checklist:

  1. Abilitare audit NTLM.
  2. Identificare i server SQL coinvolti.
  3. Verificare auth_scheme sulle connessioni reali.
  4. Identificare client e applicazioni che arrivano in NTLM.
  5. Verificare se usano IP, alias o nomi non coerenti.
  6. Controllare gli SPN MSSQLSvc.
  7. Controllare l’account di servizio SQL.
  8. Cercare duplicati SPN.
  9. Stabilizzare porte statiche dove serve.
  10. Correggere le stringhe di connessione.
  11. Testare nuovamente Kerberos.
  12. Applicare il blocco NTLM prima su OU pilota.
  13. Documentare eventuali eccezioni temporanee.
  14. Rimuovere progressivamente le eccezioni.

Conclusioni

Tornando al commento che ha originato questo articolo: sì, il caso SQL Server merita assolutamente un seguito, perché è uno dei punti dove l’hardening di NTLM smette di essere teoria e diventa operatività pura. Nel nostro lab abbiamo visto che SQL Server non usa NTLM e basta. Con il nome DNS corretto e il SPN corretto, la connessione usa Kerberos. Con l’indirizzo IP, invece, la stessa istanza SQL viene raggiunta tramite un percorso che porta al fallback NTLM. Quando NTLM viene bloccato, quel percorso smette di funzionare.

Quindi il problema non è necessariamente un comportamento di default di SQL Server e non è sempre una misconfiguration unica e banale.

È spesso il risultato di più elementi:

  • come i client raggiungono SQL Server;
  • quali nomi sono usati nelle stringhe di connessione;
  • dove sono registrati gli SPN;
  • con quale account gira il servizio SQL;
  • se le porte sono statiche o dinamiche;
  • se esistono alias DNS o accessi tramite IP.

La correzione parte dall’osservazione. Prima si misura. Poi si corregge Kerberos. Solo dopo si blocca NTLM.

NTLM è pericoloso anche perché è comodo. Quando Kerberos non riesce a fare il suo lavoro, NTLM spesso arriva e rende tutto apparentemente funzionante. Questo è utile per la compatibilità, ma pessimo per la visibilità. Nel caso SQL Server, il rischio è pensare che tutto sia configurato correttamente solo perché i client riescono a connettersi. La domanda giusta non è: La connessione funziona? La domanda giusta è: Con quale protocollo si sta autenticando?

Qui abbiamo visto tre situazioni diverse:

tcp:SQL01.lab.local,1433 -> KERBEROS

tcp:sql-prod.lab.local,1433 -> KERBEROS

tcp:192.168.148.173,1433 -> NTLM

Poi abbiamo bloccato NTLM in ingresso su SQL01.

Risultato:

tcp:192.168.148.173,1433 -> fallisce

tcp:SQL01.lab.local,1433 -> continua a funzionare con KERBEROS

Questo dimostra che il blocco di NTLM non deve essere affrontato come un interruttore globale da abbassare alla cieca. Deve essere un percorso: audit, diagnosi, correzione, test, blocco progressivo.

NTLM non è sempre visibile. Kerberos non è magia. E gli SPN, purtroppo, continuano a essere quella piccola riga di configurazione che separa un hardening ben fatto da un pomeriggio passato a leggere errori SSPI con lo sguardo perso nel vuoto.

Stay tuned!