Cloud Intelligence™Cloud Intelligence™

Cloud Intelligence™

Accéder aux services AWS depuis Google Kubernetes Engine (GKE) en toute sécurité

By Alexei LedenevFeb 17, 20208 min read

Cette page est également disponible en English, Deutsch, Español, Italiano, 日本語 et Português.

1 nxvyzhmtvz4ujcq1yadd7g

Il arrive fréquemment qu'une application qui tourne sur Google Kubernetes Engine ( GKE) ait besoin d'accéder aux API d'Amazon Web Services ( AWS). Chaque application a ses besoins : exécuter une requête analytique sur Amazon Redshift, accéder à des données stockées dans un bucket Amazon S3, convertir du texte en parole avec Amazon Polly ou faire appel à n'importe quel autre service AWS. Ce scénario multi-cloud est aujourd'hui monnaie courante, à mesure que les entreprises s'appuient sur plusieurs fournisseurs cloud.

1 nxvyzhmtvz4ujcq1yadd7g

L'accès inter-cloud soulève un nouveau défi : comment gérer les identifiants cloud nécessaires pour qu'un fournisseur accède aux services hébergés chez un autre ? L'approche naïve, qui consiste à distribuer et stocker les secrets du fournisseur cloud, n'est pas la plus sûre. Distribuer des identifiants à long terme à chaque service ayant besoin d'accéder aux services AWS est compliqué à gérer et constitue un risque de sécurité potentiel.

Solutions actuelles

Chaque cloud propose sa propre solution pour relever ce défi, et si vous travaillez avec un seul fournisseur cloud, cela suffit largement.

Google Cloud a annoncé Workload Identity, la méthode recommandée pour permettre aux applications GKE de s'authentifier auprès des autres services Google Cloud et de les consommer. Workload Identity associe des comptes de service Kubernetes à des comptes de service Cloud IAM. Vous pouvez ainsi vous appuyer sur des concepts natifs de Kubernetes pour définir quels workloads s'exécutent sous quelles identités, et autoriser vos workloads à accéder automatiquement aux autres services Google Cloud, sans avoir à gérer le moindre Kubernetes secret ni la moindre clé de compte de service IAM ! Lisez l'article DoiT Kubernetes GKE Workload Identity.

Amazon Web Services propose une fonctionnalité équivalente avec IAM Roles for Service Accounts. Sur les clusters Amazon EKS, cette fonctionnalité permet d'associer un rôle IAM à un compte de service Kubernetes. Ce compte de service accorde alors les permissions AWS aux conteneurs de tout pod qui l'utilise. Plus besoin, dès lors, d'attribuer des permissions étendues au rôle IAM du worker node pour que les pods de ce node puissent appeler les API AWS.

Mais que faire si vous exécutez votre workload applicatif sur un cluster GKE et que vous souhaitez accéder aux services AWS sans rien sacrifier à la sécurité ?

Définition du cas d'usage

Supposons que vous disposiez déjà d'un compte AWS et d'un cluster GKE, et que votre entreprise ait choisi d'exécuter une application en microservices sur ce cluster GKE, tout en continuant à exploiter les ressources du compte AWS (services Amazon S3 et SNS) pour s'intégrer à d'autres systèmes déployés sur AWS.

Par exemple, le job d'orchestration (déployé sous forme de Kubernetes Job) s'exécute à l'intérieur d'un cluster GKE et doit charger un fichier de données dans un bucket S3 puis envoyer un message à un topic Amazon SNS. La ligne de commande équivalente pourrait ressembler à ceci :

https://gist.github.com/90682305b879a96d273284df5d20fdcb

Exemple plutôt simple. Pour que ces commandes aboutissent, le job d'orchestration doit disposer d'identifiants AWS, et ces identifiants doivent être habilités à effectuer les appels d'API en question.

L'approche naïve (et non sécurisée) : identifiants IAM à long terme

Exporter une AWS Access Key et une Secret Key pour un utilisateur AWS IAM, puis injecter ces identifiants AWS dans le job d'orchestration, sous forme de fichier d'identifiants ou de variables d'environnement. Sans doute pas en direct, mais via une ressource Kubernetes Secrets protégée par une politique d'autorisation RBAC.

Le risque, c'est que ces identifiants n'expirent jamais. Il faut bien les transférer d'une manière ou d'une autre depuis l'environnement AWS vers l'environnement GCP, et la plupart du temps, on souhaite les conserver quelque part pour pouvoir recréer le job d'orchestration par la suite si besoin.

Avec des identifiants AWS à long terme, votre compte AWS peut être compromis de bien des façons : commit involontaire dans un dépôt GitHub, conservation dans un Wiki, réutilisation des mêmes identifiants entre plusieurs services et applications, accès non restreint, et ainsi de suite.

