Preuve de concept — Azure Kubernetes Service

Déploiement AKS privé avec identité managée

Contournement des stratégies d'accès conditionnel d'Entra ID pour le déploiement de clusters AKS privés avec l'identité managée Azure et des runners auto-hébergés

📅 2 avril 2026

🇬🇧 English

01 Vue d'ensemble

Cette preuve de concept valide que l'identité managée Azure contourne les stratégies d'accès conditionnel (AC) d'Entra ID lors du déploiement de clusters AKS privés.

Les organisations ayant des stratégies AC strictes basées sur la localisation peuvent utiliser l'identité managée pour éviter les échecs d'authentification qui surviennent avec les principaux de service. Le PoC déploie un cluster AKS entièrement privé depuis un runner GitHub Actions auto-hébergé à l'intérieur du même VNet, démontrant la connectivité privée de bout en bout et le contournement de l'AC.

0,08 $
Coût par exécution de 30 min
3
Tâches automatisées
0
Évaluations AC déclenchées
100 %
Accès API privé

02 Énoncé du problème

L'authentification par principal de service lors de la création AKS est bloquée par les stratégies d'accès conditionnel basées sur la localisation.

Lorsque le fournisseur de ressources AKS s'authentifie avec les informations d'identification d'un principal de service lors de l'exécution de az aks create, la connexion provient des adresses IP des centres de données Azure, et non du réseau du client. Si l'organisation applique des stratégies d'accès conditionnel qui restreignent l'authentification aux adresses IP du périmètre connu, ces stratégies bloquent la connexion du principal de service.

Comparaison des flux d'authentification

FLUX PRINCIPAL DE SERVICE (PROBLÉMATIQUE) :
VM Runner → az login --service-principal → login.microsoftonline.com (depuis IP du runner ✓)
VM Runner → az aks create → ARM → AKS RP → login.microsoftonline.com (depuis IP du centre de données Azure ✗)
                                                                       ↑ BLOQUÉ par l'AC

