RDS per‑utente non rilasciate su Windows Server 2019: risolto blocco TCP 3389 dai CyberArk PSM ai RDSH

In un ambiente Windows Server 2019 con RDS per‑utente, gli utenti via CyberArk PSM non ricevevano le CAL e il Session Host sembrava limitato a due connessioni. La causa? Un blocco firewall in ingresso sulle nuove subnet RDSH: sbloccata la TCP 3389 dai PSM, le sessioni si sono stabilite e le User CAL sono state emesse subito.

Indice

Panoramica del problema

  • Infrastruttura Windows Server 2019 con licenze RDS per‑utente (User CAL) disponibili (20), ma il Session Host consente apparentemente solo due connessioni simultanee.
  • Gli utenti si connettono tramite due CyberArk PSM (Privileged Session Manager) che fungono da broker RDP. Da un PSM le sessioni funzionano, dall’altro no; gli utenti diretti (non‑PSM) ricevono regolarmente la CAL.
  • GPO, registro e versioni (Session Host, Licensing Server e CAL) sono correttamente configurati e allineati (tutti 2019 Datacenter).

Schema logico dell’ambiente

[Utente] ──RDP──> [CyberArk PSM #1] ──RDP:3389──> [RDSH A/B]
             └──> [CyberArk PSM #2] ──RDP:3389──> [RDSH A/B]
                                          │
                                          └──RPC (135 + porte dinamiche)──> [RDS License Server]
  

Il tranello: “due connessioni” non significa per forza un problema di licenza

Nel mondo RDS è frequente confondere il limite di due connessioni (tipico dell’accesso amministrativo) con un errore di licensing. In realtà, la negoziazione delle licenze entra in gioco dopo che la sessione RDP è stata instaurata a livello di rete e di protocollo. Se il percorso RDP è bloccato, la richiesta di licenza non parte nemmeno. Nel caso in esame, un PSM non completava il three‑way handshake TCP verso i RDSH: da qui il “sintomo” ingannevole.

Soluzione confermata e passi risolutivi

FaseVerifica / AzioneEsito
Controllo policy e registryConfermato ✔️: percorso GPO Remote Desktop Services → Licensing e chiavi LicensingMode impostate su per‑user; server di licenza specificato correttamente.Non risolve il sintomo.
Test di connettivitàDal PSM “non funzionante” fallisce la connessione TCP 3389 verso i server RDSH.Identifica un problema di rete.
Analisi dei log di reteI pacchetti SYN partono dal PSM, ma i pacchetti di ACK di ritorno non arrivano.Conferma un blocco inbound.
Audit del firewall internoManca una regola in ingresso per le nuove subnet che ospitano i RDSH.Root‑cause individuata.
CorrezioneAggiunta la regola firewall per permettere TCP 3389 dai PSM verso i RDSH.✔️ Connessioni RDP stabilite, le User CAL vengono rilasciate regolarmente da subito.

Riepilogo tecnico

La negoziazione delle licenze RDS avviene solo dopo l’instaurazione completa della sessione RDP. Il secondo PSM non riusciva a completare il three‑way handshake TCP con i Remote Desktop Session Host, quindi la richiesta di licenza non raggiungeva mai il server CAL. Una volta aperta la porta 3389/TCP sulle subnet interessate, la catena di comunicazione (PSM → RDSH ↔ License Server) è tornata stabile e il limite apparente di due sessioni è scomparso.


Procedura di troubleshooting completa (passo‑passo)

Verifiche di base su GPO e registro

Assicurati che i RDSH siano configurati per l’uso di licenze per‑utente e che conoscano il Licensing Server. I controlli tipici includono:

  • GPO: Computer Configuration → Policies → Administrative Templates → Windows Components → Remote Desktop Services → Remote Desktop Session Host → Licensing.
    • Abilita “Use the specified Remote Desktop license servers” con l’FQDN del Licensing Server.
    • Imposta “Set the Remote Desktop licensing mode” su Per User.
  • Registro (valori più comuni, possono variare in base alle policy):
    • HKLM\SOFTWARE\Policies\Microsoft\Windows NT\Terminal Services\LicensingModeDWORD (valore tipico 4 = Per‑User; 2 = Per‑Device).
    • HKLM\SOFTWARE\Policies\Microsoft\Windows NT\Terminal Services\LicenseServers<FQDN_LS> → presenza del Licensing Server.

Controlli PowerShell utili

# Su un RDSH
1) Stato porta RDP in ascolto
Get-NetTCPConnection -LocalPort 3389 -State Listen

2) Lettura rapida del valore LicensingMode (se esiste in Path Policies)

