PowerShell / SecretManagement

PowerShell module to consistent usage of secrets through different extension vaults
MIT License
317 stars 46 forks source link

Feature Request: Support Service Account Scenarios #118

Open JustinGrote opened 3 years ago

JustinGrote commented 3 years ago

From: https://github.com/PowerShell/PowerShell/issues/11218

SecretManagement currently supports the following scenarios very well:

  1. Usage by an interactive user to retrieve credentials for cmdlets
  2. Usage in scripts run by an interactive user with pre-configured vault and/or secret names
  3. Scripts run in Task Scheduled under the existing user's context

However there are additional scenarios where the usage of SecretManagement is desired but not ideal:

  1. Running scripts as SYSTEM (you have to psexec to system and run the secretmanagement commands there)
  2. Running scripts as Managed Service Accounts which may not have DPAPI or a profile folder for the secretmanagement vault registry manifest

What needs to exist is a simpler way to register secret vaults for these scenarios, as it currently involves a lot of psexec or manual tasks. Register-SecretVault -ManifestPath switch may be a way to inject these files where they need to go, maybe further expanded with -User to pre-define the profile path based on the user.

The only aspect of SecretManagement that may need to change is the ability to specify a path to the SecretManagement vault "registry" file for all commands (or a command to set it and keep it in module scope context) or supply it as an InputObject.

Putting this out here for further discussion and ideas.

PaulHigin commented 3 years ago

Both SecretManagement and SecretStore currently only work with accounts having profiles, which determine the location and security of registry, configuration, and store files needed for the implementation. Consequently, SecretManagment and SecretStore won't work under some Windows managed accounts.

We thought about this for SecretStore and have a -Scope configuration option for 'CurrentUser' and 'AllUsers' (machine scope). However, only 'CurrentUser' scope is implemented for v1.0 release. It is clear we will need to do the same thing (add a -Scope configuration option) for SecretManagement, probably for v1.0+ as well.

At minimum we need to call this out in the documentation, and I'll include that for the upcoming GA releases. AFAIK this only affects Windows platforms.

But there is another issue related to https://github.com/PowerShell/PowerShell/issues/11218. SecretManagement is dependent on SecureString type and ConvertTo-SecureString, which relies on .NET, which in turn relies on Windows DPAPI. We need to think through the security implications for SecretManagement on Windows platforms for accounts where DPAPI is unavailable.

/cc @SydneyhSmith @SteveL-MSFT

iSazonov commented 3 years ago

In PowerShell repo we have an issue about implementing sudo. I came to the conclusion that this can only be implemented through PowerShell remoting (by run and connecting to another PowerShell process under target user account). Perhaps the approach could be used for the scenario too.

JAK1047 commented 3 years ago

I'll give my two cents following from the other issue the main benefits of DPAPI & ConvertTo-SecureString\ConvertFrom-SecureString are:

  1. Convenience. No additional module or setup needed on a bare metal server. Having to grab just the SecretManagement module from the gallery is minimal, but further prerequisites would make this look less appealing.
  2. Security. The fact DPAPI will encrypt uniquely to the account running the cmd means we can secure a password to a single service account without needing to keep a salt or key accessible opening up security gaps. This is the main thing that's kept us from using other cryptographic options so any solution I feel should at the very least have an option to mimic this encrypting using some anchor of the account context itself.
JMyklebust commented 1 year ago

I'm questioning the assertion that DPAPI is not available for Managed Service Accounts?

Here is a simple test setup using PsExec from Systinternals (executed with local admin rights). If you create a secure string and turn it into a string under the gMSA user, you cannot decrypt it from either your normal user, nor from SYSTEM. I.e. only the gMSA user can turn that string version back into a SecureString, and then decrypt the content.

# Run ISE as a gMSA user
PsExec64.exe -d -i -u <DOMAIN>\<GMSAUSER>$ -p ~ powershell_ise.exe

# Run ISE as SYSTEM
PsExec64.exe -d -i -s powershell_ise.exe

# Create a securestring object
$SecureStringObject = ConvertTo-SecureString -String "MyTestString" -AsPlainText -Force
# Convert securestring to a text string
$SecureStringObject | ConvertFrom-SecureString

# Add the text string of the securestring into a variable (try this in different user context)
$PlaintextSecureString = "<InsertPlaintextSecureStringHere>"
# Convert plainstring back to a SecureString
# This will fail if not done in the right context because DPAPI cannot decrypt it
$ConvertedBackToSecureString = ($PlaintextSecureString | ConvertTo-SecureString)

