Im Rahmen meiner neuen Rolle als Sicherheitschef bin ich dafür verantwortlich, die Sicherheit von Anmeldedaten und AWS-Konten sicherzustellen.

Heute möchte ich Ihnen eine der bewährten Techniken für den Schutz Ihres AWS-Kontos vorstellen: die Rotation von Schlüsseln. Laut allgemein gültigen Richtlinien benötigen Sie mindestens alle 90 Tage einen neuen Schlüssel - häufiger, wenn möglich -, sodass im Falle eines Datenlecks Anmeldedaten bereits abgelaufen wären.

Sie sollten so viele Rollen wie möglich für Ihre AWS-Instanzen verwenden und API-Schlüssel so wenig wie möglich einsetzen. In Nuxeo befinden sich einige Jenkins-Instanzen jedoch außerhalb von AWS und es ist nach wie vor nützlich, von Zeit zu Zeit einige AWS-APIs über den eigenen Laptop aufrufen zu können.

Vor Einführung neuer Sicherheitsrichtlinien bei Nuxeo versuche ich, mich in die Situation meiner Kollegen hineinzuversetzen und ihnen den Alltag so einfach wie möglich zu machen. Für das oben beschriebene Szenario entschied ich mich also, nach einer Lösung sowohl für die Shell- als auch die Jenkins-Schlüsselrotation zu finden.

Die Rotation von Schlüsseln selbst ist ein sehr einfacher Prozess. Er umfasst folgende Schritte:

  1. Erstellung eines neuen AWS-Schlüssels
  2. Speichern des neuen AWS-Schlüssels
  3. Löschen des alten AWS-Schlüssels

AWS CLI

Es ist immer nützlich, bestimmte Object ACL auf dem eigenen Computer prüfen zu können, wenn jemand Sie bittet, ihm oder ihr mit einem Problem mit dem eigenen S3 Object zu helfen.

aws --profile=nuxeo s3api get-object-acl --bucket MyBucket --key MyObject

Es wäre extrem nützlich, wenn ich den Zugangsschlüssel mithilfe eines einfachen Befehls wie dem folgenden dynamisch zuweisen könnte:

aws --profile=nuxeo configure rotate-keys

Leider gibt es diesen Befehl noch nicht. Die Pull-Abfrage wurde hier durchgeführt und es wird auf die Zusammenführung gewartet (wenn Sie alle +1 hinzufügen, beschleunigt das vielleicht den Zusammenführungsprozess :) )

Beim Warten habe ich ein weiteres AWS-Dienstprogramm erstellt: yaws.

Es ist auf pip verfügbar.

pip install yaws

Es ermöglicht Ihnen die Rotation von Schlüsseln, indem Sie Folgendes eingeben:

yaws --profile=nuxeo rotate-keys

Und schon wurden die Schlüssel aktualisiert! Sie können AWS weiterverwenden und müssen lediglich einen Crontab-Befehl eingeben.

0 0 * * * yaws --profile=nuxeo rotate-keys

Und das war's schon - Ihr Schlüssel wird jetzt jede Stunde dynamisch neu zugewiesen, ohne dass Sie es merken.

Jenkins

Jenkins muss meist auf die Cloud zugreifen, beispielsweise, um Artefakte an einen S3-Bucket zu pushen oder ein Ansible-Skript auszuführen.

Mit dem Plugin "aws-credentials-plugin" wird die Schlüsselspeicherung für Sie übernommen und auch die Aktion "assumeRole" ausgeführt, wenn Sie "roleArn" festlegen.

So können Sie einen einzigen AccessKey nutzen, der unterschiedlichen AWS-Jenkins-Anmeldedaten mit verschiedenen Rollen zugeordnet ist.

Diese AccessKeys müssen wir jedoch weiter rotieren und alle Anmeldedaten aktualisieren, die diese Schlüssel nutzen.

Hierfür können wir wie folgt ein Groovy-Skript einsetzen:

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}")
            }
        }
    }
}

Für das Skript ist es erforderlich, dass Sie Jenkins-Administrator sind und die Anwendung nicht in der Groovy-Sandbox ausführen.

Jetzt müssen Sie lediglich eine geplante Aufgabe in Jenkins erstellen und schon sind Sie fertig.

HashiCorp Vault

Neben den heute vorgestellten Lösungsmöglichkeiten gibt es auch andere Ansätze - wie beispielsweise HashiCorp Vault -, mit denen Sie kurzlebige Anmeldeschlüssel erstellen können. Diese werde ich Ihnen höchstwahrscheinlich in einem meiner künftigen Blogbeiträge vorstellen.