Get-ItemProperty -Path 'HKLM:\SOFTWARE\Policies\Microsoft\Windows NT\Terminal Services' `
-Name LicensingMode -ErrorAction SilentlyContinue

3) Elenco dei License Servers definiti via Policy (se presenti)

Get-ChildItem 'HKLM:\SOFTWARE\Policies\Microsoft\Windows NT\Terminal Services\LicenseServers' `
-ErrorAction SilentlyContinue | Select-Object PSChildName 

Suggerimento: il Diagnostico Remote Desktop Licensing Diagnoser (sui RDSH) individua velocemente mismatch di versione o insufficienza CAL. Trattandosi di User CAL, il sistema può non “bloccare” attivamente le connessioni, ma segnala l’errore: non fermarti al Diagnoser, integra sempre con test di rete.

Test di connettività end‑to‑end

La priorità è validare che il PSM “problematico” raggiunga i RDSH sulla 3389/TCP. Esegui test mirati dal PSM interessato:

# Dal PSM: test TCP puro verso ciascun RDSH
Test-NetConnection -ComputerName RDSH-A.contoso.local -Port 3389 -InformationLevel Detailed
Test-NetConnection -ComputerName RDSH-B.contoso.local -Port 3389 -InformationLevel Detailed

In alternativa con PsPing (Sysinternals) se disponibile

psping RDSH-A.contoso.local:3389

Se tnc fallisce dal PSM ma funziona da un altro host, il problema è sulla path del PSM.

Se i test falliscono, esegui un packet capture leggero per capire dove si ferma il flusso:

# Sul PSM o sul RDSH (a seconda di dove hai accesso)
Traccia di base con netsh (non invasiva)
netsh trace start capture=yes scenario=NetConnection tracefile=C:\Temp\rdp_trace.etl
Riproduci il problema, poi:
netsh trace stop

Nei casi di blocco inbound vedrai SYN → senza il corrispondente ← SYN/ACK di ritorno.

Audit del firewall e delle ACL

Il caso reale è stato risolto scoprendo che mancava una regola in ingresso per le nuove subnet RDSH. Due strategie efficaci:

  1. Regola mirata per sorgenti PSM: consenti TCP 3389 dai due PSM verso i RDSH.
  2. Regola per subnet autorizzate: consenti TCP 3389 dalle VLAN di salto/broker verso i RDSH. Più scalabile se aggiungi in futuro altri broker.

Esempi con Windows Defender Firewall (RDSH)

# Visualizza le regole RDP esistenti
netsh advfirewall firewall show rule name="Remote Desktop*" verbose

Crea una regola specifica per i PSM (sostituisci IP/Subnet)

netsh advfirewall firewall add rule name="ALLOWRDPFROM_PSM" dir=in action=allow protocol=TCP `
localport=3389 remoteip=10.50.20.15,10.50.20.16 profile=domain

Variante per intere subnet PSM

netsh advfirewall firewall add rule name="ALLOWRDPFROMPSMSUBNET" dir=in action=allow protocol=TCP `
localport=3389 remoteip=10.50.20.0/24 profile=domain 

Nota su UDP 3389: l’RDP moderno usa anche UDP 3389 per ottimizzare la qualità, ma non è necessario per la negoziazione delle licenze. Aprilo se desideri prestazioni migliori.

Validazioni post‑fix

  • Gli utenti via PSM stabiliscono la sessione senza errori e ricevono User CAL secondo il Diagnoser.
  • Sui RDSH non compaiono più errori critici; monitora gli ID 4105/4106 (TermService) e il log RemoteDesktopServices‑Licensing.
  • Il contatore delle licenze disponibili/assegnate riflette l’attività degli utenti.

Matrice rete e porte da conoscere

SorgenteDestinazionePorte/ProtocolliMotivo
CyberArk PSMRDSHTCP 3389 (necessaria), UDP 3389 (opzionale)Canale RDP principale. Senza 3389/TCP la sessione non nasce e la CAL non può essere emessa.
RDSHRDS License ServerTCP 135 + porte RPC dinamiche (in genere 49152‑65535)Comunicazione per la gestione delle licenze (richiesta/assegnazione/aggiornamento).
RDS License ServerAD DS (se in uso)LDAP/LDAPS, Kerberos, DNSRisoluzione e pubblicazione attributi di licenza nel dominio.

