Au travers de cet article nous allons traiter une problématique commune à beaucoup de projets cloud AWS. Elle peut se traduire par l’exigence suivante : “En tant que responsable sécurité, je veux que la production soit isolée et accessible uniquement par les personnes autorisées tout en garantissant l’autonomie des développeurs”.
Pour paraphraser, il est capital que la production soit séparée de l’instabilité du développement et que son accès soit soumis à autorisation. Côté développement il est important que les développeurs disposent de droits suffisants pour mener à bien leurs activités.
Je vous propose de découvrir une organisation typique des comptes AWS permettant de développer sereinement une application et de l’exploiter en production, tout en ayant un niveau de ségrégation des responsabilités élevé.
Plusieurs comptes
Pour garantir cette ségrégation nous allons créer trois comptes AWS :
-
Compte parent : il héberge les utilisateurs IAM et la facture consolidée
-
Compte de développement : ici se trouvent les ressources nécessaires pour développer, qualifier et industrialiser l’application
-
Compte de production : ressources de production
Notez bien que l’article ne couvre pas le cas de fédération d’identité et se concentre uniquement sur les utilisateurs IAM.
Compte Parent
Il a pour vocation de recenser les utilisateurs IAM qui vont utiliser la plateforme AWS pour développer ou exploiter l’application. En général, il ne contient aucune ressource hormis les utilisateurs.
Chaque personne (développeur, exploitant, testeur, …) dispose d’un compte (login, mot de passe) lui donnant les droits d’accéder à AWS. La liste des actions qu’il peut réaliser sur ce compte est très restreinte. Il peut généralement se gérer lui-même (mot de passe, MFA, clés SSH) et accéder aux comptes de développement et de production.
En guise d’exemple, voici la policy IAM que j’utilise pour que les utilisateurs puissent gérer uniquement leur compte IAM. À savoir, modifier leur mot de passe, gérer le MFA et leurs clés SSH.
{ "Version": "2012-10-17", "Statement": [ { "Sid": "List", "Effect": "Allow", "Action": [ "iam:GetAccountPasswordPolicy", "iam:ListMFADevices", "iam:ListUsers", "iam:ListVirtualMFADevices" ], "Resource": [ "*" ] }, { "Sid": "ModifyItself", "Effect": "Allow", "Action": [ "iam:ChangePassword", "iam:CreateAccessKey", "iam:CreateLoginProfile", "iam:DeleteAccessKey", "iam:DeleteLoginProfile", "iam:GetLoginProfile", "iam:GetUser", "iam:UpdateAccessKey", "iam:UpdateLoginProfile", "iam:UpdateUser", "iam:ListSSHPublicKeys", "iam:GetSSHPublicKey" ], "Resource": [ "arn:aws:iam::*:user/${aws:username}" ] }, { "Sid": "AddMFA", "Effect": "Allow", "Action": [ "iam:CreateVirtualMFADevice", "iam:EnableMFADevice", "iam:ResyncMFADevice" ], "Resource": [ "arn:aws:iam::*:user/${aws:username}", "arn:aws:iam::*:mfa/${aws:username}" ] }, { "Sid": "DeleteMFA", "Effect": "Allow", "Action": [ "iam:DeactivateMFADevice", "iam:DeleteVirtualMFADevice" ], "Resource": [ "arn:aws:iam::*:user/${aws:username}", "arn:aws:iam::*:mfa/${aws:username}" ] } ] }
En plus des utilisateurs, ce compte porte la notion de paiement et de facture consolidée. Cela permet de ne renseigner qu’une seule fois son numéro de carte bancaire et de centraliser les dépenses. En d’autres termes, les autres comptes sont rattachés à ce dernier et le paiement est porté par le compte parent. Si vous souhaitez que vos utilisateurs puissent accéder au “dashboard billing”, ajoutez cette policy aux groupes idoines :
{ "Version": "2012-10-17", "Statement": [ { "Sid": "ViewBilling", "Effect": "Allow", "Action": [ "aws-portal:ViewAccount", "aws-portal:ViewBilling", "aws-portal:ViewUsage" ], "Resource": [ "*" ] } ] }
Comme évoqué plus haut, le compte parent est le seul point d’entrée pour vos utilisateurs. C’est à partir de lui qu’ils accéderont aux autres comptes. Pour ce faire, ils devront assumer un rôle dans le compte cible qui leur permettra alors d’effectuer les tâches souhaitées (créer des instances, lancer une lambda …)
Détaillons le schéma :
1 – Bob dispose d’un utilisateur IAM sur le compte parent. Cet utilisateur est dans un groupe dont une des policies permet d’assumer un rôle sur le compte “PRODUCTION”
{ "Version": "2012-10-17", "Statement": { "Effect": "Allow", "Action": "sts:AssumeRole", "Resource":"arn:aws:iam::<ID_COMPTE_CIBLE>:role/<ROLE>" } }
2 – Le compte PRODUCTION comporte différents rôles permettant d’effectuer les opérations nécessaires. Par exemple le rôle “ops” a le droit de créer et détruire des ressources tandis que le rôle “dev” ne peut que consulter en lecture les services (lister les instances EC2, voir la configuration des lambdas …) Ces rôles ont la particularité de pouvoir être assumés par des utilisateurs du compte parent.
La configuration “Cross-Account” pour un rôle se fait dans l’onglet “Trust Relationships”
{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Principal": { "AWS": "arn:aws:iam::ID_COMPTE_PARENT:root"}, "Action": "sts:AssumeRole" } ] }
3 – Bob effectue l’opération “assume role” (voir chapitre “Naviguer entre les comptes” pour les détails techniques) qui lui permet d’endosser un rôle dans le compte cible.
4 – Security Token Service (STS) lui renvoie les informations (credentials + token) lui permettant d’accéder au compte cible.
5 – Les appels API utilisent les credentials précédemment retournées.
Compte DEVELOPMENT
Avant de livrer en production, l’application subit un cycle de vie, propre à chaque équipe, mais dont l’essentiel se concentre sur le développement, la qualification et l’industrialisation. Ce compte est dédié à ces activités et constitue le laboratoire pour les équipes de développement. Par commodité, un accès privilégié aux ressources est octroyé (c. à d. avoir le droit de créer et supprimer).
Compte PRODUCTION
Pour être accessible aux utilisateurs finaux, l’application est déployée dans ce compte. Elle est alors totalement isolée des ressources de développement. L’accès au compte PRODUCTION est bien évidemment beaucoup plus restrictif que celui du compte DEVELOPMENT. On pourra par exemple donner l’accès en lecture aux développeurs pour leur faciliter la phase de debug tandis que l’infogérance aura les droits en écriture. Si nécessaire, il est possible de confier temporairement l’accès en écriture aux développeurs pour qu’ils puissent intervenir efficacement lors d’un bug majeur de production. Cet opération se fait en rajoutant l’utilisateur à un groupe dédié ou en plaçant directement la policy lui octroyant les droits sur son compte IAM.
Exemple de policy à rajouter pour donner l’accès à un utilisateur en écriture au compte de PRODUCTION. Cela suppose que le compte cible dispose du rôle “role-ops” portant les autorisations nécessaires :
{ "Version": "2012-10-17", "Statement": { "Effect": "Allow", "Action": "sts:AssumeRole", "Resource":"arn:aws:iam::<ID_COMPTE_PRODUCTION>:role/role-ops" } }
Naviguer entre les comptes
Concrètement quel est l’impact de diviser son projet en plusieurs comptes AWS pour les tâches quotidiennes ? La séparation en 3 comptes n’a que peu de conséquences sur l’utilisation de la plateforme AWS. En effet, la console, le SDK et la CLI supportent le mécanisme “d’assume role”.
Console
La console AWS supporte un seul niveau “d’assume role”. L’accès se situe en haut à droite de la page web, en cliquant sur votre login. La partie basse du menu déroulant liste les rôles déjà endossés. Pour en ajouter un nouveau cliquer sur “Switch Role”.
L’écran qui suit affiche un formulaire permettant de renseigner les informations de connexion :
- Compte : identifiant ou alias du compte cible qui héberge le rôle à endosser
-
Rôle : nom du rôle à assumer dans le compte distant
-
Nom complet : texte à afficher dans la barre en haut à droite
-
Couleur : alliée au nom complet, la couleur permet de visualiser d’un coup d’oeil le compte avec lequel vous êtes connecté. Personnellement je choisis rouge pour la production et vert pour le développement
Quand vous cliquez sur le bouton “Changer de rôle” vous naviguez maintenant sur le compte cible avec les droits conférés au rôle endossé.
Vous pouvez consulter la documentation officielle AWS pour plus de détails.
Command-line interface
Il est bien évidemment possible de réaliser les mêmes opérations à travers la ligne de commande. Au lieu de configurer le profil avec des informations d’identification, vous spécifiez l’ARN du rôle et le nom du profil qui y a accès grâce aux instructions “role_arn” et “source_profile”.
~/.aws/config [xebia-parent] region = eu-west-1 output = json [profile xebia-dev] role_arn=arn:aws:iam::ID_COMPTE_DEVELOPMENT:role/role-ops source_profile=xebia-parent region=eu-west-1 [profile xebia-prod] role_arn=arn:aws:iam::ID_COMPTE_PRODUCTION:role/role-readonly source_profile=xebia-parent region=eu-west-1 ~/.aws/credentials [xebia-parent] aws_access_key_id = AKIAXXXXXX aws_secret_access_key = YXYXYXYXYXYXYXYXYXYXYXYX
Il suffit à présent de préciser le profil à utiliser dans les lignes de commande pour qu’elles s’exécutent sur le compte souhaité. Par exemple, pour lister les buckets S3 du compte xebia-dev :
AWS_PROFILE=xebia-dev aws s3 ls
aws --profile xebia-dev s3 ls
Conclusion
Une organisation des comptes AWS comme celle présentée dans cet article permet de garantir une isolation de la production afin qu’elle ne soit pas impactée par les mouvements incessants de la plateforme de développement.
De plus, cette structuration permet de centraliser les utilisateurs sur le compte parent ce qui facilite grandement l’application des règles de sécurité (politique de mot de passe, onboarding et offboarding de personnes sur le projet etc…). L’obtention ou la révocation des droits se fait grâce à de simples policies IAM qui donnent la possibilité ou non de faire l’opération “sts:AssumeRole”.
Devant la facilité d’utilisation et les avantages procurés, n’ayez pas peur de scinder vos comptes en trois. D’autant plus, tant que le compte parent ne dispose d’aucune ressource, il ne coûte rien :)