Dans le cadre de mon nouveau poste en tant que Head of Security, je suis responsable des identifiants de connexion et de la sécurité AWS.
Aujourd’hui, je vais vous parler d’une bonne pratique en matière de sécurité AWS qui s’appelle la rotation des clés. L’idée est de modifier ses clés au moins tous les 90 jours, et plus souvent si possible, pour qu’en cas de fuite de données, les informations d’identification soient déjà expirées.
Dans la mesure du possible, nous sommes supposés utiliser des rôles pour nos instances AWS et réduire l’utilisation des clés d’API. Cependant, chez Nuxeo, certains serveurs Jenkins sont en dehors d’AWS et il est toujours pratique de pouvoir exécuter des API AWS à partir de votre ordinateur portable.
Avant de mettre en place une politique de sécurité chez Nuxeo, j’aime bien me mettre à la place des différents utilisateurs pour faciliter leur travail. Pour le scénario ci-dessus, j’ai donc décidé de trouver une solution pour la rotation des clés Shell et Jenkins.
La rotation des clés est un processus très simple. Les étapes sont les suivantes :
1. Création d’une nouvelle clé AWS 2. Stockage de la nouvelle clé AWS 3. Suppression de l’ancienne clé AWS
CLI AWS
C’est toujours pratique de pouvoir vérifier la liste de contrôles d’accès (ACL) d’un objet sur votre ordinateur lorsque quelqu’un vous demande de l’aider à comprendre le problème qu’il/elle rencontre avec son objet S3.
aws --profile=nuxeo s3api get-object-acl --bucket MyBucket --key MyObject
L’idéal serait de pouvoir changer la clé d’accès en utilisant une seule commande comme celle-ci :
aws --profile=nuxeo configure rotate-keys
Mais malheureusement, cette commande n’existe pas encore. La Pull request a été lancée ici et elle attend d’être mergée (peut-être que si tout le monde ajoute un +1, le processus ira plus vite :) )
Mais en attendant, j’ai créé un autre utilitaire AWS : yaws
Il est disponible sur pip
pip install yaws
Et vous permet de modifier la clé en entrant :
yaws --profile=nuxeo rotate-keys
Les clés sont mises à jour ! Vous pouvez continuer à utiliser votre AWS et il vous suffit d’ajouter une table de planification.
0 0 * * * yaws --profile=nuxeo rotate-keys
Et voilà. Vos clés sont modifiées toutes les heures et vous n’avez plus rien à faire.
Jenkins
Jenkins doit généralement réaliser certaines opérations dans le Cloud, par ex. envoyer un artéfact vers un bucket S3 ou exécuter un script Ansible.
Il existe un plug-in aws-credentials-plugin
qui gère le stockage des clés à votre place et qui prend en charge l’opération assumeRole
si vous spécifiez un paramètre roleArn
.
De cette façon, vous pouvez disposer d’une seule clé d’accès associée à plusieurs accès Jenkins AWS avec plusieurs rôles.
Mais nous devons encore ajouter la rotation de ces clés d’accès et mettre à jour toutes les clés correspondantes.
Pour y parvenir, on peut par exemple utiliser un script Groovy comme celui-ci :
import com.cloudbees.jenkins.plugins.awscredentials.AWSCredentialsImpl;
import com.cloudbees.plugins.credentials.domains.*;
node {
// Load all the AWSCredentials from Jenkins
def creds = com.cloudbees.plugins.credentials.CredentialsProvider.lookupCredentials(
com.cloudbees.jenkins.plugins.awscredentials.AWSCredentialsImpl.class,
jenkins.model.Jenkins.instance
)
// Create a map of AccessKey to Credentials attached to this accessKeys
def rotate = [:]
// Building the map
creds.each {
if (!rotate[it.accessKey]) {
rotate[it.accessKey] = []
}
rotate[it.accessKey].add(it)
}
// For each accessKey
rotate.each {
// Rotate the access key
accessKey = it.value[0].accessKey
secret = it.value[0].secretKey.getPlainText()
withEnv(["AWS_ACCESS_KEY_ID=${accessKey}","AWS_SECRET_ACCESS_KEY=${secret}"]) {
newKeyRaw = sh(returnStdout: true, script: "${aws_bin} iam create-access-key").trim()
newKey = readJSON(text: newKeyRaw)
sh(script: "${aws_bin} iam delete-access-key --access-key-id=${accessKey}")
println("Rotate key ${accessKey} to ${newKey.AccessKey.AccessKeyId}")
}
// Now that the accessKey has been rotated on AWS, we need to update all credentials using it
def credentials_store = jenkins.model.Jenkins.instance.getExtensionList(
'com.cloudbees.plugins.credentials.SystemCredentialsProvider'
)[0].getStore()
it.value.each {
cred = it
domain = null
// Search domain for this credentials : not optimal
credentials_store.getDomains().each {
curDomain = it
credentials_store.getCredentials(it).each {
if (it == cred) {
domain = curDomain
}
}
}
if (curDomain) {
newIt = new AWSCredentialsImpl(it.scope, it.id, newKey.AccessKey.AccessKeyId, newKey.AccessKey.SecretAccessKey, it.description, it.iamRoleArn, it.iamMfaSerialNumber)
credentials_store.updateCredentials(curDomain, it, newIt)
} else {
println("WARNING: key ${it.id} (${it.AccessKey} failed to be update with ${newKey.AccessKey.AccessKeyId}")
}
}
}
}
Pour exécuter ce script, vous devez être administrateur Jenkins et ne pas l’exécuter dans le bac à sable Groovy.
Il ne vous reste plus qu’à créer une tâche planifiée sur Jenkins !
Vault HashiCorp
Il existe d’autres approches que celles que je viens de vous présenter, par ex. un Vault HashiCorp, qui peut générer des accès jetables pour vous. Ça sera probablement le sujet d’un prochain blog.
Apprenez tout sur Nuxeo et AWS dans ce guide.