Pratica consigliata: se segmenti le reti, documenta le ACL in una tabella come questa e versionale in un repo interno. Gli errori “una porta dimenticata” sono la causa n.1 dei falsi problemi di licensing.

Runbook operativo (riutilizzabile)

  1. Conferma la modalità di licenza (Per‑User) su GPO/Registro e l’elenco dei License Server.
  2. Testa RDP 3389 dal PSM “non funzionante” verso tutti i RDSH (usa Test-NetConnection o psping).
  3. Se fallisce, cattura pacchetti o una netsh trace per verificare l’assenza di SYN/ACK.
  4. Correggi le ACL (firewall locale, firewall perimetrale, NGFW). Applica regole inbound dai PSM alle subnet RDSH su TCP 3389.
  5. Ritesta: apri sessioni multiple via entrambi i PSM e verifica l’emissione delle User CAL.
  6. Monitora eventi 4105/4106 e il log TerminalServices‑Licensing. Imposta alert su variazioni anomale.

Buone pratiche aggiuntive

  1. Verifiche di base: prima di concentrarti su RDS, assicurati che il traffico RDP sia consentito su tutta la path di rete e che non vi siano dispositivi NAT/firewall che alterano le porte.
  2. Licensing Diagnoser: usa lo strumento integrato per rilevare rapidamente mismatch di versione o CAL esaurite, ma integra sempre con test di rete (Telnet/PortQry/PsPing).
  3. CyberArk / PAM: quando si interpongono dispositivi di session brokering, testa sia la connettività broker → RDSH sia (se pertinente alla tua architettura) broker → License Server. Bastano poche porte bloccate per inibire l’emissione delle licenze.
  4. Monitoraggio continuo: registra gli eventi ID 4105/4106 (TermService) e gli errori nel log RemoteDesktopServices‑Licensing per rilevare tempestivamente problemi futuri.

Approfondimenti pratici

Per‑User vs Per‑Device: cosa cambia nel troubleshooting

AspettoPer‑User CALPer‑Device CAL
Enforcement sul RDSHSpesso non “bloccante” in tempo reale; il Diagnoser avvisa.Più rigoroso: i device possono trovarsi senza CAL valide.
Segnali tipiciAvvisi nel Diagnoser, ma la sessione nasce se la rete è ok.Rifiuto di nuove connessioni quando le CAL sono esaurite o non aggiornate.
Impatto del problema di reteBlocca la sessione prima della licenza → falso positivo licensing.Idem: la sessione non nasce, indipendentemente dalle CAL.

Eventi e log da tenere d’occhio

  • Applications and Services Logs → Microsoft → Windows → TerminalServices‑LocalSessionManager/Operational: creazione/chiusura sessioni.
  • … → TerminalServices‑RemoteConnectionManager/Operational: negoziazione connessione.
  • … → RemoteDesktopServices‑Licensing: esito delle richieste di licenza, errori di contatto con il Licensing Server.
  • System: eventuali drop del firewall, problemi di rete, driver.

Verifiche rapide lato RDSH

# 1) Porta RDP locale effettivamente in ascolto
(Get-NetTCPConnection -LocalPort 3389 -State Listen) -ne $null

2) Connettività verso il Licensing Server (RPC 135)

Test-NetConnection -ComputerName RDLS.contoso.local -Port 135

3) Health di base dei servizi RDS

Get-Service -Name TermService, UmRdpService | Format-Table -Auto 

Come evitare che il problema si ripresenti

  • Modello di regole “a gruppi”: crea un gruppo firewall “RDP‑Brokers” con gli IP dei PSM e referenzialo in un’unica regola. Quando aggiungi un nuovo PSM, aggiorni solo il gruppo.
  • Change management: ogni introduzione di nuove subnet RDSH deve prevedere uno step di apertura porte con check‑list firmata.
  • Test sintetici schedulati: uno script che ogni 5 minuti lancia Test-NetConnection dai PSM verso i RDSH e invia alert se fallisce (integra con il tuo sistema di monitoring).

FAQ

Perché vedevo solo due connessioni contemporanee?
Perché le sessioni via PSM non nascevano (blocco 3389/TCP) e le uniche connessioni operative erano quelle di amministrazione diretta, che per design sono limitate. Sbloccato il percorso PSM → RDSH, la capacità utente è tornata normale.

