In this blog I describe my experience experimenting with the Azure Key Vault service RBAC support for implementing access control on individual Secrets.
Problem Statement:
Today Key Vault does not provide fine grained access control for individual secrets. The access control is at the Key Vault level. All users that have read access to the Vault can read all the secrets. The work around today is to create multiple vaults for each user group and grant access at the vault level. Take the common scenario of secrets that must be shared to some users of group A and some users of group B? There is no good way to solve this today with KeyVault. This means creating yet another vault that includes only secrets that must be shared with the users from groups A and B and providing vault access to only those users. This creates an unmanageable situation with just a few of these use cases in play. Azure role-based access control for Key Vault (in preview as of 11/04/2020) brings access control to individual secrets within the same vault.
Azure Key Vault Basics:
Azure Key Vault is the Azure way of securely storing and accessing secrets of various types. This post is focused on secrets of type Secret.
Azure Key Vault is not a feature for feature replacement for an on-premise Vaulting application that provides a robust end user interface, for example Secret Server. The primary use case for Azure Key Vault is to be accessed programmatically by either Azure or on-premise applications.
The Azure Portal Kev Vault web interface provides an extremely basic capability to create individual Secrets.
- Does not provide any search or sort functions for Secret names or Secret Tag values.
- Does not provide a good way to view Secret Tag data.
- Does not provide a bulk secret import capability.
- Does not provide an unstructured text field for descriptive data for the secret.
Required PowerShell Modules:
Required .Net classes:
Solution:
Azure Role-Based Access Control for Key Vault Secrets
This Key Vault feature is currently in preview and your mileage may vary.
This will break existing access configured under the standard access policy.
This preview feature is not enabled by default.
Enable it using the Azure Portal. To enable this preview feature, select the desired key vault and click the Access policies link in the navigation pane. Then select the Permission model Azure role-based access control (preview).
Create the lab key vault:
Open a PowerShell session where you have installed all the required PowerShell modules and connect Use of DisableSoftDelte is just for lab work to avoid having to purge each secret as I experiment.
PS C:\Users\chris> New-AzKeyVault -Name rbac-lab-keyvault -ResourceGroupName $resourceGrp -Location $loc -DisableSoftDelete
Enable RBAC fine grained control for the lab key vault:
As far as I know this must be done through the portal.
Create a service principle that could be used by a PowerShell script and assign it privileges to manage the lab key vault:
PS C:\Users\chris> $labPsSp = New-AzADServicePrincipal -DisplayName LabPSSP
The service principle does not need the Contributor role for the subscription so remove that now.
$roleassignment = Get-AzRoleAssignment -ServicePrincipalName $labPsSp.ServicePrincipalNames[0]
PS C:\Users\chris> $roleassignment.Count
1
PS C:\Users\chris> Remove-AzRoleAssignment -InputObject $roleassignment
PS C:\Users\chris> $roleassignment = Get-AzRoleAssignment -ServicePrincipalName $labPsSp.ServicePrincipalNames[0]
PS C:\Users\chris> $roleassignment.Count
0
Now assign the Roles it will need to manage the vault and vault roles. It’s important at this point to understand the intended scope of the service principal and this role. Apply the role for just the rbac-lab-keyvault vault.
PS C:\Users\chris> $vaultScope = (Get-AzKeyVault -VaultName rbac-lab-keyvault).ResourceID
PS C:\Users\chris> New-AzRoleAssignment -Scope $vaultScope -ServicePrincipalName $labPsSp.ServicePrincipalNames[0] -RoleDefinitionName 'Key Vault Administrator (preview)'
You can see below that the Role is applied to the specified vault for the Service Principal “LabPsSP”.
PS C:\Users\chris> New-AzRoleAssignment -Scope $vaultScope -ServicePrincipalName $labPsSp.ServicePrincipalNames[0] -RoleDefinitionName ' User Access Administrator'
For lab use will use password auth with the service principal.
$bsstring = [System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($labPsSp.Secret)
$secret = [System.Runtime.InteropServices.Marshal]::PtrToStringAuto($bsstring)
$applicationId = $labPsSp.ApplicationId.Guid # use the $secret string for the password
PS C:\Users\chris> $cred = Get-Credential $applicationID
PS C:\Users\chris> $cred | Export-Clixml -path .\$labPsSP.cred
Create the Groups and assign users:
Create 4 users and assign them to 2 groups in Azure AD and 1 user in both.
Azure AD User Name | Azure AD Group Assignment |
TestUser1 | AppTeam |
TestUser2 | AppTeam |
TestUser3 | ServerTeam |
TestUser4 | ServerTeam |
TestUser5 | Both |
PS C:\Users\chris> Connect-AZureAD -TenantId $tennantId
PS C:\Users\chris> $testUsers = Get-AzADUser -SearchString "Test"
PS C:\Users\chris> $testUsers.DisplayName
TestUser1
TestUser2
TestUser3
TestUser4
TestUser5
PS C:\Users\chris> $groups = Get-AzADGroup
PS C:\Users\chris> $groups | Select-Object DisplayName | Where-Object {$_.DisplayName -Like "*Team"}
DisplayName
-----------
AppTeam
ServerTeam
PS C:\Users\chris>
PS C:\Users\chris> $groups | Where-Object {$_.DisplayName -Like "*Team"} | %{$_.DisplayName;(Get-AzADGroupMember -GroupObjectID $_.ObjectID).DisplayName}
AppTeam
TestUser1
TestUser2
ServerTeam
TestUser3
TestUser4
PS C:\Users\chris>
Create the secrets to experiment with the RBAC Secret controls:
Secret | SecretValue | Azure AD Group Assignment | Individual User Read Access |
Sec1 | Sec1Value | AppTeam | TestUser5 |
Sec2 | Sec2Value | AppTeam | TestUser5 |
Sec3 | Sec3Value | ServerTeam | TestUser5 |
Sec4 | Sec4Value | ServerTeam | TestUser5 |
Sec5 | Sec5Value | App and Server teams | none |
First switch to using the service principal LabPSSP and confirm access to the key vault.
PS C:\Users\chris> Disconnect-AzureAD
PS C:\Users\chris> Disconnect-AzAccount
PS C:\Users\chris> $cred = Import-Clixml -path .\labPsSP.cred
PS C:\Users\chris> Connect-AzAccount -ServicePrincipal -Credential $cred -TenantId $tennantID
PS C:\Users\chris> $vault = Get-AzKeyVault
PS C:\Users\chris> $vault.VaultName
rbac-lab-keyvault
PS C:\Users\chris>
Note that the tenant has multiple vaults but the service principal can only see rbac-lab-keyvault as expected.
Now add the secrets.
PS C:\Users\chris\exceltest> $objSplat = @{
>> VaultName = 'rbac-lab-keyvault'
>> Name = ''
>> ContentType = 'Password'
>> SecretValue = ''
>> }
PS C:\Users\chris>
PS C:\Users\chris> $objSplat.Name = 'Sec1'
PS C:\Users\chris> $objSplat.SecretValue = ConvertTo-SecureString 'Sec1Value' -AsPlainText -Force
PS C:\Users\chris> Set-AzKeyVaultSecret @objSplat
Repeat above to add the 5 test secrets.
…
PS C:\Users\chris> (Get-AzKeyVaultSecret -VaultName rbac-lab-keyvault).Name
Sec1
Sec2
Sec3
Sec4
Sec5
PS C:\Users\chris>
Now everything should be in place to assign RBAC to each Secret using the service principal. For each Secret assign the ‘ Key Vault Secrets User (preview)’ Role to the appropriate Group(s). The Scope for an individual secret is formed by the $vaultScope /secrets/$secret.Name.
PS C:\Users\chris> $role = ‘Key Vault Secrets User (preview)'
PS C:\Users\chris> $secrets = Get-AzKeyVaultSecret -VaultName rbac-lab-keyvault | Where-Object {$_.Name -Like "Sec*"}
PS C:\Users\chris> $secrets | Add-Member -TypeName NoteProperty -NotePropertyName 'Scope' -NotePropertyValue ''
PS C:\Users\chris> $secrets | %{$scope = "$vaultScope/secrets/$($_.Name)";$_.Scope = $scope}
Cannot connect using service principal unless create cert and deploy an ADservices application which adds cost so use a regular account that has access to your tenant AD here
PS C:\Users\chris> Disconnect-AZureAD
PS C:\Users\chris> Connect-AZureAD -TenantId $tennantId
PS C:\Users\chris> $appTeamID = (Get-AzADGroup -DisplayName 'AppTeam').ID
PS C:\Users\chris> $azroledef = Get-AzRoleDefinition -Name 'Key Vault Secrets User (preview)
PS C:\Users\chris> $roleAssignment = New-AzRoleAssignment -Scope $secrets[0].Scope -RoleDefinitionId $azroledef.Id -PrincipalId $appTeamID
PS C:\Users\chris> $roleAssignment = New-AzRoleAssignment -Scope $secrets[1].Scope -RoleDefinitionId $azroledef.Id -PrincipalId $appTeamID
PS C:\Users\chris> $serverTeamID = (Get-AzADGroup -DisplayName 'ServerTeam').ID
PS C:\Users\chris> $roleAssignment = New-AzRoleAssignment -Scope $secrets[2].Scope -RoleDefinitionId $azroledef.Id -PrincipalId $serverTeamID
PS C:\Users\chris> $roleAssignment = New-AzRoleAssignment -Scope $secrets[3].Scope -RoleDefinitionId $azroledef.Id -PrincipalId $serverTeamID
PS C:\Users\chris> $roleAssignment = New-AzRoleAssignment -Scope $secrets[4].Scope -RoleDefinitionId $azroledef.Id -PrincipalId $serverTeamID
PS C:\Users\chris> $roleAssignment = New-AzRoleAssignment -Scope $secrets[4].Scope -RoleDefinitionId $azroledef.Id -PrincipalId $appTeamID
Test results of user access to Secrets:
Because the test users do not have the ‘Key Vault Reader (preview)’ Role they cannot see/access secrets using the Portal.
The users can access the individual secrets using the Get-AzKeyVaultSecret cmdlet from a PowerShell session that the user has connected to the account with Connect-AzAccount.
I tested 1 user from each of the two groups using this method.
User | Secret | Access Test Results |
TestUser1 | Sec1 | Success |
TestUser1 | Sec2 | Success |
TestUser1 | Sec3 | Fail |
TestUser1 | Sec4 | Fail |
TestUser1 | Sec5 | Success |
TestUser3 | Sec1 | Fail |
TestUser3 | Sec2 | Fail |
TestUser3 | Sec3 | Success |
TestUser3 | Sec4 | Success |
TestUser3 | Sec5 | Success |