S'il est tout à fait possible de mettre en place une solution adéquate de gestion d'identifiants pour les credentials d'utilisateurs IAM émis, autant ne jamais créer ces identifiants à long terme dès le départ.

L'approche proposée

L'idée de base consiste à attribuer un AWS IAM Role à un Pod GKE, à l'image des fonctionnalités Workload Identity et EKS IAM Roles for Service Accounts propres à chaque cloud.

Heureusement pour nous, AWS permet de créer un rôle IAM pour des fournisseurs d'identité OpenID Connect Federation OIDC à la place d'utilisateurs IAM. De son côté, Google met en œuvre un fournisseur OIDC qu'il intègre étroitement à GKE via la fonctionnalité Workload Identity, fournissant un token OIDC valide à un pod GKE qui s'exécute sous un Kubernetes Service Account lié à un Google Cloud Service Account. Tous ces ingrédients réunis vont nous permettre de mettre en place un accès sécurisé entre GKE et AWS.

Échanger un access token OIDC contre un ID token

Il manque encore une pièce au puzzle. Avec une Workflow Identity correctement configurée, le Pod GKE obtient un access token OIDC qui ouvre l'accès aux services Google Cloud. Pour obtenir des identifiants AWS temporaires auprès du AWS Security Token Service ( STS), il faut en revanche fournir un ID token OIDC valide.

Le SDK AWS (et l'outil aws-cli) demanderont automatiquement des identifiants AWS temporaires au service STS dès lors que les variables d'environnement suivantes sont correctement configurées :

  • AWS_WEB_IDENTITY_TOKEN_FILE - le chemin du fichier de web identity token (ID token OIDC)
  • AWS_ROLE_ARN - l'ARN du rôle à assumer par les conteneurs du Pod
  • AWS_ROLE_SESSION_NAME - le nom appliqué à cette session assume-role

Cela peut sembler un peu complexe, mais je vais détailler la marche à suivre étape par étape, en m'appuyant sur le projet open source dointl/gtoken qui simplifie la configuration.

gtoken-webhook Kubernetes Mutating Admission webhook

Le gtoken-webhook est un mutating admission webhook Kubernetes qui modifie tout Pod K8s s'exécutant sous un Kubernetes Service Account spécialement annoté (voir détails ci-dessous).

Flux de mutation de gtoken-webhook

Le gtoken-webhook injecte un gtoken``initContainer dans le Pod cible ainsi qu'un conteneur compagnon gtoken supplémentaire (chargé de rafraîchir l'ID token OIDC juste avant son expiration), monte un volume de token et injecte trois variables d'environnement spécifiques à AWS. Le conteneur gtoken génère un ID Token GCP OIDC valide et l'écrit dans le volume de token. Il injecte également les variables d'environnement AWS nécessaires.

Le SDK AWS effectuera automatiquement les appels AssumeRoleWithWebIdentity correspondants vers AWS STS pour votre compte. Il prend en charge la mise en cache en mémoire ainsi que le renouvellement des identifiants à la demande.

1 7usxylt69dwxddxzi50teg

Guide de configuration

Déployer gtoken-webhook

  1. Pour déployer le serveur gtoken-webhook, il faut créer un service webhook et un déploiement dans notre cluster Kubernetes. C'est plutôt simple, à un détail près : la configuration TLS du serveur. Si vous prenez le temps d'examiner le fichier deployment.yaml, vous constaterez que le certificat et la clé privée correspondante sont lus depuis les arguments de ligne de commande, et que le chemin de ces fichiers provient d'un volume mount pointant vers un Kubernetes secret :

https://gist.github.com/b9fc2fe5acb556e3b48ee89af7db368e

Il est essentiel de bien penser à définir le certificat CA correspondant plus tard dans la configuration du webhook, afin que l'apiserver sache qu'il doit l'accepter. Pour l'instant, nous allons réutiliser le script écrit à l'origine par l'équipe Istio pour générer une demande de signature de certificat. Nous enverrons ensuite la requête à l'API Kubernetes, récupérerons le certificat, puis créerons le secret requis à partir du résultat.

Commencez par exécuter le script webhook-create-signed-cert.sh et vérifiez que le secret contenant le certificat et la clé a bien été créé :

https://gist.github.com/91d2bd8cddf116aa0fa8cefbf3208bcd

Une fois le secret créé, on peut passer à la création d'un déploiement et d'un service. Il s'agit de ressources Kubernetes deployment et service tout à fait standard. À ce stade, nous n'avons rien produit d'autre qu'un serveur HTTP qui accepte des requêtes via le service sur le port 443 :

https://gist.github.com/34ec56018700a610f37a09a9e846eecb

Configurer le Mutating Admission webhook

