Comment automatiser la rotation des clés d’accès AWS
Spoiler alert : cet article traite de l’automatisation de la rotation des clés d’accès AWS et des bonnes pratiques à appliquer sur la base d’une durée de vie des clés définie au préalable.
Access Keys Rotation. En contemplant l’âge avancé des clés chez un client (voir ci-dessous), ces trois mots se répétaient en boucle dans ma tête comme un mantra. Si seulement avec quelques incantations en plus, je pouvais arriver à mes fins…
Un peu de contexte…
Sur AWS, la gestion des identités et des accès passe par le service AWS IAM qui permet de créer des utilisateurs, les affecter à des groupes, octroyer des permissions, etc… Bien que les bonnes pratiques recommandent l’utilisation de rôles pour endosser des permissions nécessaires pour interagir avec les API d’AWS, parfois l’usage de clés d’accès s’avère nécessaire. Par exemple, dans les cas suivants :
- Accès aux ressources AWS depuis l’extérieur via d’autres d’applications ou comptes de service
- Intégration avec des outils ou des services tiers qui nécessitent un accès aux API AWS
- Exécution de tâches administratives qui ne peuvent pas être effectuées via la console de gestion AWS
Les clés d’accès ou access keys sont des identifiants à long terme qui référencent un utilisateur IAM. Si endosser un rôle, c’est montrer patte blanche avec la bonne casquette qui va bien, posséder des access keys c’est avoir le double des clés du magasin. Le décor étant planté, il convient tout de même de renouveler les access keys régulièrement conformément aux bonnes pratiques de sécurité. On n’est jamais à l’abri d’une clé qui s’égare et dans ce cas, il vaudrait mieux pour mon compte que cette clé ne soit plus utilisable.
Heureusement, avec AWS, changer les serrures n’aura jamais été aussi facile. En quelques clics dans la console (ou quelques commandes dans la CLI), voilà que j’ai généré de nouvelles clés et révoqué les précédentes. Toujours est-il que la tâche peut très vite s’avérer fastidieuse si j’ai des dizaines d’utilisateurs et de clés dans mon compte. Cela s’accentue d’autant plus si j’ai des dizaines de comptes dans mon organisation et que c’est moi, le seul et l’unique, l’admin tout-puissant, qui dois m’en charger (avec mon petit stagiaire).
Concrètement, c’est quoi le projet ?
L’idée est de s’accorder sur une “durée de vie” des clés. Sachant que chaque utilisateur IAM peut avoir au plus deux Access Keys à la fois, la rotation consiste tout bonnement à en créer une nouvelle peu de temps avant la désactivation, puis suppression de la précédente.
Il n’existe pas de règle standard pour la bonne durée de vie d’un secret (mot de passe, clé…). Il faut être ni trop long (sinon tant qu’à faire, gardons les pour toujours) ni trop court (sinon c’est vite énervant). Chaque organisation doit donc s’accorder sur la durée qui leur semble la plus pertinente face à ses enjeux et inscrire ses conclusions dans la PSSI (Politique de Sécurité des Systèmes d’Information).
Dans notre cas, nous allons tabler sur 180 jours (vous me direz si c’est raisonnable). Passé ce délai, plus aucune clé ne doit être utilisable. Maintenant que nous avons défini cette échéance, on peut regarder de plus près quel sera notre workflow pour mettre en place la rotation des clés.
Des clés pas très utilisées
Il n’est pas rare que des clés soient créées à la volée et qu’on se rende compte ultérieurement qu’elles ne sont pas ou plus nécessaires pour diverses raisons : cas de test, projet court, abandonné ou décommissionné, un Owner qui a quitté l’entreprise, etc.
Dans ce cas, soit on corrige le tir, soit on laisse tel quel. Accordons-nous donc le bénéfice du doute. Si au bout de 45 jours consécutifs, aucune utilisation n’en est faite, il serait peut-être judicieux de notifier le propriétaire. De cette manière, il ne sera pas surpris, s’il est toujours dans l’entreprise, de voir sa clé disparaître prochainement.
Le compteur tourne et on approche des 60 jours. Toujours aucune activité ? Il est temps de désactiver la clé. Il sera préférable de prévoir une période de grâce avant les actions définitives : la clé ne sera supprimée qu’au 90ᵉ jour.
Utilisation régulière
Cette fois, j’avais de bonnes raisons. Cette clé, je l’utilise assez fréquemment. Mais voilà, the time has come ! L’Access Key a été créée il y a maintenant 150 jours. Ceci déclenche la création d’une nouvelle Access Key qui sera amenée à remplacer la précédente. Cette nouvelle clé est stockée dans le coffre-fort d’AWS : Secrets Manager avec une policy qui interdit à qui que ce soit en dehors de l’utilisateur concerné, de récupérer le secret.
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Deny",
"Principal": {
"AWS": "arn:aws:iam::{{AccountId}}:role/{{CrossAccountRole}}"
},
"Action": "*",
"Resource": "{{secret_arn}}",
"Condition": {
"StringNotLike": {
"aws:userid": "*:{{username}}"
}
}
}
]
}
On veillera aussi à envoyer régulièrement un reminder à destination des Owners jusqu’au 180ᵉ jour, date de la désactivation. Encore une fois, on conservera quelques jours de latence avant la suppression effective de la clé lorsque celle-ci aura atteint l’âge de 210 jours.
Les exceptions… sinon c’est pas drôle
En supposant qu’il existe des cas où vraiment, ce n’est pas envisageable de supprimer, voire renouveler une Access Key dans le délai imparti (je vous mets au défi de trouver des cas où c’est justifié), dans ce cas, une exception peut être sollicitée (avec tout le processus de validation associé bien sûr).
Concrètement, comment est-ce que cela se matérialise pour nous ? Via un simple tag, rien de plus. Une fois la validation actée, un tag “exception” est appliqué à l’utilisateur concerné avec pour valeur la date de fin de validité. Une fois cette date atteinte, l’utilisateur et sa clé rentrent dans le processus initial décrit plus haut.
Pendant toute la durée de l’exception, aucune action n’est menée sur les clés concernées. Les owners seront tout de même prévenus à intervalles réguliers lorsque l’exception approchera son terme afin de prendre les dispositions nécessaires.
Schéma fonctionnel
Ce workflow peut être décrit par le schéma fonctionnel ci-dessous :
Un résumé des étapes peut se présenter comme suit:
Key age (days) | Last activity (days) | Lifecycle stage | Action (CASE) |
>= 210 | / | 6 | Suppression |
>= 180 | 5 | Désactivation | |
>= 150 | 4 | Renouvellement | |
< 150 | >= 120 | 3 | Suppression |
>= 90 | 2 | Désactivation | |
>= 45 | 1 | Rappel | |
< 45 | 0 | RAS |
Architecture technique
Pour implémenter notre belle solution, on se basera sur une architecture full serverless, facile à implémenter et peu coûteuse.
Tout débute par une eventbridge rule qui fait office de cron job quotidien pour déclencher une première lambda (1).
Cette lambda fait l’inventaire des clés sur l’ensemble des comptes enregistrés (2) et stocke les attributs collectés dans une table DynamoDB (3).
L’exécution réussie de cette lambda déclenche la suivante (4) qui mettra à jour les données variables de la table (5) notamment le statut des clés.
Une fois cette étape passée, une troisième lambda est déclenchée (6) pour mener les actions nécessaires sur les clés et mettre à jour la table en conséquence (7 et 8).
Lorsqu’une nouvelle clé est créée, celle-ci est stockée dans Secrets Manager (9). Une policy est associée à la fois au secret et à l’utilisateur pour lui permettre de le consulter.
Chaque étape du processus nécessitant une notification se fait via SES (10) afin de tenir les utilisateurs informés.
Voici un aperçu de la table DynamoDB :
Field name | Type | Description |
access_key_id | string | ID de l’Access Key |
account_id | decimal | ID du compte de l’utilisateur IAM |
account_name | string | Nom du compte de l’utilisateur IAM |
contact | string | Email de l’owner (valeur du tag “contact”) |
create_date | string | Date de création de l’Access Key |
exception | decimal | Nombre de jours avant la fin de l’exception (décompte négatif) |
key_age_days | decimal | Age de la clé en nombre de jours |
last_activity_days | decimal | Nombre de jours depuis la dernière utilisation |
last_used_date | string | Date de la dernière utilisation |
lifecycle_stage | decimal | Étape du cycle de vie (cf. tableau précédant) |
notification_count | decimal | Indice de notification (mail envoyé si Indice % SPAM_DELAY == 0) |
replacement_key | string | ID de l’Access Key créée pour remplacer celle-ci (CASE 4) |
status | string | Statut de l’Access Key |
user | string | Nom d’utilisateur IAM détenant l’Access Key |
PS : le contenu de cette table peut faire l’objet d’un export quotidien vers S3 dans le but d’alimenter un dashboard de suivi de l’état des Access Keys.
Disclaimer : c’est la conclusion !
Durant tout cet article, je me suis placé inconsciemment du point de vue d’un admin, ou du moins de quelqu’un qui a assez de privilèges pour s’amuser avec ses users et ses clés.
Ceci dit, on se rend très vite compte que dans un contexte où les droits sont passés au peigne fin (ce qui devrait normalement être le cas), et qu’il faut tout un process pour demander et valider la création d’un user avec Access Key (en passant par le support, leurs formulaires et leur réactivité, sans parler du device physique à associer au MFA), et le cachet du CISO, tout ça pour dire que la gestion des users et des clés, ce n’est pas ce qu’il y a de plus drôle pour tout le monde. Donc, autant l’automatiser.
Autre chose. On pourrait imaginer un service basé sur Identity Center et une gestion des utilisateurs aux petits oignons, SSO login via CLI et autres features… Pour l’avoir utilisé, c’est plutôt pratique et très bien pensé. Malheureusement, il y a quelques années, quand ces features n’existaient pas encore, certaines organisations ont pris de mauvaises habitudes dont on subit encore les conséquences aujourd’hui. Vous y avez certainement pensé, ce projet de rotation automatique des Access Keys a fait l’objet d’un PoC par AWS (voici le lien l’article : Automatically rotate IAM user access keys at scale with AWS Organizations and AWS Secrets Manager). Il serait intéressant d’en faire une fonctionnalité native (une bouteille à la mer…👀).
Je vous laisse sur cette question concernant Secrets Manager : selon vous, est-il préférable de stocker les clés dans un compte de management ou alors au plus près de l’utilisateur dans le même compte que celui-ci ?