Store a Chef organization certificate in Azure Key Vault

Through the Azure Chef VM Extension, you can easily bootstrap the Chef client on to the VM operating system. This allows you to apply Chef-driven configuration management, compliance scanning and automation quickly and easily.

In order to securely authenticate a new node against the Chef server (or hosted Chef), the bootstrap process must use the private key of the organization to which it will be joined. Each Chef organization has a separate private key, which is usually called <ORGNAME>-validator.pem.

Naturally, you don’t want to expose this private key in plain text, so in order to protect the key during the deployment of the Chef VM Extension resource, we can make use of Azure Key Vault.  During the deployment of the VM Extension resource, Azure accesses the private key within the Key Vault and passes it (in an encrypted format) as a deployment parameter.  The Chef client can then present the key to the Chef server and authenticate successfully.

You can perform these steps using the Azure Portal, but in this scenario I’m using Azure PowerShell.

To create a new Azure Key Vault, authenticate against your Azure Subscription:

## Log in to Azure Resource Manager
Add-AzureRmAccount

 

Next, create a a Resource Group which will house the new Key Vault:

## Create Resource Group
$resourceGroupName = 'keyvault'
$location = 'Australia Southeast'
$resourceGroup = New-AzureRmResourceGroup `
-Name $resourceGroupName `
-Location $location -Force
$resourceGroup

Now we can create the Key Vault. Because the name assigned to the resource has to be globally unique, we’ll make use of a function written by Jon Gurgul which takes a string (which in this case will be the resource ID of the Azure Resource Group we just created) and converts it in to a hashed string. We’ll use this hash to generate a unique name for the Key Vault.

## Create Azure KeyVault
Function Get-StringHash([String] $String,$HashName = "MD5")
{
$StringBuilder = New-Object System.Text.StringBuilder
[System.Security.Cryptography.HashAlgorithm]::Create($HashName).ComputeHash([System.Text.Encoding]::UTF8.GetBytes($String))|%{
[Void]$StringBuilder.Append($_.ToString("x2"))
}
$StringBuilder.ToString()
}
$vaultName = 'myVault'
$uniqueString = (Get-StringHash -String $resourceGroup.ResourceId).substring(0,10)
$vaultName = $vaultName + $uniqueString
$vault = New-AzureRmKeyVault `
-VaultName $vaultName `
-ResourceGroupName $resourceGroup.ResourceGroupName `
-Location $resourceGroup.Location `
-EnabledForTemplateDeployment -Verbose
$vault

Note that we chose to enable this Key Vault for template deployment.  This means that an Azure Resource Manager deployment can query the Key Vault.

Next we read the contents of the Chef organization validation certificate (the private key) and convert it to a secure string:

## Get Certificate details
$certPath = "$env:USERPROFILE\Documents\git\chef-hosted\.chef"
$certName = 'chef-org-validator.pem'
$cert = $certPath + '\' + $certName
$secretName = 'chef-org-validator'
$secretValue = Get-Content $cert -Raw | ConvertTo-SecureString -AsPlainText -Force

If you built your own instance of Chef Server, then you should have access to the organization validation certificate.  If you’re using Hosted Chef (https://api.chef.io) then you can access the validation key by resetting it from the management portal: Administration –> Organizations –> Reset Validation Key.

Finally we can create a new secret within the Key Vault using the stored value:

## Create Key Vault secret
$vaultSecret = Set-AzureKeyVaultSecret `
-VaultName $vault.VaultName `
-Name $secretName `
-SecretValue $secretValue `
-Verbose
$vaultSecret

We can then call the Key Vault secret during an ARM template deployment in the parameters file (e.g. azuredeploy.parameters.json):

"chefValidationKey": {
"reference": {
"keyVault": {
"id": "/subscriptions/<SUBSCRIPTIONID>/resourceGroups/<RESOURCEGROUPNAME>/providers/Microsoft.KeyVault/vaults/<VAULTNAME>"
},
"secretName": "chef-org-validator"
}
}

This process works for other, non-Chef certificates as well.  All we are doing is reading the private key as a string using PowerShell and storing it as a secure variable, then creating a new Key Vault secret using the encrypted value.

And here’s the entire PowerShell script:

 

## Log in to Azure Resource Manager
Add-AzureRmAccount
## Create Resource Group
$resourceGroupName = 'keyvault'
$location = 'Australia Southeast'
$resourceGroup = New-AzureRmResourceGroup `
-Name $resourceGroupName `
-Location $location -Force
$resourceGroup
## Create Azure KeyVault
Function Get-StringHash([String] $String,$HashName = "MD5")
{
$StringBuilder = New-Object System.Text.StringBuilder
[System.Security.Cryptography.HashAlgorithm]::Create($HashName).ComputeHash([System.Text.Encoding]::UTF8.GetBytes($String))|%{
[Void]$StringBuilder.Append($_.ToString("x2"))
}
$StringBuilder.ToString()
}
$vaultName = 'myVault'
$uniqueString = (Get-StringHash -String $resourceGroup.ResourceId).substring(0,10)
$vaultName = $vaultName + $uniqueString
$vault = New-AzureRmKeyVault `
-VaultName $vaultName `
-ResourceGroupName $resourceGroup.ResourceGroupName `
-Location $resourceGroup.Location `
-EnabledForTemplateDeployment -Verbose
$vault
## Get Certificate details
$certPath = "$env:USERPROFILE\Documents\git\chef-hosted\.chef"
$certName = 'chef-org-validator.pem'
$cert = $certPath + '\' + $certName
$secretName = 'chef-org-validator'
$secretValue = Get-Content $cert -Raw | ConvertTo-SecureString -AsPlainText -Force
## Create Key Vault secret
$vaultSecret = Set-AzureKeyVaultSecret `
-VaultName $vault.VaultName `
-Name $secretName `
-SecretValue $secretValue `
-Verbose
$vaultSecret

Leave a Reply

You can use these HTML tags

<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>