# To verify after recreating the secure string, we can use a PSCredentialObject to read back the original text from the secure string
[pscredential]::new("NoUser",$ConvertedBackToSecureString).GetNetworkCredential().Password

In my experience gMSA accounts can also have Profile directories created, though I have not properly tested what actually triggers that.

Ofcourse the issue is still that you need to piggyback on PsExec for this....

JAK1047 commented 1 year ago

I'm questioning the assertion that DPAPI is not available for Managed Service Accounts?

Here is a simple test setup using PsExec from Systinternals (executed with local admin rights). If you create a secure string and turn it into a string under the gMSA user, you cannot decrypt it from either your normal user, nor from SYSTEM. I.e. only the gMSA user can turn that string version back into a SecureString, and then decrypt the content.

# Run ISE as a gMSA user
PsExec64.exe -d -i -u <DOMAIN>\<GMSAUSER>$ -p ~ powershell_ise.exe

# Run ISE as SYSTEM
PsExec64.exe -d -i -s powershell_ise.exe

# Create a securestring object
$SecureStringObject = ConvertTo-SecureString -String "MyTestString" -AsPlainText -Force
# Convert securestring to a text string
$SecureStringObject | ConvertFrom-SecureString

# Add the text string of the securestring into a variable (try this in different user context)
$PlaintextSecureString = "<InsertPlaintextSecureStringHere>"
# Convert plainstring back to a SecureString
# This will fail if not done in the right context because DPAPI cannot decrypt it
$ConvertedBackToSecureString = ($PlaintextSecureString | ConvertTo-SecureString)

# To verify after recreating the secure string, we can use a PSCredentialObject to read back the original text from the secure string
[pscredential]::new("NoUser",$ConvertedBackToSecureString).GetNetworkCredential().Password

In my experience gMSA accounts can also have Profile directories created, though I have not properly tested what actually triggers that.

Ofcourse the issue is still that you need to piggyback on PsExec for this....

So we use DPAPI with gMSAs all the time. We encode passwords to each gMSA we have on a box and save them in an Azure Key Vault. I can confirm the resultant password can only be converted back to a string back the gMSA that initially converted it ON the server it was converted on which matches DPAPI's specifications. We just have each gMSA launch a Powershell script in its own context though instead of PSExec

#Create script to be run by the scheduled task using Here string
$Command = @'
    #Get the gSMA username to be used in the Vault entry
    $User = $(([Security.Principal.WindowsIdentity]::GetCurrent()).Name.Replace("$ENV:USERDOMAIN\",'') -replace '\$')

    #Connect to Azure with VM managed idenity
    Connect-AzAccount -Identity

    #Enter the password to encypt here
    $Password = 'Password Goes Here'

    #Convert it to a Secure String, then From the Secure string to use DPAPI, then back to a Secure String as that's what Set-AzKeyVaultSecret needs
    $SecurePassword = ConvertTo-SecureString -String $Password -AsPlainText -Force
    $EncryptedPassword = ConvertFrom-SecureString -SecureString $SecurePassword
    $EncryptedSecurePassword = ConvertTo-SecureString -String $EncryptedPassword -AsPlainText -Force

    #Add to the key vault
    Set-AzKeyVaultSecret -VaultName 'ACEAutomation' -Name "Password1-$env:COMPUTERNAME-$User" -SecretValue $EncryptedSecurePassword
'@

#Dump the temp Powershell script to file
$Command | Out-File -FilePath C:\Update.ps1

#Make a scheduled task action for it
$Action = New-ScheduledTaskAction -Execute 'Pwsh.exe' -Argument '-File C:\Update.ps1'

#All gMSAs on the box
$GMSAArray = 'GMSA1$',         
'GMSA2$',      
'GMSA3$'   

#Make a scheduled task for each gMSA running the script as them, then remove the task
ForEach ($GSMA in $GMSAArray) {
    $Principal = New-ScheduledTaskPrincipal -UserID DomainName\$GSMA -LogonType Password

    $null = Register-ScheduledTask -TaskName 'Update GMSA' -Action $Action -Principal $Principal
    $null = Start-ScheduledTask -TaskName 'Update GMSA' 
    While ((Get-ScheduledTask -TaskName 'Update GMSA').State -ne 'Ready') { Start-Sleep -Seconds 1 }
    Unregister-ScheduledTask -TaskName 'Update GMSA' -Confirm:$False

    "Updating $GSMA"
}

#Cleanup the temp script file
Remove-Item -Path C:\Update.ps1 -Force