Given a windows container running inside Azure Kubernetes Service (AKS). How do I get a certificate (PFX) that I've stored in Azure Key Vault (AKV) stored in the local certificate store of the container?
N.B. This assumes you've already successfully gotten AKS wired up and talking to AKV. Pause and start elsewhere if you've not successfully brought simple passwords across into the environment of your windows container yet.
The trick is to recognise that when you install a certificate (PFX) into keyvault this is accessed as two separate objects and you can get these pulled into the environment as a combined PEM if you setup your k8s secret provider appropriately.
First you must setup your k8s secrets to request it as an objecttype of 'secret' (not key or cert) e.g. :
apiVersion: secrets-store.csi.x-k8s.io/v1
kind: SecretProviderClass
metadata:
name: sc-demo-keyvault-csi
spec:
provider: azure
parameters:
usePodIdentity: "false"
useVMManagedIdentity: "true" # Set to true for using managed identity
userAssignedIdentityID: <redacted> # Set the clientID of the user-assigned managed identity to use
keyvaultName: <redacted> # Set to the name of your key vault
objects: |
array:
- |
objectName: testcert # keyvault secret name
objectType: secret # getting a cert as a secret returns the public & private key pair as a pem, a type of cert just returns the public key (https://azure.github.io/secrets-store-csi-driver-provider-azure/docs/configurations/getting-certs-and-keys/)
tenantId: <REDACTED> # The tenant ID of the key vault
secretObjects:
- data:
- key: secretcert
objectName: testcert
secretName: foosecret
type: Opaque
Once this is done and you've mapped the secret through to your container as an environment variable in your deployment/pod description e.g.
apiVersion: apps/v1
kind: Deployment
spec:
template:
spec:
containers:
- name: test
env:
- name: SIGNING_KEYPAIR
valueFrom:
secretKeyRef:
name: foosecret
key: secretcert
volumeMounts:
- name : secrets-store01-inline
mountPath: "/mnt/secrets-store"
readOnly: true
volumes:
- name: secrets-store01-inline
csi:
driver: secrets-store.csi.k8s.io
readOnly: true
volumeAttributes:
secretProviderClass: 'sc-demo-keyvault-csi'
If you were to fire up your windows container at this point you'd find your environment contains a PEM file (I assume there's a potential issue here around the size of the certificates but not something I@ve run into.)
So then we just need to take that PEM, reconstruct into into a PFX file, load it into the certificate store in the container and apply the appropriate permissions.
Something like this works in powershell:
# Extract the keys from the environment variable
$matches = [regex]::match($Env:SIGNING_KEYPAIR,'(?smi)-----BEGIN PRIVATE KEY-----\s*(.+)-----END PRIVATE KEY-----\s*-----BEGIN CERTIFICATE-----\s*(.+)-----END CERTIFICATE-----')
$PRIVATE_KEY= $matches.Groups[1].Value
$PUBLIC_KEY= $matches.Groups[2].Value
# Write them out to a random file pair
$RANDOM_FILE= New-Guid
Out-File -FilePath "$RANDOM_FILE.key" -InputObject $PRIVATE_KEY
Out-File -FilePath "$RANDOM_FILE.cer" -InputObject $PUBLIC_KEY
# Create the PFX (the .key file will be attached as it shares the same filename)
& certutil -p "ignored,$RANDOM_FILE" -MergePFX "$RANDOM_FILE.cer" "$RANDOM_FILE.pfx" | Out-Null
$c= Import-PfxCertificate -Password (ConvertTo-SecureString -String "$RANDOM_FILE" -AsPlainText -Force) -FilePath "$RANDOM_FILE.pfx" -CertStoreLocation "Cert:\LocalMachine\My"
# Cleanup the environment
# (doesn't really improve the security position, but I'd rather not have secrets in two places)
Remove-Item "$RANDOM_FILE.*"
At this point you should have everything you need ($c.Thumbprint) to setup appropriate access to the private key as you would normally do.
The approach described here definitely works on containers based on mcr.microsoft.com/dotnet/framework/aspnet:4.8-windowsservercore-ltsc2019 . YMMV for containers based on other base containers.