Home >> Blog >> Infrastructure AKS (cluster Azure Kubernetes Service) : Sécuriser ses secrets dans Azure

Infrastructure AKS (cluster Azure Kubernetes Service) : Sécuriser ses secrets dans Azure

04 décembre 2023

By Lionel Gurret.

Introduction

Dans Kubernetes, les Secrets représentent des objets API destinés à la conservation d’informations sensibles, à l’exemple de mots de passe, de jetons et de clés. Leur fonctionnement est semblable à celui des ConfigMaps. Les Secrets servent à maintenir une distinction entre les données sensibles et le code applicatif, facilitant ainsi leur gestion indépendante de l’application. Au sein des Pods, ces Secrets peuvent être exploités sous la forme de variables d’environnement, d’arguments en ligne de commande ou encore de fichiers de configuration stockés dans un volume.

Il convient de souligner que les données contenues dans les Secrets de Kubernetes ne bénéficient pas de chiffrement puisqu’elles sont enregistrées en base64 et simplement protégées par le RBAC en amont. Afin de renforcer la sécurité, Kubernetes propose le chiffrement au repos (Encryption at rest) grâce à EncryptionConfig. Ce mécanisme chiffre les données sensibles lors de la création d’un Secret en utilisant une clé de chiffrement dédiée.

En alternative, il est recommandé d’utiliser des systèmes externes de gestion des secrets tels que Vault ou Azure Key Vault. Cette approche représente une meilleure pratique pour le stockage et la gestion de données sensibles, renforçant ainsi la sécurité au sein de Kubernetes.

Présentation de Microsoft Workload Identity

Une identité de charge de travail (ou Workload Identity) est une identité que vous attribuez à une charge de travail logicielle (telle qu’une application, un service, un script ou un conteneur) pour authentifier et accéder à d’autres services et ressources.

Elles reposent sur le protocole OIDC (OpenID Connect) qui est un protocole d’authentification basé sur le standard OAuth 2.0. Il permet à un utilisateur de s’authentifier auprès d’une application en utilisant un fournisseur d’identité tiers, tel que dans notre cas Microsoft Entra ID.

Dans un contexte AKS, elle permet notamment aux pods d’accéder aux ressources Azure, éliminant ainsi le besoin de stocker des secrets d’authentification.

Par exemple, pour un workload qui peut stocker des fichiers dans Azure Storage, lorsque celui-ci doit accéder à ces fichiers, ou encore comme dans cet article à un Azure KeyVault pour récupérer un mot de passe.

Ci-dessous le schéma de principe de notre démo :

Nous observons plusieurs points clefs :

  • L’activation de deux fonctionnalités dans AKS
  • La création d’un compte de service Kubernetes
  • La création d’une Azure Managed Identity.
  • Un lien de fédération entre le service account, l’OIDC Issuer et la managed Identity

Passons maintenant à la mise en place de l’infrastructure et d’une application de test.

Mise en place de l’infrastructure

Variables d’environnement

Avant de lancer nos commandes AZ CLI, nous allons préconfigurer des variables d’environnement pour que vous puissiez facilement adapter la mise en place de cette démonstration.

export AKS_CLUSTER_NAME=aks-demo-workloadidentity
export RESOURCE_GROUP="WorkloadIdentityResourceGroup"
export LOCATION="eastus"
export SERVICE_ACCOUNT_NAMESPACE="default"
export SERVICE_ACCOUNT_NAME="workload-identity-sa"
export SUBSCRIPTION="$(az account show --query id --output tsv)"
export USER_ASSIGNED_IDENTITY_NAME="myIdentity"
export FEDERATED_IDENTITY_CREDENTIAL_NAME="myFedIdentity"
export KEYVAULT_NAME="lgurret-demo-aks-wi"
export KEYVAULT_SECRET_NAME="my-secret"

Création du cluster AKS et ses fonctionnalités

Commençons par créer notre resource group :

az group create -n $RESOURCE_GROUP -l $LOCATION

Nous pouvons créer le cluster AKS en activant les fonctionnalités nécessaires :

