Cross-Account Freigabe von Daten mit AWS Secrets Manager und KMS
September 21, 2020In 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.