-
Notifications
You must be signed in to change notification settings - Fork 8
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
baf646b
commit b50d424
Showing
15 changed files
with
1,176 additions
and
28 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,357 @@ | ||
--- | ||
title: "Azure Key Vault AKS integration with CSI Driver" | ||
date: "2023-03-08" | ||
thumbnail: "img/aks2.webp" | ||
disable_myintraments: false # Optional, disable Disqus myintraments if true | ||
authorbox: true # Optional, enable authorbox for specific post | ||
toc: true # Optional, enable Table of Contents for specific post | ||
mathjax: true # Optional, enable MathJax for specific post | ||
tags: | ||
- "Azure" | ||
- "AKS" | ||
- "Kubernetes" | ||
- "K8S" | ||
draft: false | ||
coffeebox: yes | ||
keywords: | ||
- "Azure" | ||
- "AKS" | ||
- "streaming" | ||
--- | ||
|
||
|
||
In this Post I will show you how you can use CSI Driver to mount secrets from Azure Key Vault to AKS. | ||
<!--more--> | ||
|
||
## Create key vault and add keys | ||
|
||
First we need to create a key vault | ||
|
||
```bash | ||
az aks enable-addons \ | ||
--addons=azure-keyvault-secrets-provider \ | ||
--name=$CLUSTER \ | ||
--resource-group=$RG | ||
|
||
# create the key vault and turn on Azure RBAC; we will grant a managed identity access to this key vault below | ||
az keyvault create \ | ||
--name $KV \ | ||
--resource-group $RG \ | ||
--location westeurope \ | ||
--enable-rbac-authorization true | ||
|
||
# get the subscription id | ||
SUBSCRIPTION_ID=$(az account show --query id -o tsv) | ||
|
||
# get your user object id | ||
USER_OBJECT_ID=$(az ad signed-in-user show --query objectId -o tsv) | ||
|
||
# grant yourself access to key vault | ||
az role assignment create \ | ||
--assignee-object-id $USER_OBJECT_ID \ | ||
--role "Key Vault Administrator" \ | ||
--scope /subscriptions/$SUBSCRIPTION_ID/resourceGroups/$RG/providers/Microsoft.KeyVault/vaults/$KV | ||
|
||
# add a secret to the key vault | ||
az keyvault secret set --vault-name $KV --name $SECRET --value $VALUE | ||
``` | ||
|
||
Create secret in keyvault: | ||
|
||
```bash | ||
az keyvault secret set \ | ||
--vault-name "$KV" \ | ||
--name "sqldatabase" \ | ||
--value 'kvdemo' | ||
|
||
az keyvault secret set \ | ||
--vault-name "$KV" \ | ||
--name "sqlusername" \ | ||
--value 'root' | ||
|
||
az keyvault secret set \ | ||
--vault-name "$KV" \ | ||
--name "sqlpassword" \ | ||
--value 'Password1' | ||
``` | ||
|
||
## Acces key vault with AKS's managed identity | ||
|
||
If you created your AKS cluster with managed identity you need to grant access to a managed identity: | ||
|
||
```bash | ||
# grab the managed identity principalId assuming it is in the default | ||
# MC_ group for your cluster and resource group | ||
IDENTITY_ID=$(az identity show -g MC\_$RG\_$CLUSTER\_westeurope --name azurekeyvaultsecretsprovider-$CLUSTER --query principalId -o tsv) | ||
|
||
# grant access rights on Key Vault | ||
az role assignment create \ | ||
--assignee-object-id $IDENTITY_ID \ | ||
--role "Key Vault Administrator" \ | ||
--scope /subscriptions/$SUBSCRIPTION_ID/resourceGroups/$RG/providers/Microsoft.KeyVault/vaults/$KV | ||
``` | ||
|
||
Create a SecretProviderClass: | ||
|
||
```bash | ||
AZURE_TENANT_ID=$(az account show --query tenantId -o tsv) | ||
CLIENT_ID=$(az aks show -g $RG -n $CLUSTER --query addonProfiles.azureKeyvaultSecretsProvider.identity.clientId -o tsv) | ||
``` | ||
|
||
```yaml | ||
cat <<EOF | kubectl apply -f - | ||
apiVersion: secrets-store.csi.x-k8s.io/v1 | ||
kind: SecretProviderClass | ||
metadata: | ||
name: demo-secret | ||
namespace: default | ||
spec: | ||
provider: azure | ||
parameters: | ||
usePodIdentity: "false" | ||
# use managed identity | ||
useVMManagedIdentity: "true" | ||
# add managed identity id manually | ||
userAssignedIdentityID: "$CLIENT_ID" | ||
tenantId: "$AZURE_TENANT_ID" | ||
keyvaultName: "$KV" | ||
# name and type in keyvault | ||
objects: | | ||
array: | ||
- | | ||
objectName: "sqldatabase" | ||
objectType: secret | ||
- | | ||
objectName: "sqlusername" | ||
objectType: secret | ||
- | | ||
objectName: "sqlpassword" | ||
objectType: secret | ||
secretObjects: | ||
- secretName: databasesecrets | ||
type: Opaque | ||
# name and key in secret | ||
data: | ||
- objectName: "sqldatabase" | ||
key: sqldatabase | ||
- objectName: "sqlusername" | ||
key: sqlusername | ||
- objectName: "sqlpassword" | ||
key: sqlpassword | ||
EOF | ||
``` | ||
|
||
Mount the secrets in pods | ||
|
||
```yaml | ||
cat <<EOF | kubectl apply -f - | ||
apiVersion: apps/v1 | ||
kind: Deployment | ||
metadata: | ||
labels: | ||
app: secretpods | ||
name: secretpods | ||
spec: | ||
replicas: 1 | ||
selector: | ||
matchLabels: | ||
app: secretpods | ||
template: | ||
metadata: | ||
labels: | ||
app: secretpods | ||
spec: | ||
containers: | ||
- image: nginx | ||
name: nginx | ||
# get as environment variables | ||
env: | ||
- name: sqldatabase | ||
valueFrom: | ||
secretKeyRef: | ||
name: databasesecrets | ||
key: sqldatabase | ||
- name: sqlusername | ||
valueFrom: | ||
secretKeyRef: | ||
name: databasesecrets | ||
key: sqlusername | ||
- name: sqlpassword | ||
valueFrom: | ||
secretKeyRef: | ||
name: databasesecrets | ||
key: sqlpassword | ||
# mount as file | ||
volumeMounts: | ||
- name: secret-store | ||
mountPath: "mnt/secret-store" | ||
readOnly: true | ||
# get the SecretProviderClass object | ||
volumes: | ||
- name: secret-store | ||
csi: | ||
driver: secrets-store.csi.k8s.io | ||
readOnly: true | ||
volumeAttributes: | ||
secretProviderClass: "demo-secret" | ||
EOF | ||
``` | ||
|
||
## Acces key vault with service principal | ||
|
||
Create an identity in Azure | ||
|
||
```bash | ||
export CLIENT_SECRET=$(az ad sp create-for-rbac \ | ||
--role Contributor --scopes /subscriptions/$SUBSCRIPTION_ID \ | ||
--name http://secrets-store-test --query 'password' -otsv) | ||
|
||
export CLIENT_ID=$(az ad sp show --id http://secrets-store-test --query 'appId' -otsv) | ||
``` | ||
|
||
Provide the identity to access the Azure key vault | ||
|
||
```bash | ||
az keyvault set-policy -n $KV --secret-permissions get --spn $CLIENT_ID | ||
``` | ||
|
||
Create the Kubernetes secret with credentials | ||
|
||
```yaml | ||
cat <<EOF | kubectl apply -f - | ||
apiVersion: v1 | ||
data: | ||
clientid: $CLIENT_ID | ||
clientsecret: $CLIENT_SECRET | ||
kind: Secret | ||
metadata: | ||
labels: | ||
secrets-store.csi.k8s.io/used: "true" | ||
name: secrets-store-creds | ||
namespace: default | ||
type: Opaque | ||
EOF | ||
``` | ||
|
||
Create and apply your own SecretProviderClass object | ||
|
||
```yaml | ||
cat <<EOF | kubectl apply -f - | ||
apiVersion: secrets-store.csi.x-k8s.io/v1 | ||
kind: SecretProviderClass | ||
metadata: | ||
name: demo-secret | ||
namespace: default | ||
spec: | ||
provider: azure | ||
parameters: | ||
usePodIdentity: "false" | ||
useVMManagedIdentity: "false" | ||
tenantId: "$AZURE_TENANT_ID" | ||
keyvaultName: "$KV" | ||
# name and type in keyvault | ||
objects: | | ||
array: | ||
- | | ||
objectName: "sqldatabase" | ||
objectType: secret | ||
- | | ||
objectName: "sqlusername" | ||
objectType: secret | ||
- | | ||
objectName: "sqlpassword" | ||
objectType: secret | ||
secretObjects: | ||
- secretName: databasesecrets | ||
type: Opaque | ||
# name and key in secret | ||
data: | ||
- objectName: "sqldatabase" | ||
key: sqldatabase | ||
- objectName: "sqlusername" | ||
key: sqlusername | ||
- objectName: "sqlpassword" | ||
key: sqlpassword | ||
EOF | ||
``` | ||
|
||
Mount the secrets in pods | ||
|
||
```yaml | ||
cat <<EOF | kubectl apply -f - | ||
apiVersion: apps/v1 | ||
kind: Deployment | ||
metadata: | ||
labels: | ||
app: secretpods | ||
name: secretpods | ||
spec: | ||
replicas: 1 | ||
selector: | ||
matchLabels: | ||
app: secretpods | ||
template: | ||
metadata: | ||
labels: | ||
app: secretpods | ||
spec: | ||
containers: | ||
- image: nginx | ||
name: nginx | ||
# get as environment variables | ||
env: | ||
- name: sqldatabase | ||
valueFrom: | ||
secretKeyRef: | ||
name: databasesecrets | ||
key: sqldatabase | ||
- name: sqlusername | ||
valueFrom: | ||
secretKeyRef: | ||
name: databasesecrets | ||
key: sqlusername | ||
- name: sqlpassword | ||
valueFrom: | ||
secretKeyRef: | ||
name: databasesecrets | ||
key: sqlpassword | ||
# mount as file | ||
volumeMounts: | ||
- name: secret-store | ||
mountPath: "mnt/secret-store" | ||
readOnly: true | ||
# get the SecretProviderClass object | ||
volumes: | ||
- name: secret-store | ||
csi: | ||
driver: secrets-store.csi.k8s.io | ||
readOnly: true | ||
volumeAttributes: | ||
secretProviderClass: "demo-secret" | ||
# Only required when using service principal mode | ||
nodePublishSecretRef: | ||
name: secrets-store-creds | ||
EOF | ||
``` | ||
|
||
|
||
## Demo time | ||
|
||
Now we you can werify the secret in the pod: | ||
|
||
```bash | ||
export POD_NAME=$(kubectl get pods -l "app=secretpods" -o jsonpath="{.items[0].metadata.name}") | ||
|
||
# if this does not work, check the status of the pod | ||
# if still in ContainerCreating there might be an issue | ||
kubectl exec -it $POD_NAME -- sh | ||
|
||
cd /mnt/secret-store | ||
ls | ||
sqldatabase sqlpassword sqlusername | ||
# the file containing the secret is listed | ||
cat sqldatabase | ||
|
||
# echo the value of the environment variable | ||
echo $sqldatabase | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.