Key Vault


The presented resiliency recommendations in this guidance include Key Vault and associated settings.

Summary of Recommendations

Recommendations Details

KV-1 - Key vaults should have soft delete enabled

Category: Disaster Recovery

Impact: High

Guidance

Key Vault’s soft-delete feature allows recovery of the deleted vaults and deleted key vault objects (for example, keys, secrets, certificates), known as soft-delete.When soft-delete is enabled, resources marked as deleted resources are retained for a specified period (90 days by default). The service further provides a mechanism for recovering the deleted object, essentially undoing the deletion

Resources

Resource Graph Query

// Azure Resource Graph Query
// This Resource Graph query will return all Key Vaults that do not have soft delete enabled.
resources
| where type == "microsoft.keyvault/vaults"
| where isnull(properties.enableSoftDelete) or properties.enableSoftDelete != "true"
| project recommendationId = "kv-1", name, id, tags, param1 = "EnableSoftDelete: Disabled"



KV-2 - Key vaults should have purge protection enabled

Category: Disaster Recovery

Impact: High

Guidance

Malicious deletion of a key vault can lead to permanent data loss. A malicious insider in your organization can potentially delete and purge key vaults. Purge protection protects you from insider attacks by enforcing a mandatory retention period for soft deleted key vaults. No one inside your organization or Microsoft will be able to purge your key vaults during the soft delete retention period.

Resources

Resource Graph Query

// Azure Resource Graph Query
// This resource graph query will return all Key Vaults that do not have Purge Protection enabled.
resources
| where type == "microsoft.keyvault/vaults"
| where isnull(properties.enablePurgeProtection) or properties.enablePurgeProtection != "true"
| project recommendationId = "kv-2", name, id, tags, param1 = "EnablePurgeProtection: Disabled"



Category: Networking

Impact: High

Guidance

Azure Private Link Service enables you to access Azure Key Vault and Azure hosted customer/partner services over a Private Endpoint in your virtual network. An Azure Private Endpoint is a network interface that connects you privately and securely to a service powered by Azure Private Link. The private endpoint uses a private IP address from your VNet, effectively bringing the service into your VNet. All traffic to the service can be routed through the private endpoint, so no gateways, NAT devices, ExpressRoute or VPN connections, or public IP addresses are needed. Traffic between your virtual network and the service traverses over the Microsoft backbone network, eliminating exposure from the public Internet. You can connect to an instance of an Azure resource, giving you the highest level of granularity in access control.

Resources

Resource Graph Query

// Azure Resource Graph Query
// This resource graph query will return all Key Vaults that does not have a Private Endpoint Connection or where a private endpoint exists but public access is enabled

resources
| where type == "microsoft.keyvault/vaults"
| where isnull(properties.privateEndpointConnections) or properties.privateEndpointConnections[0].properties.provisioningState != ("Succeeded") or (isnull(properties.networkAcls) and properties.publicNetworkAccess == 'Enabled')
| extend param1 = strcat('Private Endpoint: ', iif(isnotnull(properties.privateEndpointConnections),split(properties.privateEndpointConnections[0].properties.privateEndpoint.id,'/')[8],'No Private Endpoint'))
| extend param2 = strcat('Access: ', iif(properties.publicNetworkAccess == 'Disabled', 'Public Access Disabled', iif(isnotnull(properties.networkAcls), 'NetworkACLs in place','Public Access Enabled')))
| project recommendationID = "kv-3", name, id, tags, param1, param2



KV-4 - Use separate key vaults per application per environment

Category: Governance

Impact: High

Guidance

Key vaults define security boundaries for stored secrets. Grouping secrets into the same vault increases the blast radius of a security event because attacks might be able to access secrets across concerns. To mitigate access across concerns, consider what secrets a specific application should have access to, and then separate your key vaults based on this delineation. Separating key vaults by application is the most common boundary. Security boundaries, however, can be more granular for large applications, for example, per group of related services.

Resources

Resource Graph Query

// under-development



KV-5 - Diagnostic logs in Key Vault should be enabled

Category: Monitoring

Impact: Low

Guidance

Enable logs , set up alerts and retain them as per the retention requirement. This enables you to monitor how and when your key vaults are accessed, and by whom.

Resources

Resource Graph Query

// Azure Resource Graph Query
// This resource graph query will return all Key Vaults that does not have Diagnostic logs enabled

policyresources
|wheretype=='microsoft.policyinsights/policystates'
|whereproperties.complianceState=='NonCompliant'
| extend policyDefinitionId = tostring(tolower(properties.policyDefinitionId)),resourceId = tostring(tolower(properties.resourceId)), PolicyAssignmentName = properties.policyAssignmentName, policySetDefinitionId = tostring(tolower(properties.policySetDefinitionId))
| project resourceId,policySetDefinitionId,policyDefinitionId
| join kind=inner(
    policyresources
    |where type == 'microsoft.authorization/policydefinitions'
    | extend displayName = tostring(properties.displayName)
    | where displayName contains "Resource logs in Key Vault should be enabled"
    | project policyDefinitionId=tostring(tolower(id)),displayName
) on policyDefinitionId
| project resourceId,policySetDefinitionId,policyDefinitionId
| join kind=inner(
    policyresources
    |where type == 'microsoft.authorization/policysetdefinitions'
    | extend displayName = tostring(properties.displayName)
    | where displayName contains "Microsoft cloud security benchmark"
    | project policySetDefinitionId=tostring(tolower(id)),displayName
) on policySetDefinitionId
| join kind=inner(
  resources
  | where type == 'microsoft.keyvault/vaults'
  | project resourceId = tostring(tolower(id)),name,tags
)on resourceId
| project-away resourceId1,policySetDefinitionId1,policySetDefinitionId,policyDefinitionId,displayName
| project recommendationID = "kv-5",id=resourceId,name,tags