FLUX IDENTITÉ MANAGÉE (RECOMMANDÉ) :
VM Runner → az login --identity → IMDS 169.254.169.254 (interne, pas d'AC ✓)
VM Runner → az aks create → ARM → AKS RP → jeton Azure fabric (interne, pas d'AC ✓)
                                                                ↑ NON évalué par l'AC

La distinction est architecturale : les identités managées ne déclenchent pas l'accès conditionnel car leurs informations d'identification sont gérées par Azure et l'émission de jetons s'effectue au sein de l'infrastructure Azure. Il n'y a pas d'« adresse IP source » à évaluer par l'AC.

03 Solution

L'identité managée contourne entièrement l'accès conditionnel — les jetons sont acquis en interne via IMDS, et non via login.microsoftonline.com.

« Les identités managées ne sont pas couvertes par les stratégies. » — Documentation Microsoft sur l'accès conditionnel pour les identités de charge de travail
🔐

Acquisition de jetons IMDS

Les jetons MI sont acquis via 169.254.169.254 — un point de terminaison interne de l'infrastructure Azure. Aucune connexion externe ne se produit.

🛡️

Contournement du moteur AC

Le moteur d'accès conditionnel n'évalue pas du tout les demandes de jetons d'identité managée. Aucune adresse IP source n'est exposée pour évaluation.

🌐

Runner dans le VNet

Une VM runner auto-hébergée dans le même VNet que le cluster AKS assure l'accès API privé et l'authentification basée sur IMDS.

04 Architecture

Workflow à trois tâches sur deux types de runners : hébergé par GitHub pour le provisionnement de l'infrastructure, auto-hébergé pour le déploiement AKS privé.

graph TD
    A["GitHub workflow_dispatch"] -->|ubuntu-latest| B["Tâche 1 : Configuration de l'infrastructure"]
    B --> B1["Créer VNet\nsubnet-aks + subnet-runner"]
    B1 --> B2["Créer MI + RBAC"]
    B2 --> B3["Créer VM Runner\ndans subnet-runner"]
    B3 --> B4["Enregistrer le runner GH Actions"]

    B4 -->|runner auto-hébergé| C["Tâche 2 : Déploiement + Validation"]
    C --> C1["az login --identity via IMDS"]
    C1 --> C2["az aks create\n--enable-private-cluster\ndans subnet-aks"]
    C2 --> C3{"Déploiement OK ?"}
    C3 -->|Oui| C4["kubectl get nodes\nvia point de terminaison privé"]
    C4 --> C5["Journaliser les IP + Téléverser les artéfacts"]
    C3 -->|Non| C5
    C5 --> C6["Attendre N minutes"]
    C6 --> C7["Supprimer le RG AKS"]

    C7 -->|ubuntu-latest| D["Tâche 3 : Nettoyage"]
    D --> D1["Désenregistrer le runner"]
    D1 --> D2["Supprimer le RG d'infrastructure"]

    style A fill:#1e1b4b,stroke:#4f46e5,color:#e6edf3
    style B fill:#1e3a5f,stroke:#3b82f6,color:#e6edf3
    style C fill:#3b0764,stroke:#7c3aed,color:#e6edf3
    style D fill:#4a1530,stroke:#e11d48,color:#e6edf3
    style C3 fill:#78350f,stroke:#d97706,color:#e6edf3
    style C4 fill:#064e3b,stroke:#059669,color:#e6edf3
    

Architecture réseau

graph LR
    subgraph VNET["VNet partagé — 10.224.0.0/16"]
        direction LR
        subgraph SAKS["subnet-aks\n10.224.0.0/24"]
            AKS["Cluster AKS privé\nAPI : 10.224.0.4\nNœud : 10.224.0.5"]
        end
        subgraph SRUNNER["subnet-runner\n10.224.1.0/24"]
            RUNNER["VM Runner\n10.224.1.4"]
        end
        RUNNER -->|"point de terminaison privé"| AKS
    end

    RUNNER -->|"IMDS\n169.254.169.254"| FABRIC["Azure Fabric\nÉmission de jetons"]

    style VNET fill:#0c1929,stroke:#3b82f6,stroke-width:2px,color:#e6edf3
    style SAKS fill:#1a0a2e,stroke:#7c3aed,color:#e6edf3
    style SRUNNER fill:#0a2e1e,stroke:#059669,color:#e6edf3
    style FABRIC fill:#1e1b4b,stroke:#4f46e5,color:#e6edf3
    

Flux d'identité

graph LR
    A["VM Runner"] -->|"az login --identity"| B["IMDS\n169.254.169.254"]
    B -->|"jeton MI"| C["Azure Fabric"]
    A -->|"az aks create"| D["ARM"]
    D --> E["AKS RP"]
    E -->|"MI du cluster via IMDS"| C

    style A fill:#0a2e1e,stroke:#059669,color:#e6edf3
    style B fill:#1e1b4b,stroke:#4f46e5,color:#e6edf3
    style C fill:#064e3b,stroke:#059669,stroke-width:2px,color:#e6edf3
    style D fill:#1e293b,stroke:#64748b,color:#e6edf3
    style E fill:#1e293b,stroke:#64748b,color:#e6edf3
    

05 Workflows GitHub Actions

Cycle de vie de l'infrastructure entièrement automatisé — provisionnement, déploiement, validation et nettoyage en une seule exécution de workflow.

deploy-private-aks.yml

Un workflow à trois tâches déclenché par workflow_dispatch :

🏗️

Tâche 1 : setup-runner

S'exécute sur ubuntu-latest via OIDC. Crée un VNet partagé avec deux sous-réseaux, provisionne une identité managée avec RBAC, crée une VM runner et l'enregistre comme runner auto-hébergé.

🚀

Tâche 2 : deploy-and-log

S'exécute sur le runner auto-hébergé. S'authentifie via l'identité managée (IMDS), déploie un AKS privé dans subnet-aks, valide avec kubectl, journalise les IP et téléverse les artéfacts.

🧹

Tâche 3 : teardown-runner

S'exécute sur ubuntu-latest. Désenregistre le runner, supprime les groupes de ressources AKS et d'infrastructure. S'exécute toujours, même en cas d'échec.

cleanup-safety-net.yml

Un workflow déclenché manuellement qui recherche les groupes de ressources correspondant au modèle rg-aks-poc-* datant de plus de 45 minutes. Sert de filet de sécurité pour les ressources orphelines.

06 Exécution vérifiée — 2 avril 2026

Les 3 tâches ont réussi. Objectifs du PoC confirmés. Exécution du workflow #23919580744

Exécution des tâches

TâcheRunnerDuréeRésultat
setup-runnerubuntu-latest7 min✅ Succès
deploy-and-logself-hosted (dans le VNet)40 min (incl. 30 min d'attente)✅ Succès
teardown-runnerubuntu-latest18 sec✅ Succès

Constat 1 : L'identité managée contourne l'accès conditionnel

Le journal d'activité Azure confirme que l'opération d'écriture ARM az aks create provient de l'adresse IP 20.104.78.99 — l'adresse IP publique de la VM runner. L'authentification s'est effectuée via IMDS, et non via login.microsoftonline.com. Aucune évaluation d'AC n'a été déclenchée.

Extrait du journal d'activité :
  Microsoft.ContainerService/managedClusters/write  Accepted  ClientIp: 20.104.78.99
  Microsoft.ContainerService/managedClusters/write  Started   ClientIp: 20.104.78.99

Constat 2 : Le cluster privé est véritablement privé

enablePrivateCluster : true
privateFqdn          : aks-poc-23-...privatelink.canadacentral.azmk8s.io
API Server Endpoint  : https://...privatelink.canadacentral.azmk8s.io:443
Le FQDN privé résout : 10.224.0.4 (adresse IP privée au sein du VNet)

Constat 3 : Le runner dans le même VNet atteint le serveur API privé

10.224.1.4
VM Runner
subnet-runner
10.224.0.4
Serveur API
point de terminaison privé
10.224.0.5
Nœud AKS
subnet-aks
kubectl cluster-info  → Plan de contrôle Kubernetes à ...privatelink.canadacentral.azmk8s.io:443
kubectl get nodes     → 1 nœud, Ready, v1.34.4
kubectl get pods -n kube-system → 15 pods, tous en cours d'exécution
kubectl get namespaces → default, kube-node-lease, kube-public, kube-system
nslookup FQDN privé → 10.224.0.4 ✓

Constat 4 : Artéfacts téléversés

Fichier de journalContenu
runner-network.logIP publique/privée de la VM runner, nom d'hôte, sous-réseau
aks-create.logSortie complète de az aks create (9 Ko)
aks-cluster-info.logPropriétés du cluster (version, FQDN, configuration réseau)
kubectl-validation.logToute la sortie kubectl incluant la résolution DNS
ip-activity-log.logIP des appelants des opérations ARM du journal d'activité Azure
ip-signin-log.logRequête de connexion Entra (erreur 403 attendue sans P1/P2)

07 Démarrage rapide

Exécutez le PoC dans votre abonnement en quelques minutes.

Prérequis

Étapes de configuration

  1. Créer un enregistrement d'application Azure AD avec des informations d'identification fédérées OIDC pour la branche main
  2. Attribuer les rôles Contributeur + Administrateur de l'accès utilisateur au niveau de l'abonnement
  3. Ajouter les secrets GitHub Actions :
    • AZURE_CLIENT_ID — Identifiant client de l'enregistrement d'application
    • AZURE_TENANT_ID — Identifiant du locataire Entra ID
    • AZURE_SUBSCRIPTION_ID — Identifiant de l'abonnement cible
    • GH_PAT — PAT GitHub avec le scope repo
  4. Déclencher le workflow deploy-private-aks depuis l'interface GitHub Actions

⚠️ Important : La tâche 3 du workflow s'exécute toujours (même en cas d'échec) et supprime les groupes de ressources AKS et d'infrastructure. Le nettoyage manuel n'est nécessaire que si le workflow lui-même est annulé avant l'exécution de la tâche 3.

Estimation des coûts

Chaque exécution de PoC de 30 minutes coûte environ 0,05 $ à 0,08 $ avec un seul nœud Standard_B2s sur le plan de contrôle AKS gratuit. La VM runner ne fonctionne que pendant la durée du workflow et est automatiquement supprimée.

08 Références

Documentation officielle et dépôt source.

RessourceLien
Dépôt sourceaks-private-deployment
Clusters AKS privés Azurelearn.microsoft.com
Utiliser l'identité managée avec AKSlearn.microsoft.com
Accès conditionnel pour les identités de charge de travaillearn.microsoft.com