Maintenant que notre serveur webhook tourne, il peut accepter des requêtes de l'apiserver. Il reste cependant à créer quelques ressources de configuration côté Kubernetes. Commençons par notre validating webhook ; nous configurerons le mutating webhook ensuite. Si vous regardez la configuration du webhook, vous remarquerez qu'elle contient un placeholder pour CA_BUNDLE :

https://gist.github.com/d40101fba8203cc77d62629dde4d6a8f

Un petit script remplace le placeholder CA_BUNDLE de la configuration par cette CA. Exécutez cette commande avant de créer la configuration du validating webhook :

https://gist.github.com/5e0ed44892fc7a21709ac18b381cb5f6

Créez une configuration de mutating webhook :

https://gist.github.com/a545e146e1168f3b88d04b3f4c490d68

Configurer le RBAC pour gtoken-webhook

Créez le Kubernetes Service Account à utiliser avec gtoken-webhook :

https://gist.github.com/aabfa30bc005ae8654b360814523c9bc

Définissez les permissions RBAC du service account du webhook :

https://gist.github.com/5ceb8a6e1ca19d9be3b8ed2d810ddc96

Variables du flux

Certaines variables ci-dessous doivent être renseignées par l'utilisateur ; d'autres sont générées automatiquement et réutilisées dans les étapes suivantes.

  • PROJECT_ID - ID du projet GCP (fourni par l'utilisateur)
  • CLUSTER_NAME - nom du cluster GKE (fourni par l'utilisateur)
  • GSA_NAME - nom du Google Cloud Service Account (fourni par l'utilisateur)
  • GSA_ID - ID unique du Google Cloud Service Account (généré par Google)
  • KSA_NAME - nom du Kubernetes Service Account (fourni par l'utilisateur)
  • KSA_NAMESPACE - namespace Kubernetes (fourni par l'utilisateur)
  • AWS_ROLE_NAME - nom du rôle AWS IAM (fourni par l'utilisateur)
  • AWS_POLICY_NAME - une policy AWS IAM à attribuer au rôle IAM (fournie par l'utilisateur)
  • AWS_ROLE_ARN - identifiant ARN du rôle AWS IAM (généré par AWS)

Google Cloud : activer GKE Workload Identity

Créez un nouveau cluster GKE avec Workload Identity activé :

https://gist.github.com/0fcbc309cd1967edac19bac6795d651d

ou mettez à jour un cluster existant :

https://gist.github.com/05a94c349aa4a274fc27daae45448be9

Google Cloud : créer un Google Cloud Service Account

Créez un Google Cloud Service Account :

https://gist.github.com/1790d9e61c63123937d01c8146c1a67d

Mettez à jour le Google Service Account GSA_NAME avec les rôles suivants :

  • roles/iam.workloadIdentityUser - usurper l'identité de comptes de service depuis les workloads GKE
  • roles/iam.serviceAccountTokenCreator - usurper l'identité de comptes de service pour créer des access tokens OAuth2, signer des blobs ou signer des tokens JWT

https://gist.github.com/19570300e584798d4d5c3a234ceb0d62

AWS : créer un rôle AWS IAM avec Google OIDC Federation

Préparez un document de trust policy de rôle pour le fournisseur OIDC Google :

https://gist.github.com/d80514354a5270bd0f6766dee7947902

Créez le rôle AWS IAM avec Google Web Identity :

https://gist.github.com/94f0ce213cf60cfce6ac987af51afbed

Attribuez les policies souhaitées au rôle AWS :

https://gist.github.com/afd9ecc776cfba5148137769aaf71a5f

Récupérez l'ARN du rôle AWS à utiliser dans l'annotation du SA K8s :

https://gist.github.com/d407769dffd4eb276ed22f7b723cbdc4

GKE : créer un Kubernetes Service Account

Créez le namespace K8s :

https://gist.github.com/59dafb6baf1223552eb7d9fc3c3cbea3

Créez le Kubernetes Service Account :

https://gist.github.com/58fd6924e79270857f39d924a0f12011

Annotez le Kubernetes Service Account avec GKE Workload Identity (email du Service Account GCP) :

https://gist.github.com/0be380a02ff228a38b2a9e5400b75dcb

Annotez le Kubernetes Service Account avec l'ARN du rôle AWS :

https://gist.github.com/01620978944525a8df6c3d76ddc1fb67

Lancer la démo

Lancez un nouveau Pod K8s avec le Service Account K8s ``${KSA_NAME}``` :

https://gist.github.com/9c5a30f5dd3825d5c3bc6adc14b76fbc

Références externes


En résumé

J'espère que cet article vous sera utile. N'hésitez pas à partager vos commentaires et vos questions.

Envie d'en lire davantage ? Consultez notre blog, ou suivez Alexei sur Twitter.