az aks create -g $RESOURCE_GROUP -n $AKS_CLUSTER_NAME --node-count 1 --enable-oidc-issuer 
  --enable-workload-identity --generate-ssh-keys

Le cluster étant créé, nous pouvons maintenant récupérer le kubeconfig ainsi que l’OIDC issuer URL pour la suite de la démo :

# get cluster credentials; requires kubectl
az aks get-credentials -g $RESOURCE_GROUP -n $AKS_CLUSTER_NAME --admin

# get OIDC issuer URL
export AKS_OIDC_ISSUER="$(az aks show -n $AKS_CLUSTER_NAME -g $RESOURCE_GROUP --query "oidcIssuerProfile.issuerUrl" -otsv)"

Création du KeyVault

Ici, nous allons créer le Keyvault, son secret et récupérer son URL pour l’utiliser plus tard dans la démonstration :

# Create a KV
az keyvault create --resource-group "${RESOURCE_GROUP}" --location "${LOCATION}" --name "${KEYVAULT_NAME}"

# Add secret
az keyvault secret set --vault-name "${KEYVAULT_NAME}" --name "${KEYVAULT_SECRET_NAME}" --value 'Hello!'

# Get KV URL
export KEYVAULT_URL="$(az keyvault show -g "${RESOURCE_GROUP}" -n ${KEYVAULT_NAME} --query properties.vaultUri -o tsv)"

Création une Identité Gérée dans Azure

A présent, nous allons créer une Identité gérée :

# get current subscription id
export SUBSCRIPTION_ID="$(az account show --query "id" -otsv)"

# create user assigned managed identity
az identity create --name $USER_ASSIGNED_IDENTITY_NAME --resource-group $RESOURCE_GROUP 
  --location $LOCATION --subscription $SUBSCRIPTION_ID

# Set an access policy for the managed identity to access the Key Vault secret
export USER_ASSIGNED_CLIENT_ID="$(az identity show --resource-group "${RESOURCE_GROUP}" --name "${USER_ASSIGNED_IDENTITY_NAME}" --query 'clientId' -otsv)"

On lui ajoute une politique d’accès pour qu’elle puisse lire les secrets du Keyvault :

az keyvault set-policy --name "${KEYVAULT_NAME}" --secret-permissions get --spn "${USER_ASSIGNED_CLIENT_ID}"

Création un Compte de Service dans Kubernetes

Nous pouvons à présent lancer la création des ressources sur le cluster AKS. Commençons par la création du compte de service qui sera utilisé par notre POD de démonstration pour se connecter au KeyVault :

cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: ServiceAccount
metadata:
  annotations:
    azure.workload.identity/client-id: ${USER_ASSIGNED_CLIENT_ID}
  name: ${SERVICE_ACCOUNT_NAME}
  namespace: ${SERVICE_ACCOUNT_NAMESPACE}
EOF

Le lien entre notre compte de service et notre identité managée se fait via une annotation dans le manifeste. En parallèle, des pods de webhook s’exécutent en arrière-plan lors de la création ou de la mise à jour des pods. Ces pods de webhook assurent que le jeton d’authentification du compte de service est correctement associé à un fichier dans le conteneur du pod, et qu’ils configurent les variables d’environnement nécessaires pour permettre au pod d’accéder de manière sécurisée aux ressources Azure.

Établissement d’une relation de confiance fédérée entre Kubernetes et AAD (Azure Active Directory)

Nous créons l’identité fédérée entre l’identité mangée, l’issuer OICD et le compte de service :

az identity federated-credential create --name ${FEDERATED_IDENTITY_CREDENTIAL_NAME} --identity-name ${USER_ASSIGNED_IDENTITY_NAME} --resource-group ${RESOURCE_GROUP} --issuer ${AKS_OIDC_ISSUER} --subject system:serviceaccount:${SERVICE_ACCOUNT_NAMESPACE}:${SERVICE_ACCOUNT_NAME}

Validation de l’infrastructure