È obbligatorio che il PSM raggiunga il Licensing Server?
No: la catena standard è RDSH ↔ Licensing Server. Tuttavia, in architetture complesse o con controlli proxy/L7 tra PSM e RDSH, testare anche il percorso PSM → Licensing Server può aiutare a scovare regole errate o scenari non standard.

Devo aprire anche le porte RPC dinamiche dal PSM?
No. Le porte RPC dinamiche servono tra RDSH e Licensing Server. Dal PSM verso i RDSH basta la 3389/TCP (e facoltativamente 3389/UDP).

Come capisco se il problema è davvero di licensing e non di rete?
Se Test-NetConnection su 3389 fallisce dal PSM verso i RDSH, è prima di tutto un problema di rete. Se invece la 3389 è aperta ma i log RemoteDesktopServices‑Licensing mostrano errori di contatto con il Licensing Server o esaurimento CAL, allora indaga il licensing.

Checklist finale (da stampare)

  • ✔️ GPO/Registro coerenti: modalità Per‑User e License Server specificato.
  • ✔️ PSM → RDSH 3389/TCP raggiungibile da entrambi i PSM.
  • ✔️ RDSH → Licensing Server su 135/TCP + porte dinamiche abilitato (o range ristretto documentato).
  • ✔️ Log puliti: nessun 4105/4106 ricorrente; Diagnoser senza errori bloccanti.
  • ✔️ Monitor attivo con alert su latenza/porte chiuse e sugli eventi RDS.

Conclusioni

Quando le User CAL non “si rilasciano” e il Session Host “sembra” limitato a due sessioni, la tentazione è di scavare solo nel licensing. Questo caso dimostra l’opposto: se la porta RDP 3389/TCP è bloccata lungo la path (qui tra un CyberArk PSM e i RDSH), la negoziazione della licenza non parte e si cade in un falso allarme di CAL. La correzione è stata semplice e strutturale: una regola firewall inbound per consentire il traffico dai PSM alle nuove subnet RDSH. Da quel momento, le sessioni RDP si sono instaurate regolarmente e le licenze per‑utente sono state emesse senza ritardi.

Porta con te due messaggi: 1) rete prima di licensing; 2) standardizza le ACL e monitora costantemente. Ridurrai drasticamente i tempi di fermo e i falsi positivi.


Appendice: snippet utili

Script PowerShell di verifica con report

# Imposta host e License Server
$rdsh = @("RDSH-A.contoso.local","RDSH-B.contoso.local")
$psm  = @("PSM-01.contoso.local","PSM-02.contoso.local")
$ls   =  "RDLS.contoso.local"

Test 3389 dai PSM ai RDSH (richiede WinRM e credenziali adeguate)

$results = foreach($p in $psm){
foreach($h in $rdsh){
try{
Invoke-Command -ComputerName $p -ScriptBlock {
param($target)
$r = Test-NetConnection -ComputerName $target -Port 3389 -WarningAction SilentlyContinue
[pscustomobject]@{
From    = $env:COMPUTERNAME
To      = $r.ComputerName
Reach   = $r.TcpTestSucceeded
Latency = if($r.TcpTestSucceeded){$r.PingReplyDetails.RoundtripTime}else{$null}
}
} -ArgumentList $h -ErrorAction Stop
} catch {
[pscustomobject]@{ From=$p; To=$h; Reach=$false; Latency=$null }
}
}
}

$results | Sort-Object From,To | Format-Table -Auto

Test RPC 135 dal RDSH al License Server

$rdsh | ForEach-Object {
Test-NetConnection -ComputerName $ls -Port 135 |
Select-Object ComputerName, RemotePort, TcpTestSucceeded
} 

Regole firewall “modellate” per subnet

# Regola unica che consente RDP da più subnet PSM
netsh advfirewall firewall add rule name="ALLOWRDPFROMPSMVLANS" dir=in action=allow protocol=TCP `
    localport=3389 remoteip=10.50.20.0/24,10.60.30.0/24 profile=domain

Abilitare rapidamente i log RDP

# LocalSessionManager e RemoteConnectionManager (su RDSH)
wevtutil sl "Microsoft-Windows-TerminalServices-LocalSessionManager/Operational" /e:true
wevtutil sl "Microsoft-Windows-TerminalServices-RemoteConnectionManager/Operational" /e:true

Caso reale in una riga

Root‑cause: mancava una regola firewall in ingresso per le nuove subnet RDSH. Fix: sblocco TCP 3389 dai CyberArk PSM verso i RDSH. Effetto: sessioni ripristinate e User CAL rilasciate immediatamente.

Indice