Active Directory Managed Service Accounts – sMSA, gMSA e dMSA
Oggi vi racconto la storia dei grandi emarginati da Active Directory che, dopo tanti anni di ombra ed oblio, sono finalmente tornati alla ribalta da grandi protagonisti. Questa è la storia di oggetti tanto fondamentali in ambito security, tanto semplici da implementare ed inspiegabilmente tanto ignorati dagli addetti ai lavori. Questa è la storia del riscatto: oggi parliamo dei Managed Service Account.
Il primo embrione di Managed Service Account (Standalone MSA – sMSA) venne introdotto con Windows Server 2008 R2. Aveva però il grosso vincolo di essere usato su un solo host specifico quindi era ideale per IIS app pool ma inutilizzabile su cluster e/o bilanciatori (rapporto uno a uno – un server, un sMSA)
Per sopperire a questo limite, con Windows Server 2012 vennero introdotti i Group Managed Service Account (gMSA) che avevano la capacità di essere usati simultaneamente su più hosts e legati a gruppi di sicurezza. Il requisito valido tutt’ora, era un dominio ed un forest functional level Windows Server 2012 o successivo. Forse, proprio per questo motivo, non attecchirono subito fra i sistemisti: era un periodo in cui le aziende erano ancora alle prese con Windows 2000 functional level e la fobia del Raise functional level.
Con l’uscita di Windows Server 2025 è stato introdotto una terza tipologia: i Delegated Managed Service Accounts (dMSA). Al momento della redazione di questo articolo sono ancora in preview ma rispetto ai gMSA, offrono la possibilità di upgradare un utente standard di servizio ad un dMSA gestendo le credenziali in AD e disabilitando al tempo stesso le credenziali originali. Non è possibile “migrare” i gMSA in dMSA.
Ne parlerò in un articolo apposito e appena usciranno in General Available ed appena avrò l’opportunità di giocarci.
Delegated Managed Service Accounts overview in Windows Server 2025 (Preview) | Microsoft Learn
Per automatizzare i processi in background, lanciare servizi, task ed applicativi abbiamo per anni utilizzato questi accorgimenti:
- Fornito gli accessi minimi richiesti (least privileged access)
- Creato Service Account da zero (mai copiarli)
- Evitato l’inserimento dei Service Account fra i built-in groups
- Rimosso di diritti non necessari
(deny logon locally, as a batch job, as a service, from network) - Settato vincoli di accessi con “Log On To” ed assegnato limiti temporali
- Password complesse e segrete
Bene: password complesse (forse), segrete (si spera) ma che, nel giro di poco tempo, anche i muri la sapevano.
I punti che vi ho appena elencato sono i punti del bravo sistemista. Ora vi elenco velocemente cosa capita di vedere in giro per le aziende e che non consiglio di seguire:
- Account personali che lanciano task
- Account domain admins che eseguono Servizi
- Account di servizio con P@ssw0rd siccome, con lo zero, P maiuscola ed il carattere speciale siamo al sicuro siccome siamo compliant con la Default Domain Policy.
Per evitare le cattive abitudini sopra citate, l’utilizzo di account sensibili nell’esecuzione jobs, che le credenziali siano digitate fisicamente, che siano “tramandate” ed intercettate, ci vengono in aiuto i Managed Service Account MSA con questa serie di caratteristiche che li rendono unici:
- Account speciale con cui possiamo lanciare servizi, applicazioni e Scheduled Task
- Random password completamente gestita da Active Directory
- Generata automaticamente, 240 byte e cambiata ogni 30 giorni, “impossibile” da decifrare
- Impossibilità di Interactive logon
- Autenticazione solo by Kerberos – no NTLM
- Password sconosciuta e NON rimane in memoria
(no escrow password da processi LSASS attraverso mimikatz e/o strumenti simili)
Ora, visto che siamo a ridosso del 2025, e probabilmente il FFL 2012 l’avete applicato già da diversi anni (era il requisito), direi che sia il caso di provarli siccome la facilità implementativa e l’incremento in ambito security che portano è davvero elevato.
Procediamo!
1 – Creazione della Key Distribution Service (KDS) root Key (attività lato ADDS)
Per prima cosa andiamo in Active Directory con diritti amministrativi per generare il KDS. La root key viene creata una sola volta e viene utilizzata per generare le password dei MSA in Windows Server. La root key del servizio KDS viene archiviata in Active Directory nel contenitore CN=Master Root Keys,CN=Group Key Distribution Service,CN=Services,CN=Configuration,DC=<forest name>.
I controller di dominio aspetteranno fino a 10 ore dal momento della creazione per permettere a tutti i controller di dominio di far convergere le repliche di Active Directory, prima di consentire la creazione di un account gMSA. L’intervallo di 10 ore è una misura di sicurezza che impedisce la generazione di password prima che tutti i controller di dominio nell’ambiente siano in grado di rispondere alle richieste di account del servizio gestito di gruppo.
Per creare e consentire la propagazione immediata delle chiavi a tutti i controller di dominio prima dell’uso, è possibile utilizzare il seguente comando da lanciare via Powershell:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
# Creazione KdsRootkey con effetto immediate Add-KdsRootKey -EffectiveImmediately # Oppure # Creazione KdsRootkey con effetto immediato Add-KdsRootKey -EffectiveTime ((Get-Date).AddHours(-10)) # Per verificare la KDSrootKey lanciare il seguente comando: # Verifica kdsRootKey Get-KdsRootKey # Per testare la KeyID lanciare questo comando # Test KeyId Test-KdsRootKey -KeyId (Get-KdsRootKey).KeyId |
Figure 1 – gMSA Add-KdsRootKey
2 – Creazione gMSA (attività lato ADDS)
Ora che in AD abbiamo il nostro KDS disponibile e funzionante, siamo pronti per creare il nostro gMSA (il primo di tanti spero) che potremo vincolare a un singolo server, ad un set di servers definiti oppure ad un gruppo di sicurezza (che a sua volta conterrà soltanto i server che potranno leggere la password del gMSA).
Per comodità suggerisco di adottare una naming per il gMSA e lo stesso per i gruppi di sicurezza legati al gMSA in modo da individuarli facilmente nel caso ne abbiate diversi. In questo caso specifico ho creato un gruppo security “SQLservers” dove andrò ad inserire gli account computer sei server che utilizzeranno il gMSA per i servizi SQL.
1 2 3 4 5 6 7 8 9 10 |
# Creazione gruppo da assegnare al gMSA New-ADGroup SQLservers -path 'OU=Service Groups,OU=Demo,DC=demo,DC=lab' -GroupScope Global -PassThru -Description 'Gruppo legato ai gMSA’ Add-AdGroupMember -Identity SQLservers -Members SRV$, FS1$ # Ora che il gruppo è pronto, creo il gMSA e lo vincolo al gruppo di sicurezza SQLservers: # Creazione del gMSA New-ADServiceAccount -name gMSA-SQL -DNSHostName gMSA-SQL.demo.lab -PrincipalsAllowedToRetrieveManagedPassword SQLservers -Description 'gMSA per tutti i server SQL' # Oppure, nel caso non abbia un gruppo apposito, inserisco singolarmente gli account computers: New-ADServiceAccount -name gMSA-SQL -DNSHostName gMSA-SQL.demo.lab -PrincipalsAllowedToRetrieveManagedPassword SQL1$, SQL2$, SQL3$ -Description 'gMSA per tutti i server SQL' |
Figure 2 – gMSA New-ADServiceAccount
N.B.: non inserire mai e poi mai (HO GIA’ DETTO MAI???) il gruppo “Domain Computers” fra i PrincipalsAllowedToRetrieveManagedPassword.
Capisco che sulle prime verrebbe da dire: che sbattimento questi gruppi, meglio se permetto a tutti i domain computers di leggersi le gMSA password, sai che figata?
ERRORE EPICO in ambito di sicurezza!!!
Se permettessimo a tutti i computers di leggersi le gMSA password, daremo la possibilità a qualsiasi macchina compromessa di effettuare un lateral movement ovunque quindi tutto questo castello crollerebbe (pure il mio lavoro).
Per intenderci:
Buongiorno Dott. Ladro, in casa ho 20 porte superblindate impossibili da scassinare.
Ahh, hai trovato la chiave del capanno degli attrezzi in giardino sotto lo zerbino? Beh vabbeh, puoi entrare solo lì e mal che vada prendi solo il decespugliatore.
Dici di NO? Con quella hai aperto anche tutte le altre di casa ed hai rubato tutto?
Uhh ma che sfortuna, ma come hai fatto??? Incredibile, con tutta la sicurezza che ho implementato!!!
Come hai detto? Il fabbro ha creato tutte le serrature identiche???
Questi comandi possono esservi utili nell’interrogazione gMSA:
1 2 3 4 5 6 7 |
# Questi comandi possono esservi utili nell’interrogazione gMSA: # Verifiche proprietà account come encryption type and SAM account name (Default encryption RC4, AES128 e AES256) Get-ADServiceAccount gMSA-SQL -Properties * | FL DNSHostName,KerberosEncryptionType,SamAccountName # Verifiche ultimo cambio pw Get-ADServiceAccount -Identity gMSA-SQL -Property PasswordLastSet Get-ADServiceAccount -Identity gMSA-SQL -Properties * |
Figure 3 – gMSA PasswordLastSet
A questo punto la preparazione “lato Active Directory” del nostro primo gMSA è pronta quindi passiamo “lato client” (che sia un server o una workstation non cambia il risultato) per verificare l’effettivo utilizzo.
3 – Installazione gMSA (attività lato host)
Per poter leggere e fruire dei servizi gMSA è un requisito fondamentale installare il modulo Active Directory Powershell attraverso il seguente comando:
1 2 3 4 5 6 7 8 9 |
# Attività Target devices Add-WindowsFeature RSAT-AD-PowerShell # requisito fondamentale (no Active Directory Powershell – no gMSA) # Montato il modulo PS possiamo “installare” il gMSA (lato pratico stiamo permettendo alla macchina di poter leggere la password cifrata scritta in Active Directory dalla macchina su cui lanciamo il comando) Install-ADServiceAccount -Identity gMSA-SQL # E testare l’effettivo funzionamento: Test-ADServiceAccount gMSA-SQL # deve restituire valore true |
Figure 4 – gMSA Install-ADServiceAccount
Graficamente potete trovare tutti i gMSA creati all’interno di Active Directory Users and Computer mentre, all’interno di Active Directory Site and Services, le Master Keys:
Figure 5 – gMSA list
Figure 6 – gMSA Master Keys
OK, il test funziona ma poi? E’ arrivato il momento di utilizzarlo e cambiare un servizio che gira con uno standard account con il gMSA appena creato.
4 – Utilizzo del gMSA (attività lato host)
I gMSA possono esser utilizzarli per esecuzione servizi, task, jobs, accesso a share NTFS e tanto altro ancora. Quello che va ricordato è che si tratta a tutti gli effetti di oggetti AD account computer “speciali” (hanno qualche attributo in più) e che quindi andranno assegnati tutti i diritti necessari per compiere le attività. Gli esempi che riporto a seguire sono relativi al cambio service account e scheduled job.
N.B.: vi siete stancati di leggere? Quello che seguirà è stato mostrato durante l’evento #POWERCON2024 del 14 giugno 2024 e a questo url il video relativo https://www.youtube.com/watch?v=lThy1GDO0Fo
Cambio Service Account standard user in gMSA
L’obiettivo è quello di sostituire lo standard user in favore del gMSA per il lancio del servizio. Certo, qui come esempio ho preso SQL ma la procedura vale per tutti i servizi windows su cui abbiamo service_SQL che è un utente standard da sostituire. La classica situazione che troviamo è questa:
Figure 7 – std user to gMSA
In genere preferisco fare questo cambio via Powershell ma vi assicuro che funziona ugualmente modificando manualmente il servizio e riavviandolo. Quello che faccio a seguire è modificare solo i servizi che trovate in grassetto in base ai nomi scelti per il cambio nella parte ## Edit Service account by PowerShell.
Successivamente, a seconda di cosa devo sostituire, eseguirò la parte # Change logon to gMSA oppure # Change logon to std user
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
# Edit Service account by PowerShell $serviceName = 'MSSQL$SQLEXPRESS' $Service = Get-WmiObject -Class Win32_Service -Filter "Name='$serviceName'" $gMSA = $env:userdomain + "\" + (Get-ADServiceAccount -Identity 'gMSA-SQL').samaccountname $Account = $env:userdomain + "\" + (Get-ADUser -Identity 'service_SQL').samaccountname # Change logon to gMSA $Service.Change($null,$null,$null,$null,$null,$null,$gMSA,$null,$null,$null,$null) Set-Service -Name $ServiceName -StartupType Automatic | Restart-Service # Check service Get-CIMInstance -Class Win32_Service -Filter "Name='$serviceName'" | Select-Object Name,StartMode,Started,StartName,Status # Change logon to std user $Service.Change($null,$null,$null,$null,$null,$null,$Account,$null,$null,$null,$null) Set-Service -Name $ServiceName -StartupType Automatic | Restart-Service # Check service Get-CIMInstance -Class Win32_Service -Filter "Name='$serviceName'" | Select-Object Name,StartMode,Started,StartName,Status |
Dopo aver lanciato gli script sopra riportati oppure editato manualmente l’esecuzione del servizio, ci troveremo un servizio SQL lanciato da un gMSA di cui le credenziali le conosce solo AD e che sono fruibili solo dai device che abbiamo definito poco fa
Figure 8 – std user to gMSA
Cambio Scheduled Task standard user in gMSA
L’obiettivo è quello di sostituire lo standard user in favore del gMSA per l’esecuzione di task. In questo esempio riporto il classico Service_backup user che gozzoviglia tutte le notti a salvare dati dell’azienda con l’aggravante che, per tutti i server è amministratore essendo quasi sempre (purtroppo) un Domain Admin. La classica situazione che troviamo è questa:
Figure 9 – Task to gMSA
Siccome si tratta di un ambito diverso da SQL ho creato un apposito gMSA da utilizzare per tutte le macchine che necessitano Task Schedulati di nome gMSA-Jobs.
Anche per questo caso preferisco il cambio via Powershell ma vi assicuro che funziona ugualmente modificando manualmente il job e riavviandolo. Quello che faccio a seguire è modificare solo il nome del gMSA prescelto che trovate in grassetto indicato per il cambio nella parte # Change scheduled task in gMSA. Mi raccomando, essendo il gMSA un oggetto computer, necessita del “$” dopo il nome.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
## Check task properties $Result = @() $Computer = $(Get-WmiObject Win32_Computersystem).name Write-Host "Getting task list from $Computer" -ForegroundColor Green $Result += schtasks.exe /query /s $Computer /V /FO CSV | ConvertFrom-Csv | Where { $_.HostName -eq $computer -and $_.Author -ne "N/A" -and $_.'Run As User' -ne "N/A" -and $_.'Next Run Time' -ne "N/A" -and $_.Author -notmatch "Microsoft" -and $_.TaskName -notmatch "User_Feed_Synchronization" -and $_.'Scheduled Task State' -ne "Disabled" } $Result | select -Property HostName,TaskName,'Run As User','Last Result',Author,'Last Run Time','Next Run Time','Task To Run' # | Export-Csv $WorkingDir\$ExportFile -NoTypeInformation -Force -Append # Change scheduled task in gMSA schtasks /Change /TN Service_backup /RU "DEMO\gMSA-Jobs$" /RP "" #switch from std user to gMSA # Change scheduled task in std user # Compilare lo switch /RP "Con1Password" schtasks /Change /TN Service_backup /RU "DEMO\Service_backup" /RP "" #switch from gMSA to std user |
Figure 10 – Task to gMSA
Vi faccio notare che, durante il cambio Powershell si lamenta con un Warning riguardo la password Emty: tutto normale visto che la password la conosce solo Active Directory ed il client.
N.B: per eseguire uno scheduled task con gMSA è necessario assegnare i permessi “Log on as a batch job” altrimenti non può funzionare.
Figure 11 – Log on as a batch job for gMSA
5 – ed ora??? Analisi
Wow, fantastico spiegone e adesso carico come una molla sostituisco tutti i miei Standar Users in gMSA, vado avanti come un treno e… spacco tutto!
Calma, prima di iniziare con i cambi in gMSA va necessariamente fatta un’analisi per avere un quadro completo che deve rispondere alle seguenti domane:
- I miei account di servizio stanno girando su quanti server? Su quali server?
- Qualche mio collega sta eseguendo batch su server con il suo admin account? Se SI, su quale server?
- Qualche sviluppatore ha cablato un account all’interno di un applicativo?
Se siete ricchi e felici potete affidarvi a soluzioni a pagamento tipo Configuration Manager di casa MS o similari di terze parti ma, se non avete nessuna soluzione di mngmt, dobbiamo affidarci al buon vecchio Power Shell lavorando come si dice dalle mie parti “a manoni”. Ciò vuol dire che possiamo ottenere facilmente una risposta alle prime due domande.
Riguardo la terza domanda l’unico modo è un’approfondita analisi log DC ma questo è un altro viaggio che va approfondito a parte.
Questi a seguire sono script di esempio che ci danno la “foto” della situazione attuale:
Local Administrators – PS Assessment
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
## Local Administrators – Power Shell Assessment $WorkingDir = '\\ESSECICCIEMME.demo.lab\ScheduledTaskExport$' $sourceOU = ‘OU=Servers,OU=Demo,DC=demo,DC=lab’ $ExportFile = "LocalAdministratorsList.csv.csv" $Result = @() ## Import the computer list From OU $ComputerListOU = (Get-ADComputer -SearchBase $sourceOU -Filter 'operatingsystem -like "*Windows server*" -and enabled -eq "true"').DNSHostName ForEach ($Computer in $ComputerListOU) { Write-Host "Getting Local Administrators list from $Computer" -ForegroundColor Green $Result += Invoke-Command -ComputerName $Computer -ScriptBlock {Get-LocalGroupMember -Name 'Administrators'} } $Result | select -Property PSComputerName,Name,ObjectClass,PrincipalSource | Export-Csv $WorkingDir\$ExportFile -NoTypeInformation -Force #-Append |
Questo è un esempio del risultato dell’assessment:
Figure 12 – Local Administrators Assessment
Run As Service – PS Assessment
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
## Run As Service – PowerShell Assessment $WorkingDir = '\\ESSECICCIEMME.demo.lab\ScheduledTaskExport$' $sourceOU = ‘OU=Servers,OU=Demo,DC=demo,DC=lab’ $sourceDC = ‘OU=Domain Controllers,DC=demo,DC=lab’ $ExportFile = "ServiceList.csv" $Result = @() ## Import the computer list From OU or DC $ComputerListOU = (Get-ADComputer -Filter * -SearchBase $sourceOU).DNSHostName $ComputerListDC = (Get-ADComputer -Filter * -SearchBase $sourceOU).DNSHostName ## Get Services excluding LocalSystem and NT Authority% ## ForEach ($Computer in $ComputerListOU) { Write-Host "Getting service list from $Computer" -ForegroundColor Green $Result += Get-CimInstance -ComputerName $Computer -Class Win32_Service -filter "StartName != 'LocalSystem' AND NOT StartName LIKE 'NT Authority%' " | ` Select-Object SystemName, StartName, State, Displayname, StartMode } $Result | Export-Csv $WorkingDir\$ExportFile -NoTypeInformation |
Questo è un esempio del risultato dell’assessment:
Figure 13- Run As Service Assessment
Scheduled Task – PS Assessment
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
## Scheduled Task – Power Shell Assessment $WorkingDir = '\\ESSECICCIEMME.demo.lab\ScheduledTaskExport$' $sourceOU = ‘OU=Servers,OU=Demo,DC=demo,DC=lab’ $ExportFile = "TasksList.csv" $Result = @() ## Import the computer list From OU $ComputerListOU = (Get-ADComputer -Filter * -SearchBase $sourceOU).DNSHostName ## Get Scheduled Tasks excluding Microsoft Scheduled Tasks ## ForEach ($Computer in $ComputerListOU) { Write-Host "Getting task list from $Computer" -ForegroundColor Green $Result += schtasks.exe /query /s $Computer /V /FO CSV | ` ConvertFrom-Csv | Where { $_.HostName -eq $computer -and $_.Author -ne "N/A" -and $_.'Run As User' -ne "N/A" -and $_.'Next Run Time' -ne "N/A" -and $_.Author -notmatch "Microsoft" -and $_.TaskName -notmatch "User_Feed_Synchronization" -and $_.'Scheduled Task State' -ne "Disabled" } } $Result | select -Property HostName,TaskName,'Run As User','Last Result','Author','Last Run Time','Next Run Time','Task To Run' | ` Export-Csv $WorkingDir\$ExportFile -NoTypeInformation -Force #-Append |
Questo è un esempio del risultato dell’assessment:
Figure 14 – Scheduled Task Assessment
Conclusioni
Come avete potuto vedere i nostri Managed Service Account son tornati protagonisti dopo anni di oblio. Dal mio personale punto di vista sono molto contento che le aziende stiano capendo ed apprezzando gli indubbi benefici che portano. Ciò è sicuramente dovuto all’aumento di “sensibilità” in ambito security, infatti, Microsoft ha esteso la loro funzionalità con Windows Server 2025 e sta finalmente iniziando a promuoverli come prima scelta durante i setup (vedi Entra Cloud Sync).
Figure 15 – gMSA Entra Cloud Sync
Sono orgoglioso di voi MSA, finalmente vi siete riscattati e questa storia ha un lieto fine