Nous allons maintenant valider en créant un POD utilisant notre compte de service et notre mot de passe stocké dans l’Azure KeyVault. Pour cela, nous allons utiliser une image msal-go dédiée à cette validation, puisqu’elle possède tout le nécessaire pour aller lire notre secret en utilisant notre workload identity :

cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Pod
metadata:
  name: quick-start
  namespace: ${SERVICE_ACCOUNT_NAMESPACE}
  labels:
    azure.workload.identity/use: "true"
spec:
  serviceAccountName: ${SERVICE_ACCOUNT_NAME}
  containers:
    - image: ghcr.io/azure/azure-workload-identity/msal-go
      name: oidc
      env:
      - name: KEYVAULT_URL
        value: ${KEYVAULT_URL}
      - name: SECRET_NAME
        value: ${KEYVAULT_SECRET_NAME}
  nodeSelector:
    kubernetes.io/os: linux
EOF

Dans la pratique, nous recommandons évidemment de gérer les Workload Identities en IaC avec un outil comme Terraform.

La commande suivante nous permet de vérifier que nous pouvons lire notre notre de passe !

kubectl logs quick-start

Utilisation de la fonctionnalité Microsoft Entra Workload ID

Maintenant que nous avons pu mettre en place l’infrastructure nous permettant d’utiliser les Workload Identities, nous pouvons à présent passer à l’étape suivante en l’intégrant dans nos images de conteneur et nos charges de travail.

La fonctionnalité Microsoft Entra Workload ID s’intègre avec la gamme de bibliothèques clientes Azure Identity et la bibliothèque d’authentification Microsoft (MSAL), en particulier lorsque vous optez pour une inscription d’application (App registration).

Ici, vous trouvez un exemple de code Java pour récupérer un secret via les Workload Identities :

import java.util.Map;

import com.azure.security.keyvault.secrets.SecretClient;
import com.azure.security.keyvault.secrets.SecretClientBuilder;
import com.azure.security.keyvault.secrets.models.KeyVaultSecret;
import com.azure.identity.DefaultAzureCredentialBuilder;
import com.azure.identity.DefaultAzureCredential;

public class App {
    public static void main(String[] args) {
        Map<String, String> env = System.getenv();
        String keyVaultUrl = env.get("KEYVAULT_URL");
        String secretName = env.get("SECRET_NAME");

        SecretClient client = new SecretClientBuilder()
                .vaultUrl(keyVaultUrl)
                .credential(new DefaultAzureCredentialBuilder().build())
                .buildClient();
        KeyVaultSecret secret = client.getSecret(secretName);
    }
}

Dans le cas où vous ne pourriez pas avoir recours à ces librairies, il est toujours possible de récupérer notre secret en Bash en s’aidant de curl :

# We can also use a POST with a JSON
AZURE_FEDERATED_TOKEN=$(cat $AZURE_FEDERATED_TOKEN_FILE)
RESP=$(curl -sL 
        -X GET "$AZURE_AUTHORITY_HOST/$AZURE_TENANT_ID/oauth2/v2.0/token" 
        --form 'grant_type="client_credentials"' 
        --form 'client_id="'$AZURE_CLIENT_ID'"' 
        --form 'scope="https://vault.azure.net/.default"' 
        --form 'client_assertion_type="urn:ietf:params:oauth:client-assertion-type:jwt-bearer"' 
        --form 'client_assertion="'$AZURE_FEDERATED_TOKEN'"')

ACCESS_TOKEN=$(echo "$RESP" | jq -r '.access_token')
RESP=$(curl -sL 
        -H "Authorization:Bearer $ACCESS_TOKEN" 
        -H "Content-Type:application/json" 
        ${KEY_VAULT_URL}/secrets/${SECRET_NAME}/?api-version=7.4)

export SECRET=$(echo "$RESP" | jq -r '.value')

Ici, seules les variables d’environnement SECRET_NAME et KEY_VAULT_URL sont nécessaires !

SoKube peut vous aider !

Consultez les liens suivants liés à cet article de blog et découvrez comment SoKube peut vous aider :

Sources

Laisser un commentaire

  Edit this page