Cross-Account Freigabe von Daten mit AWS Secrets Manager und KMS

September 21, 2020

In komplexen Architekturen mit AWS steht man früher oder später meist vor der Notwendigkeit bestimmte Daten aus einem AWS Account mit einem anderen Account zu teilen; oder auch innerhalb eines AWS Accounts über mehrere Regionen zu teilen bzw. den Zugriff darauf zu ermöglichen. Typische Fälle sind beispielsweise Zugangsdaten für Datenbankzugriffe oder Amazon Resource Names (ARNs) um Ressourcen identifizieren, z.B. Lambda Funktionen oder SSL Zertifikate, da diese für die Verwendung mit CloudFront zwingend in die Region us-east-1 deployt werden müssen.

Ein bewährtes Muster für die Verwaltung solcher Daten ist meist der Parameter Store des AWS Systems Manager. Auf dort gespeicherte Daten lässt sich einfach mit dem AWS SDK zugreifen oder direkt innerhalb von CloudFormation auf die Werte referenzierten. Letzteres funktioniert jedoch nur, so lange diese in der identischen Region innerhalb des identischen AWS Accounts gespeichert sind.

Eine Alternative für den Parameter Store ist der AWS Secrets Manager. Anders als beim Parameter Store, kann auf die gespeicherten Daten im Secrets Manager auch von CloudFormation aus account- und regionübergreifend zugegriffen werden. Mit Hilfe der dynamic references lassen sich mit der Syntax {{resolve:secretsmanager:arn...}} gespeicherte Informationen referenzieren. Nutzt der Parameter Store lediglich den Namen des Parameters zur Identifizierung, unterstützt der Secrets Manager die komplette ARN zur Referenz und ermöglicht so den accountübergreifenden Zugriff.

Beispiel mit CloudFormation

Das folgende Beispiel setzt zwei AWS Accounts voraus; in dem provider genannten Account wird eine Information im AWS Secrets Manager gespeichert auf die der consumer genannte Account zugreifen soll. Damit der Konsument auf die Informationen zugreifen kann, muss sowohl der Datensatz im Secrets Manager mit entsprechenden IAM Berechtigung versehen werden, wie auch der KMS Schlüssel, der zur Absicherung der Daten genutzt wird.

Ein CloudFormation Template mit dieser Konfiguration für den provider kann beispielsweise wie folgt aussehen:

AWSTemplateFormatVersion: 2010-09-09

Resources:
  Key:
    Type: AWS::KMS::Key
    Properties:
      KeyPolicy:
        Version: 2012-10-17
        Statement:
          - Action:
              - kms:Decrypt
            Effect: Allow
            Principal:
              AWS: !Sub arn:aws:iam::${ConsumerAccountID}:root
            Resource: "*"
          - Effect: Allow
            Action:
              - kms:*
            Resource: "*"
            Principal:
              AWS: !Sub arn:aws:iam::${AWS::AccountId}:root
    UpdateReplacePolicy: Delete
    DeletionPolicy: Delete

  Secret:
    Type: AWS::SecretsManager::Secret
    Properties:
      Name: my-secret-string
      KmsKeyId: !GetAtt Key.Arn
      SecretString: superluminar.io

  SecretPolicy:
    Type: AWS::SecretsManager::ResourcePolicy
    Properties:
      SecretId: !Ref Secret
      ResourcePolicy:
        Version: 2012-10-17
        Statement:
          - Resource: "*"
            Action:
              - secretsmanager:GetSecretValue
            Effect: Allow
            Principal:
              AWS: !Sub arn:aws:iam::${ConsumerAccountID}:root

Parameters:
  ConsumerAccountID:
    Type: String
    Default: 123456789000

Aus dem AWS Accounts des Konsumenten kann nun auf die Daten im provider Account zugegriffen werden. Einzig die ARN der Information im Secrets Manager ist hierzu notwendig. Das folgende CloudFormation Template zeigt den Zugriff am Beispiel mit dem AWS SDK innerhalb einer Lambda Funktion.

AWSTemplateFormatVersion: 2010-09-09
Transform: AWS::Serverless-2016-10-31

Resources:
  Function:
    Type: AWS::Serverless::Function
    Properties:
      Runtime: nodejs12.x
      Handler: index.handler
      InlineCode: !Sub |-
        const AWS = require('aws-sdk')
        const SecretsManager = new AWS.SecretsManager();

        exports.handler = async () => {
          return SecretsManager.getSecretValue({ SecretId: '${SecretArn}'} ).promise()
        };
      Policies:
        - Statement:
            - Sid: SecretsManagerGetValue
              Effect: Allow
              Action:
                - secretsmanager:GetSecretValue
              Resource: "*"
            - Sid: KMSAccessKey
              Effect: Allow
              Action:
                - kms:Decrypt
              Resource: "*"

Parameters:
  SecretArn:
    Type: String
    Default: arn:aws:secretsmanager:eu-west-1:98765432100㊙️my-secret-string

Neben dem Zugriff mit dem AWS SDK, ist das Referenzieren der Information auch direkt mit CloudFormation durch die dynamic references möglich. Beispielsweise kann so der Wert aus dem Secrets Manager direkt an eine Umgebungsvariable der Lambda Funktion gesetzt werden:

AWSTemplateFormatVersion: 2010-09-09
Transform: AWS::Serverless-2016-10-31

Resources:
  Function:
    Type: AWS::Serverless::Function
    Properties:
      Runtime: nodejs12.x
      Handler: index.handler
      InlineCode: !Sub |-
        exports.handler = async () => {
          return process.env.SECRET
        };
      Environment:
        Variables:
          SECRET: "{{resolve:secretsmanager:arn:aws:secretsmanager:eu-west-1:98765432100㊙️my-secret-string}}"
      Policies:
        - Statement:
            - Sid: SecretsManagerGetValue
              Effect: Allow
              Action:
                - secretsmanager:GetSecretValue
              Resource: "*"
            - Sid: KMSAccessKey
              Effect: Allow
              Action:
                - kms:Decrypt
              Resource: "*"

Hinweis: Die Verwendung der Information innerhalb einer Umgebungsvariable führt dazu, dass die Daten als Klartext in der AWS Management Console zu sehen sind. Ebenfalls sollte ein Zugriff nicht einem gesamten AWS Account mit arn:aws:iam::${ConsumerAccountID}:root gestattet werden. Beides sollte nicht produktiv für sensible Informationen genutzt werden, die Beispiele dienen nur der einfachen Veranschaulichung der Möglichkeiten mit dynamic references.

photo of Sebastian

Sebastian is a Senior Cloud Consultant at superluminar GmbH, AWS Serverless Hero and AWS Certified Solutions Architect. He writes here in German about AWS, Serverless, Software development, Go, TypeScript and React. English Articles can be found on his Website sbstjn.com and he can be found on Twitter under @sbstjn.