Skip to content
~/sph.sh

Mozilla SOPS: GitOps-Native Secret Encryption, die tatsächlich funktioniert

Ein umfassender Leitfaden zu Mozilla SOPS für die Verwaltung verschlüsselter Secrets in Git-Repositories. Lerne age encryption, AWS CDK-Integration, AWS Lambda-Patterns und production-ready Security-Strategien für serverless Workflows.

Abstract

Mozilla SOPS (Secrets OPerationS) löst eine grundlegende Herausforderung in GitOps: Wie kannst du Secrets sicher in die Versionskontrolle committen und gleichzeitig die Developer Productivity aufrechterhalten? Im Gegensatz zu cloud-nativen Secret Stores verschlüsselt SOPS Dateien direkt in Git-Repositories und bewahrt dabei die YAML/JSON-Struktur, während sensible Werte geschützt werden. Dieser Leitfaden behandelt praktische Implementation-Patterns wie age encryption, AWS Lambda-Integration, AWS CDK-Workflows, AWS SAM-Patterns und CI/CD-Automatisierung für serverless Deployments in GitHub Actions, GitLab CI und Jenkins.

Die GitOps Secret Management Challenge

Wenn du mit Infrastructure as Code arbeitest, entsteht sofort ein Problem. Du musst deine serverless Konfigurationsdateien, Terraform-Variablen und Environment-Configs unter Versionskontrolle stellen. Aber diese Dateien enthalten Datenbank-Passwörter, API-Keys und Service-Credentials. In dem Moment, in dem du Secrets in Git commitest, hast du eine Security-Schwachstelle geschaffen.

Traditionelle Lösungen erzeugen Friction. HashiCorp Vault erfordert laufende Infrastruktur und API-Aufrufe zur Deployment-Zeit. AWS Secrets Manager kostet $0.40 pro Secret pro Monat und fügt Runtime-API-Aufrufe zu deinen Lambda-Funktionen hinzu. AWS Systems Manager Parameter Store ist kostenlos, erfordert aber dennoch Runtime-Fetching. Jeder Ansatz zieht Secrets aus deinem GitOps-Workflow heraus und in externe Systeme.

SOPS verfolgt einen anderen Ansatz. Es verschlüsselt Dateien direkt in deinem Git-Repository und hält Secrets versioniert zusammen mit deinem Code. Wenn du einen API-Key änderst und deine Lambda-Funktion aktualisierst, gehen beide Änderungen in denselben Commit. Wenn du einen Rollback machst, werden beide zusammen zurückgesetzt. Deine Git-History wird zu deinem Audit-Trail.

SOPS-Architektur verstehen

SOPS verwendet Envelope Encryption. Wenn du eine Datei verschlüsselst, generiert SOPS einen zufälligen 256-Bit-Data-Key und verschlüsselt deinen Dateiinhalt mit AES256-GCM. Dann verschlüsselt es diesen Data-Key mit einem oder mehreren Master-Keys (AWS KMS, age, PGP, GCP KMS oder Azure Key Vault) und speichert den verschlüsselten Data-Key in den Metadaten der Datei. Age verschlüsselt den SOPS-Data-Key mit X25519 + ChaCha20-Poly1305.

Für strukturierte Formate wie YAML und JSON verschlüsselt SOPS nur die Werte, nicht die Keys. Dies hält deine Dateistruktur für Code-Reviews sichtbar und ermöglicht es Tools, das Schema zu parsen, auch wenn die Werte verschlüsselt sind.

So sieht eine verschlüsselte YAML-Datei aus:

yaml
database:  host: ENC[AES256_GCM,data:Zm9vYmFy,iv:abc123,tag:def456,type:str]  port: ENC[AES256_GCM,data:NTQzMg==,iv:xyz789,tag:uvw012,type:int]  password: ENC[AES256_GCM,data:c3VwZXI=,iv:secret,tag:hash,type:str]sops:  kms:    - arn: arn:aws:kms:us-east-1:123456789012:key/abc-123      created_at: '2025-12-17T10:00:00Z'      enc: AQICAHh...encrypted_data_key...  age:    - recipient: age1ql3z7hjy54pw3hyww5ayyfg7zqgvc7w3j2elw8zmrj2kg5sfn9aqmcac8p      enc: |        -----BEGIN AGE ENCRYPTED FILE-----        YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBl...        -----END AGE ENCRYPTED FILE-----  version: 3.11.0

Du kannst immer noch die Datenbank-Konfigurationsstruktur sehen. Du weißt, dass es Host-, Port- und Password-Felder gibt. Aber die tatsächlichen Werte sind verschlüsselt. Git diff zeigt, welche Felder sich geändert haben, nicht nur dass "der verschlüsselte Blob sich geändert hat".

Installation und Setup

Die Installation variiert je nach Plattform, dauert aber weniger als fünf Minuten:

bash
# macOS mit Homebrewbrew install sops
# Linux - latest Release herunterladenwget https://github.com/getsops/sops/releases/download/v3.11.0/sops-v3.11.0.linux.amd64chmod +x sops-v3.11.0.linux.amd64sudo mv sops-v3.11.0.linux.amd64 /usr/local/bin/sops
# Installation überprüfensops --version

Für Container-Umgebungen verwendest du das offizielle Image:

dockerfile
FROM mozilla/sops:v3.11.0
# Kopiere deine verschlüsselten DateienCOPY secrets.enc.yaml /app/
# Entschlüssele zur RuntimeCMD ["sops", "--decrypt", "/app/secrets.enc.yaml"]

Age: Die moderne Encryption-Wahl

SOPS unterstützt mehrere Key-Management-Systeme. Für Team-Umgebungen hat sich age (ausgesprochen wie "h-age") zur empfohlenen Wahl gegenüber PGP entwickelt.

Age-Public-Keys sind 62 Zeichen, Private-Keys sind 74 Zeichen lang. PGP-Keys sind 4096 Zeichen. Du kannst einen age-Public-Key in Slack kopieren und einfügen. PGP-Keys brechen über mehrere Zeilen. Age verwendet moderne Kryptographie (X25519 + ChaCha20-Poly1305). PGP kommt mit Jahrzehnten an Komplexität aus dem GPG-Keyring-System.

Generiere ein age-Key-Pair:

bash
# Installiere age (v1.2.0 oder neuer)brew install age  # macOSapt install age   # Ubuntu
# Generiere Key Pairage-keygen -o ~/.config/sops/age/keys.txt
# Output zeigt Public Key# Public key: age1ql3z7hjy54pw3hyww5ayyfg7zqgvc7w3j2elw8zmrj2kg5sfn9aqmcac8p

Der Private Key wird in ~/.config/sops/age/keys.txt gespeichert. Der Public Key ist das, was du mit Team-Mitgliedern teilst und in SOPS konfigurierst.

Um eine Datei mit age zu verschlüsseln:

bash
# Setze deinen Private-Key-Standortexport SOPS_AGE_KEY_FILE=$HOME/.config/sops/age/keys.txt
# Verschlüssele mit Public Keysops --age age1ql3z7hjy54pw3hyww5ayyfg7zqgvc7w3j2elw8zmrj2kg5sfn9aqmcac8p \  --encrypt secrets.yaml > secrets.enc.yaml
# Entschlüssele (verwendet Private Key von SOPS_AGE_KEY_FILE)sops --decrypt secrets.enc.yaml > secrets.yaml

Für Team-Distribution generiert jeder seinen eigenen age-Key und teilt seinen Public Key. Du konfigurierst SOPS so, dass es mit allen Public Keys der Team-Mitglieder verschlüsselt. Jeder mit seinem Private Key kann entschlüsseln.

Die .sops.yaml-Konfigurationsdatei

Das Erstellen einer .sops.yaml-Datei im Repository-Root eliminiert manuelles Key-Management. SOPS liest diese Datei, um anhand von Dateipfaden zu bestimmen, welche Keys verwendet werden sollen.

Hier ist eine production-ready Konfiguration:

yaml
creation_rules:  # Development - einfache age-Keys für alle Developer  - path_regex: \.dev\.yaml$    age: >-      age1ql3z7hjy54pw3hyww5ayyfg7zqgvc7w3j2elw8zmrj2kg5sfn9aqmcac8p,      age1cy0su9fwf8gzkdqh3r4r6xgc92fp8jqrjp4fvd4ak6vd3mc0jjpqnhymkw
  # Staging - AWS KMS zum Testen des Production-Flows  - path_regex: \.staging\.yaml$    kms: arn:aws:kms:us-west-2:111111111111:key/staging-key-id    age: age1ql3z7hjy54pw3hyww5ayyfg7zqgvc7w3j2elw8zmrj2kg5sfn9aqmcac8p
  # Production - mehrere KMS-Keys mit Redundanz  - path_regex: \.prod\.yaml$    key_groups:      - kms:          - arn: arn:aws:kms:us-east-1:222222222222:key/prod-key-id            role: arn:aws:iam::222222222222:role/sops-decrypt-role          - arn: arn:aws:kms:eu-west-1:222222222222:key/prod-key-eu        age:          - age1yx3z8r0hnzjy9wh6fq5gldq3p7hxg6nfkz5vgqcdqhsj8tqxj8xq8w6qur
  # Serverless Secrets - AWS KMS  - path_regex: serverless/.*\.yaml$    kms: arn:aws:kms:us-east-1:222222222222:key/serverless-key-id
  # Terraform Variables - für Infrastruktur  - path_regex: terraform/.*\.tfvars$    kms: arn:aws:kms:us-east-1:222222222222:key/terraform-key-id
  # Default-Fallback für nicht übereinstimmende Dateien  - age: age1ql3z7hjy54pw3hyww5ayyfg7zqgvc7w3j2elw8zmrj2kg5sfn9aqmcac8p

Jetzt wird die Verschlüsselung automatisch:

bash
# SOPS liest .sops.yaml, um Keys zu findensops --encrypt config.dev.yaml > config.dev.enc.yaml     # Verwendet age-Keyssops --encrypt config.staging.yaml > config.staging.enc.yaml  # Verwendet AWS KMSsops --encrypt config.prod.yaml > config.prod.enc.yaml  # Verwendet KMS + age

Die pfadbasierten Regeln eliminieren menschliche Fehler. Developer müssen sich nicht merken, welche Keys für welche Umgebung verwendet werden.

AWS KMS-Integration

Für Production-Umgebungen bietet AWS KMS zentralisiertes Key-Management mit IAM-basierter Zugriffskontrolle und Audit-Logging über CloudTrail.

Erstelle einen KMS-Key:

bash
# Erstelle KMS-Key für SOPSaws kms create-key \  --description "SOPS encryption key for production" \  --key-usage ENCRYPT_DECRYPT
# Erstelle Alias für einfachere Referenzaws kms create-alias \  --alias-name alias/sops-production \  --target-key-id <key-id-from-previous-command>
# Hole die Key-ARNaws kms describe-key --key-id alias/sops-production

Konfiguriere SOPS für KMS-Verwendung:

bash
export SOPS_KMS_ARN="arn:aws:kms:us-east-1:123456789012:key/abc-123-def"
# Verschlüssele Dateisops --kms $SOPS_KMS_ARN --encrypt secrets.yaml > secrets.enc.yaml

Für CI/CD-Umgebungen verwende IAM-Rollen statt Credentials zu speichern:

yaml
# GitHub Actions mit OIDC- name: Configure AWS Credentials  uses: aws-actions/configure-aws-credentials@v5  with:    role-to-assume: arn:aws:iam::123456789012:role/GitHubActionsSOPS    aws-region: us-east-1
- name: Entschlüssele mit KMS  run: sops --decrypt secrets.enc.yaml > secrets.yaml

Die IAM-Rolle benötigt KMS-Decrypt-Berechtigungen:

json
{  "Version": "2012-10-17",  "Statement": [{    "Effect": "Allow",    "Action": [      "kms:Decrypt",      "kms:DescribeKey"    ],    "Resource": "arn:aws:kms:us-east-1:123456789012:key/abc-123"  }]}

Multi-Account AWS Setup

Enterprise-Umgebungen operieren selten innerhalb eines einzigen AWS-Accounts. Development-, Staging- und Production-Umgebungen laufen in separaten Accounts für Security-Isolation und Blast-Radius-Containment. SOPS unterstützt diese Architektur durch umgebungsspezifische KMS-Keys und Cross-Account-IAM-Berechtigungen.

Architektur-Überblick

Ein typisches Multi-Account-Setup trennt Umgebungen in verschiedene AWS-Accounts, jeder mit dedizierten KMS-Keys. Dies verhindert, dass Developer versehentlich auf Production-Secrets zugreifen, und bietet klare Security-Boundaries.

Diese Architektur erzwingt mehrere Security-Prinzipien. Developer benötigen explizite Cross-Account-Role-Assumption, um auf jede Umgebung zuzugreifen. KMS-Keys sind account-lokal, sodass die Kompromittierung einer Umgebung andere nicht offenlegt. CloudTrail-Logs in jedem Account bieten unabhängige Audit-Trails.

KMS-Keys pro Environment-Account

Jeder AWS-Account pflegt seinen eigenen KMS-Key. Die .sops.yaml-Konfiguration mapped Dateipfade zu account-spezifischen KMS-Keys.

yaml
# .sops.yaml - Multi-Account-Konfigurationcreation_rules:  # Development Account Secrets  - path_regex: secrets/dev/.*\.yaml$    kms: arn:aws:kms:eu-central-1:111111111111:key/aaaaaaaa-dev-1111-1111-111111111111
  # Staging Account Secrets  - path_regex: secrets/staging/.*\.yaml$    kms: arn:aws:kms:eu-central-1:222222222222:key/bbbbbbbb-stg-2222-2222-222222222222
  # Production Account Secrets  - path_regex: secrets/prod/.*\.yaml$    kms: arn:aws:kms:eu-central-1:333333333333:key/cccccccc-prd-3333-3333-333333333333
  # Fallback für Development mit age  - age: age1ql3z7hjy54pw3hyww5ayyfg7zqgvc7w3j2elw8zmrj2kg5sfn9aqmcac8p

Die Verzeichnisstruktur spiegelt die Account-Trennung wider:

secrets/├── dev/│   ├── database.yaml      # Verschlüsselt mit Dev-Account-KMS│   └── api-keys.yaml├── staging/│   ├── database.yaml      # Verschlüsselt mit Staging-Account-KMS│   └── api-keys.yaml└── prod/    ├── database.yaml      # Verschlüsselt mit Prod-Account-KMS    └── api-keys.yaml

Wenn du eine Datei in secrets/prod/ verschlüsselst, verwendet SOPS automatisch den Production-Account-KMS-Key. Keine manuelle Key-Auswahl erforderlich.

Cross-Account-KMS-Zugriff

Damit CI/CD-Pipelines Secrets über Accounts hinweg entschlüsseln können, müssen KMS-Key-Policies Cross-Account-Zugriff erlauben. Dies erfordert Konfiguration sowohl in der KMS-Key-Policy als auch in den IAM-Rollen-Berechtigungen.

KMS-Key-Policy im Production-Account (333333333333):

json
{  "Version": "2012-10-17",  "Statement": [    {      "Sid": "Enable IAM User Permissions",      "Effect": "Allow",      "Principal": {        "AWS": "arn:aws:iam::333333333333:root"      },      "Action": "kms:*",      "Resource": "*"    },    {      "Sid": "Allow CI/CD account to decrypt",      "Effect": "Allow",      "Principal": {        "AWS": "arn:aws:iam::444444444444:role/GitHubActionsDeployRole"      },      "Action": [        "kms:Decrypt",        "kms:DescribeKey"      ],      "Resource": "*",      "Condition": {        "StringEquals": {          "kms:ViaService": [            "secretsmanager.eu-central-1.amazonaws.com",            "lambda.eu-central-1.amazonaws.com"          ]        }      }    }  ]}

Die Bedingung beschränkt die KMS-Nutzung auf spezifische AWS-Services und verhindert direkten Key-Zugriff außerhalb legitimer Deployment-Kontexte.

IAM-Rolle im CI/CD-Account (444444444444):

json
{  "Version": "2012-10-17",  "Statement": [    {      "Effect": "Allow",      "Action": [        "kms:Decrypt",        "kms:DescribeKey"      ],      "Resource": [        "arn:aws:kms:eu-central-1:111111111111:key/aaaaaaaa-dev-1111-1111-111111111111",        "arn:aws:kms:eu-central-1:222222222222:key/bbbbbbbb-stg-2222-2222-222222222222",        "arn:aws:kms:eu-central-1:333333333333:key/cccccccc-prd-3333-3333-333333333333"      ]    },    {      "Effect": "Allow",      "Action": "sts:AssumeRole",      "Resource": [        "arn:aws:iam::111111111111:role/LambdaDeployRole",        "arn:aws:iam::222222222222:role/LambdaDeployRole",        "arn:aws:iam::333333333333:role/LambdaDeployRole"      ]    }  ]}

Diese IAM-Policy erlaubt der CI/CD-Rolle sowohl die Entschlüsselung mit KMS-Keys als auch das Annehmen von Deployment-Rollen in Ziel-Accounts.

CI/CD mit Rollen-Assumption

GitHub-Actions-Workflows nehmen verschiedene Rollen für verschiedene Umgebungen an. Die AWS-Credentials-Action unterstützt Rollen-Chaining für Multi-Account-Deployments.

yaml
name: Multi-Account Lambda Deployon:  push:    branches: [main]
jobs:  deploy-dev:    runs-on: ubuntu-latest    permissions:      id-token: write      contents: read    steps:      - uses: actions/checkout@v4
      - name: Configure AWS Credentials fuer Dev        uses: aws-actions/configure-aws-credentials@v5        with:          role-to-assume: arn:aws:iam::111111111111:role/GitHubActionsDeployRole          aws-region: eu-central-1
      - name: Installiere SOPS        run: |          wget -q https://github.com/getsops/sops/releases/download/v3.11.0/sops-v3.11.0.linux.amd64          chmod +x sops-v3.11.0.linux.amd64          sudo mv sops-v3.11.0.linux.amd64 /usr/local/bin/sops
      - name: Entschlüssele Dev Secrets        run: sops --decrypt secrets/dev/database.yaml > /tmp/secrets.yaml
      - name: Deploye zu Dev Lambda        run: |          # Deployment-Befehle verwenden /tmp/secrets.yaml          serverless deploy --stage dev
  deploy-prod:    runs-on: ubuntu-latest    needs: [deploy-dev]    environment: production    permissions:      id-token: write      contents: read    steps:      - uses: actions/checkout@v4
      - name: Configure AWS Credentials fuer Prod        uses: aws-actions/configure-aws-credentials@v5        with:          role-to-assume: arn:aws:iam::333333333333:role/GitHubActionsDeployRole          aws-region: eu-central-1
      - name: Installiere SOPS        run: |          wget -q https://github.com/getsops/sops/releases/download/v3.11.0/sops-v3.11.0.linux.amd64          chmod +x sops-v3.11.0.linux.amd64          sudo mv sops-v3.11.0.linux.amd64 /usr/local/bin/sops
      - name: Entschlüssele Prod Secrets        run: sops --decrypt secrets/prod/database.yaml > /tmp/secrets.yaml
      - name: Deploye zu Prod Lambda        run: |          # Deployment-Befehle verwenden /tmp/secrets.yaml          serverless deploy --stage prod

Der Workflow trennt Deployment-Jobs nach Umgebung. Jeder Job nimmt die entsprechende Account-Rolle an, bevor er Secrets entschlüsselt. Die needs-Abhängigkeit stellt sicher, dass Dev vor Production deployed wird. Das environment: production fügt manuelle Approval-Gates hinzu.

Developer-Workflow mit AWS-Profilen

Developer, die lokal arbeiten, benötigen AWS-Profilkonfigurationen für jeden Account. Die ~/.aws/config-Datei definiert Rollen-Assumption-Chains.

ini
# ~/.aws/config[profile dev]role_arn = arn:aws:iam::111111111111:role/DeveloperRolesource_profile = defaultregion = eu-central-1output = json
[profile staging]role_arn = arn:aws:iam::222222222222:role/DeveloperRolesource_profile = defaultregion = eu-central-1output = json
[profile prod]role_arn = arn:aws:iam::333333333333:role/DeveloperRolesource_profile = defaultregion = eu-central-1output = jsonmfa_serial = arn:aws:iam::444444444444:mfa/ayhan.sipahi

Production-Zugriff erfordert MFA. Wenn ein Developer SOPS-Befehle für Production-Secrets ausführt, fordert AWS ein MFA-Token an.

Secrets für verschiedene Umgebungen verschlüsseln:

bash
# Verschlüssele dev secretAWS_PROFILE=dev sops --encrypt secrets/dev/database.yaml > secrets/dev/database.enc.yaml
# Verschlüssele staging secretAWS_PROFILE=staging sops --encrypt secrets/staging/database.yaml > secrets/staging/database.enc.yaml
# Verschlüssele prod secret (fordert MFA an)AWS_PROFILE=prod sops --encrypt secrets/prod/database.yaml > secrets/prod/database.enc.yaml

Für lokale Tests entschlüsseln:

bash
# Entschlüssele dev secrets lokalAWS_PROFILE=dev sops --decrypt secrets/dev/database.enc.yaml > .env.dev
# Entschlüssele staging (mit staging role)AWS_PROFILE=staging sops --decrypt secrets/staging/database.enc.yaml > .env.staging

Die Profilauswahl erfolgt über die AWS_PROFILE-Umgebungsvariable. SOPS verwendet automatisch den richtigen KMS-Key basierend auf dem Dateipfad und nimmt die entsprechende Rolle basierend auf dem aktiven Profil an.

Security-Überlegungen

Multi-Account-SOPS-Deployments führen mehrere Security-Anforderungen ein, die durch IAM-Policies und organisatorische Kontrollen durchgesetzt werden müssen.

Principle of Least Privilege: Developer sollten nur auf die Umgebungen zugreifen, mit denen sie aktiv arbeiten. Ein Junior-Developer, der in Development-Umgebungen arbeitet, sollte keine Production-KMS-Decrypt-Berechtigungen haben. Rollen-Policies sollten diese Segregation widerspiegeln.

json
{  "Version": "2012-10-17",  "Statement": [    {      "Effect": "Allow",      "Action": [        "kms:Decrypt",        "kms:DescribeKey"      ],      "Resource": [        "arn:aws:kms:eu-central-1:111111111111:key/aaaaaaaa-dev-1111-1111-111111111111"      ]    },    {      "Effect": "Deny",      "Action": [        "kms:Decrypt",        "kms:DescribeKey"      ],      "Resource": [        "arn:aws:kms:eu-central-1:333333333333:key/cccccccc-prd-3333-3333-333333333333"      ]    }  ]}

Das explizite Deny stellt sicher, dass dieser Developer Production-Secrets nicht entschlüsseln kann, selbst wenn höherstufige Policies Zugriff gewähren.

Audit-Logging mit CloudTrail: Jeder AWS-Account sollte CloudTrail aktiviert haben, wobei Logs an einen zentralisierten Security-Account gesendet werden. Dies erstellt einen unveränderlichen Audit-Trail aller KMS-Operationen.

json
{  "eventVersion": "1.08",  "userIdentity": {    "type": "AssumedRole",    "principalId": "AROAEXAMPLE:ayhan.sipahi",    "arn": "arn:aws:sts::333333333333:assumed-role/DeveloperRole/ayhan.sipahi"  },  "eventTime": "2025-12-17T10:30:00Z",  "eventSource": "kms.amazonaws.com",  "eventName": "Decrypt",  "requestParameters": {    "keyId": "arn:aws:kms:eu-central-1:333333333333:key/cccccccc-prd-3333-3333-333333333333"  },  "responseElements": null,  "requestID": "abc-123-def-456",  "resources": [{    "accountId": "333333333333",    "type": "AWS::KMS::Key",    "ARN": "arn:aws:kms:eu-central-1:333333333333:key/cccccccc-prd-3333-3333-333333333333"  }]}

CloudTrail-Logs zeigen, wer wann auf welchen KMS-Key zugegriffen hat. Dies ermöglicht die Erkennung unbefugter Zugriffsversuche oder Compliance-Audits.

Key-Policies vs. IAM-Policies: Verwende beide für Defense in Depth. KMS-Key-Policies definieren auf Ressourcenebene, wer den Key verwenden kann. IAM-Policies definieren, was die Identität tun kann. Beide müssen die Operation erlauben, damit sie erfolgreich ist.

Ein Production-KMS-Key sollte eine restriktive Key-Policy haben, die nur bestimmte Deployment-Rollen zulässt, selbst wenn anderswo breitere IAM-Policies existieren. Dies verhindert Privilege-Escalation durch IAM-Policy-Änderungen allein.

Break-Glass-Verfahren: Selbst mit MFA und restriktiven Policies erfordern Notfälle schnellen Production-Zugriff. Pflege eine Emergency-Access-Rolle mit zeitlich begrenzten Credentials und automatischer Alarmierung bei Verwendung.

json
{  "Version": "2012-10-17",  "Statement": [    {      "Effect": "Allow",      "Principal": {        "AWS": "arn:aws:iam::333333333333:role/EmergencyBreakGlassRole"      },      "Action": [        "kms:Decrypt",        "kms:DescribeKey"      ],      "Resource": "*",      "Condition": {        "DateGreaterThan": {          "aws:CurrentTime": "2025-12-17T00:00:00Z"        },        "DateLessThan": {          "aws:CurrentTime": "2025-12-18T00:00:00Z"        }      }    }  ]}

Diese Policy erlaubt Emergency-Zugriff, aber nur innerhalb eines Zeitfensters. Wenn die Rolle angenommen wird, lösen CloudWatch Events Alarme an Security-Teams und Management aus.

AWS Lambda-Integration mit SOPS

Lambda-Funktionen benötigen Secrets zur Runtime, aber du möchtest diese Secrets zusammen mit deinem Funktionscode versionskontrollieren. SOPS ermöglicht dies, indem Secrets während des Deployments entschlüsselt werden, nicht zur Runtime.

AWS CDK-Integration (Empfohlener Ansatz)

AWS CDK bietet mehrere Patterns für SOPS-Integration, von einfach bis fortgeschritten. Für moderne serverless-Entwicklung ist CDK der empfohlene Ansatz.

Pattern 1: Direkte Umgebungsvariablen-Injection

Der einfachste Ansatz entschlüsselt SOPS zur CDK-Synthese-Zeit und injiziert Werte als Lambda-Umgebungsvariablen:

typescript
// lib/lambda-stack.tsimport * as cdk from 'aws-cdk-lib';import * as lambda from 'aws-cdk-lib/aws-lambda';import { execSync } from 'child_process';import * as yaml from 'js-yaml';
export class LambdaStack extends cdk.Stack {  constructor(scope: cdk.App, id: string, props?: cdk.StackProps) {    super(scope, id, props);
    // Entschlüssele SOPS-Datei zur Synthese-Zeit    const decrypted = execSync('sops --decrypt secrets/prod.enc.yaml', {      encoding: 'utf-8'    });    const secrets = yaml.load(decrypted) as any;
    // Erstelle Lambda mit entschlüsselten Secrets    new lambda.Function(this, 'ApiFunction', {      runtime: lambda.Runtime.NODEJS_20_X,      handler: 'index.handler',      code: lambda.Code.fromAsset('src'),      environment: {        DB_HOST: secrets.database.host,        DB_USERNAME: secrets.database.username,        DB_PASSWORD: secrets.database.password,        STRIPE_SECRET_KEY: secrets.stripe.secret_key,      },    });  }}

Synthesisiere und deploye:

bash
# CDK entschlüsselt SOPS während der Synthesecdk synthcdk deploy

Pattern 2: SSM Parameter Store-Befüllung

Für Secrets, die Runtime-Rotation benötigen, befülle SSM Parameter Store aus SOPS:

typescript
// lib/secrets-stack.tsimport * as cdk from 'aws-cdk-lib';import * as ssm from 'aws-cdk-lib/aws-ssm';import { execSync } from 'child_process';import * as yaml from 'js-yaml';
export class SecretsStack extends cdk.Stack {  constructor(scope: cdk.App, id: string, props?: cdk.StackProps) {    super(scope, id, props);
    const decrypted = execSync('sops --decrypt secrets/prod.enc.yaml', {      encoding: 'utf-8'    });    const secrets = yaml.load(decrypted) as any;
    // Erstelle SSM-Parameter aus SOPS-Werten    new ssm.StringParameter(this, 'DbPassword', {      parameterName: '/prod/database/password',      stringValue: secrets.database.password,      tier: ssm.ParameterTier.ADVANCED,    });
    new ssm.StringParameter(this, 'StripeKey', {      parameterName: '/prod/stripe/secret_key',      stringValue: secrets.stripe.secret_key,      tier: ssm.ParameterTier.ADVANCED,    });  }}

Lambda-Funktionen können diese Parameter zur Laufzeit lesen:

typescript
// Lambda-Runtime-Codeimport { SSMClient, GetParameterCommand } from '@aws-sdk/client-ssm';
const ssm = new SSMClient({});
export const handler = async () => {  const dbPassword = await ssm.send(    new GetParameterCommand({      Name: '/prod/database/password',      WithDecryption: true    })  );
  // Erstelle Datenbankverbindungsstring mit Secret  const connectionString = `postgresql://admin:${dbPassword.Parameter?.Value}@...`;};

Pattern 3: cdk-sops-secrets Construct verwenden

Die Community-maintained cdk-sops-secrets-Bibliothek bietet eine elegantere Integration:

bash
npm install cdk-sops-secrets
typescript
// lib/lambda-stack.tsimport * as cdk from 'aws-cdk-lib';import * as lambda from 'aws-cdk-lib/aws-lambda';import { SopsSecret } from 'cdk-sops-secrets';
export class LambdaStack extends cdk.Stack {  constructor(scope: cdk.App, id: string, props?: cdk.StackProps) {    super(scope, id, props);
    // Importiere SOPS-Secret in Secrets Manager    const secret = new SopsSecret(this, 'AppSecrets', {      sopsFilePath: 'secrets/prod.enc.yaml',    });
    // Verwende als Lambda-Umgebungsvariablen    new lambda.Function(this, 'ApiFunction', {      runtime: lambda.Runtime.NODEJS_20_X,      handler: 'index.handler',      code: lambda.Code.fromAsset('src'),      environment: {        DB_HOST: secret.secretValueFromJson('database.host').unsafeUnwrap(),        STRIPE_KEY: secret.secretValueFromJson('stripe.secret_key').unsafeUnwrap(),      },    });  }}

Pattern 4: Multi-Stack Secret Sharing

Für das Teilen von Secrets über mehrere Stacks:

typescript
// lib/shared-secrets-stack.tsexport class SharedSecretsStack extends cdk.Stack {  public readonly secrets: { [key: string]: string };
  constructor(scope: cdk.App, id: string, props?: cdk.StackProps) {    super(scope, id, props);
    const decrypted = execSync('sops --decrypt secrets/prod.enc.yaml', {      encoding: 'utf-8'    });    this.secrets = yaml.load(decrypted) as any;  }}
// lib/lambda-stack.tsexport class LambdaStack extends cdk.Stack {  constructor(    scope: cdk.App,    id: string,    secretsStack: SharedSecretsStack,    props?: cdk.StackProps  ) {    super(scope, id, props);
    new lambda.Function(this, 'ApiFunction', {      runtime: lambda.Runtime.NODEJS_20_X,      handler: 'index.handler',      code: lambda.Code.fromAsset('src'),      environment: {        DB_HOST: secretsStack.secrets.database.host,      },    });  }}
// bin/app.tsconst app = new cdk.App();const secretsStack = new SharedSecretsStack(app, 'SharedSecrets');new LambdaStack(app, 'LambdaStack', secretsStack);

AWS SAM-Integration

AWS SAM unterstützt Umgebungsvariablen aus Dateien. Entschlüssele SOPS-Secrets während des Deployments:

yaml
# template.yamlAWSTemplateFormatVersion: '2010-09-09'Transform: AWS::Serverless-2016-10-31
Globals:  Function:    Runtime: python3.12    Timeout: 30
Resources:  ApiFunction:    Type: AWS::Serverless::Function    Properties:      CodeUri: src/      Handler: app.handler      Environment:        Variables:          DB_HOST: !Sub '{{resolve:secretsmanager:prod/database:SecretString:host}}'          DB_PASSWORD: !Sub '{{resolve:secretsmanager:prod/database:SecretString:password}}'

Deploye mit entschlüsselten Secrets:

bash
# Entschlüssele und befülle SSM/Secrets Managersops exec-file secrets/prod.enc.yaml 'aws secretsmanager create-secret \  --name prod/database \  --secret-string file://{}'
# Deploye dann SAMsam deploy --stack-name my-api --capabilities CAPABILITY_IAM

Local Development Workflow

Für lokale Tests in CDK-Projekten entschlüssele Secrets temporär:

bash
# Entschlüssele für lokale Entwicklungsops --decrypt secrets/dev.enc.yaml > .env.local
# Führe lokal mit entschlüsselten Secrets ausnpm run dev
# Oder CDK synth/deploycdk synthcdk deploy --profile dev
# Räume aufrm .env.local

Verwende den SOPS-Exec-Modus, um Befehle mit entschlüsselter Umgebung auszuführen:

bash
# CDK entschlüsselt SOPS automatisch während der Synthesesops exec-env secrets/dev.enc.yaml 'cdk synth'
# Für Next.js Development Serversops exec-env secrets/dev.enc.yaml 'npm run dev'

SSM Parameter Store vs. SOPS-Vergleich

Verwende SOPS wenn:

  • Secrets sich mit Code-Deployments ändern
  • Du Git-basierte Audit-Trails möchtest
  • Secrets statisch sind (API-Keys, OAuth-Credentials)
  • Team-Kollaboration an Secrets wichtig ist
  • Kostenoptimierung Priorität hat

Verwende SSM Parameter Store wenn:

  • Secrets unabhängig von Deployments rotieren
  • Mehrere Services dieselben Secrets teilen
  • Du AWS-native Secret-Rotation benötigst
  • Runtime-Secret-Updates ohne Redeployment nötig sind
  • Cross-Region Secret-Replikation erforderlich ist

Hybrid-Ansatz:

typescript
// CDK: Einige Secrets von SOPS, andere von SSMconst sopsSecrets = loadSopsSecrets('secrets/prod.enc.yaml');
new lambda.Function(this, 'ApiFunction', {  environment: {    // Statische Secrets von SOPS    STRIPE_PUBLIC_KEY: sopsSecrets.stripe.public_key,    OAUTH_CLIENT_ID: sopsSecrets.oauth.client_id,
    // Dynamische Secrets von SSM    DB_PASSWORD: cdk.aws_ssm.StringParameter.valueForStringParameter(      this, '/prod/database/password'    ),  },});

Terraform-Integration

Der Terraform-SOPS-Provider ermöglicht das Lesen verschlüsselter Variablendateien, während deine State-Datei sauber bleibt.

Konfiguriere den Provider:

hcl
terraform {  required_providers {    sops = {      source  = "carlpett/sops"      version = "~> 1.3"    }  }}
provider "sops" {}

Erstelle eine verschlüsselte Variablendatei:

yaml
# secrets.enc.yamldatabase:  username: postgres_admin  password: super_secret_password  host: prod-db.example.com  port: 5432
aws:  access_key: AKIAIOSFODNN7EXAMPLE  secret_key: wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY

Verschlüssele sie:

bash
sops --encrypt secrets.yaml > secrets.enc.yamlgit add secrets.enc.yaml

Referenziere in Terraform:

hcl
data "sops_file" "secrets" {  source_file = "secrets.enc.yaml"}
resource "aws_db_instance" "main" {  identifier     = "production-db"  engine         = "postgres"  instance_class = "db.t3.medium"
  username = data.sops_file.secrets.data["database.username"]  password = data.sops_file.secrets.data["database.password"]
  lifecycle {    ignore_changes = [password]  }}
output "database_endpoint" {  value     = aws_db_instance.main.endpoint  sensitive = true}

Das Passwort erscheint nie in Plaintext in deiner Terraform-State-Datei, weil wir ignore_changes verwenden. Für die initiale Erstellung entschlüsselt SOPS den Wert. Für nachfolgende Applies ignoriert Terraform Passwort-Änderungen.

Ein besseres Pattern ist SOPS zum Befüllen von AWS Secrets Manager zu verwenden und dann die Secret-ARN zu referenzieren:

hcl
resource "aws_secretsmanager_secret" "db_password" {  name = "prod/database/password"}
resource "aws_secretsmanager_secret_version" "db_password" {  secret_id     = aws_secretsmanager_secret.db_password.id  secret_string = data.sops_file.secrets.data["database.password"]}
resource "aws_ecs_task_definition" "app" {  container_definitions = jsonencode([{    secrets = [{      name      = "DB_PASSWORD"      valueFrom = aws_secretsmanager_secret.db_password.arn    }]  }])}

Jetzt fetcht deine Application-Runtime Secrets aus Secrets Manager, aber die initialen Secret-Werte sind mit SOPS versionskontrolliert.

CI/CD-Integration-Patterns

GitHub Actions mit CDK (Primary Pattern)

AWS CDK entschlüsselt SOPS automatisch während der Synthese, dies ist der empfohlene Ansatz für moderne serverless-Deployments:

yaml
name: CDK Lambda Deploy mit SOPSon:  push:    branches: [main]
jobs:  deploy:    runs-on: ubuntu-latest    permissions:      id-token: write      contents: read    steps:      - uses: actions/checkout@v4
      - name: Configure AWS Credentials        uses: aws-actions/configure-aws-credentials@v5        with:          role-to-assume: arn:aws:iam::123456789012:role/GitHubActionsCDK          aws-region: us-east-1
      - name: Installiere SOPS        run: |          wget -q https://github.com/getsops/sops/releases/download/v3.11.0/sops-v3.11.0.linux.amd64          chmod +x sops-v3.11.0.linux.amd64          sudo mv sops-v3.11.0.linux.amd64 /usr/local/bin/sops
      - name: Setup Node.js        uses: actions/setup-node@v4        with:          node-version: '20'
      - name: Installiere Dependencies        run: npm ci
      - name: CDK Synth (SOPS wird während Synthese entschlüsselt)        run: npx cdk synth
      - name: CDK Deploy        run: npx cdk deploy --all --require-approval never

CDK-Code entschlüsselt SOPS während der Synthese, daher ist kein expliziter Entschlüsselungsschritt in CI/CD erforderlich.

Die IAM-Rolle benötigt CDK-Deployment-Berechtigungen plus KMS-Decrypt:

json
{  "Version": "2012-10-17",  "Statement": [    {      "Effect": "Allow",      "Action": [        "kms:Decrypt",        "kms:DescribeKey"      ],      "Resource": "arn:aws:kms:us-east-1:123456789012:key/abc-123"    },    {      "Effect": "Allow",      "Action": [        "cloudformation:*",        "lambda:*",        "iam:*",        "s3:*"      ],      "Resource": "*"    }  ]}

Multi-Environment CDK Deployment

Deploye in verschiedene Umgebungen mit umgebungsspezifischen Secrets:

yaml
name: Multi-Environment CDK Deployon:  push:    branches: [main, develop]
jobs:  deploy-dev:    if: github.ref == 'refs/heads/develop'    runs-on: ubuntu-latest    permissions:      id-token: write      contents: read    steps:      - uses: actions/checkout@v4
      - name: Configure AWS Credentials        uses: aws-actions/configure-aws-credentials@v5        with:          role-to-assume: arn:aws:iam::111111111111:role/GitHubActionsCDK          aws-region: us-east-1
      - name: Installiere SOPS        run: |          wget -q https://github.com/getsops/sops/releases/download/v3.11.0/sops-v3.11.0.linux.amd64          chmod +x sops-v3.11.0.linux.amd64          sudo mv sops-v3.11.0.linux.amd64 /usr/local/bin/sops
      - name: Setup Node.js        uses: actions/setup-node@v4        with:          node-version: '20'
      - name: Installiere Dependencies        run: npm ci
      - name: CDK Deploy Dev        run: npx cdk deploy DevStack --require-approval never
  deploy-prod:    if: github.ref == 'refs/heads/main'    runs-on: ubuntu-latest    environment: production    permissions:      id-token: write      contents: read    steps:      - uses: actions/checkout@v4
      - name: Configure AWS Credentials        uses: aws-actions/configure-aws-credentials@v5        with:          role-to-assume: arn:aws:iam::222222222222:role/GitHubActionsCDK          aws-region: us-east-1
      - name: Installiere SOPS        run: |          wget -q https://github.com/getsops/sops/releases/download/v3.11.0/sops-v3.11.0.linux.amd64          chmod +x sops-v3.11.0.linux.amd64          sudo mv sops-v3.11.0.linux.amd64 /usr/local/bin/sops
      - name: Setup Node.js        uses: actions/setup-node@v4        with:          node-version: '20'
      - name: Installiere Dependencies        run: npm ci
      - name: CDK Deploy Prod        run: npx cdk deploy ProdStack --require-approval never

GitHub Actions mit AWS SAM (Alternative Pattern)

Für AWS SAM-Deployments:

yaml
name: SAM Deploy mit SOPSon:  push:    branches: [main]
jobs:  deploy:    runs-on: ubuntu-latest    permissions:      id-token: write      contents: read    steps:      - uses: actions/checkout@v4
      - name: Configure AWS Credentials        uses: aws-actions/configure-aws-credentials@v5        with:          role-to-assume: arn:aws:iam::123456789012:role/GitHubActionsSAM          aws-region: us-east-1
      - name: Installiere SOPS        run: |          wget -q https://github.com/getsops/sops/releases/download/v3.11.0/sops-v3.11.0.linux.amd64          chmod +x sops-v3.11.0.linux.amd64          sudo mv sops-v3.11.0.linux.amd64 /usr/local/bin/sops
      - name: Setup SAM CLI        uses: aws-actions/setup-sam@v2
      - name: Entschlüssele und befülle Secrets Manager        run: |          sops exec-file secrets/prod.enc.yaml \            'aws secretsmanager put-secret-value \              --secret-id prod/app-secrets \              --secret-string file://{}'
      - name: SAM Build        run: sam build
      - name: SAM Deploy        run: |          sam deploy \            --stack-name my-lambda-app \            --capabilities CAPABILITY_IAM \            --no-confirm-changeset \            --no-fail-on-empty-changeset

GitLab CI

GitLab CI verwendet ähnliche Patterns:

yaml
variables:  SOPS_VERSION: "3.11.0"
stages:  - decrypt  - deploy
decrypt-secrets:  stage: decrypt  image: alpine:latest  before_script:    - apk add --no-cache wget    - wget -q https://github.com/getsops/sops/releases/download/v${SOPS_VERSION}/sops-v${SOPS_VERSION}.linux.amd64    - chmod +x sops-v${SOPS_VERSION}.linux.amd64    - mv sops-v${SOPS_VERSION}.linux.amd64 /usr/local/bin/sops  script:    - mkdir -p ~/.config/sops/age    - echo "$SOPS_AGE_KEY" > ~/.config/sops/age/keys.txt    - chmod 600 ~/.config/sops/age/keys.txt    - sops --decrypt secrets.enc.yaml > secrets.yaml  artifacts:    paths:      - secrets.yaml    expire_in: 10 minutes

Das entschlüsselte Secrets-Artefakt ist für nachfolgende Stages verfügbar, läuft aber nach 10 Minuten ab.

Developer Experience und IDE-Integration

Verschlüsselte Dateien manuell zu bearbeiten wäre schmerzhaft. SOPS bietet einen Edit-Modus, der die Verschlüsselung transparent handhabt.

Setze deinen Editor:

bash
export EDITOR="code --wait"  # VS Code# oderexport EDITOR="vim"          # Vim

Bearbeite eine verschlüsselte Datei:

bash
sops secrets.enc.yaml

SOPS entschlüsselt die Datei, öffnet sie in deinem Editor, wartet darauf, dass du speicherst und schließt, und verschlüsselt dann mit aktualisierten Werten neu. Du siehst während der Bearbeitung nie den verschlüsselten Inhalt.

Für VS Code installiere die SOPS-Extension:

json
// .vscode/settings.json{  "sops.enable": true,  "sops.defaults": {    "age": "age1ql3z7hjy54pw3hyww5ayyfg7zqgvc7w3j2elw8zmrj2kg5sfn9aqmcac8p"  },  "files.associations": {    "*.enc.yaml": "yaml",    "*.enc.json": "json"  }}

Die Extension entschlüsselt Dateien automatisch, wenn du sie in VS Code öffnest, und verschlüsselt sie beim Speichern neu.

Für aussagekräftige Git-Diffs konfiguriere einen benutzerdefinierten Differ:

bash
# .gitattributes*.enc.yaml diff=sopsdiffer*.enc.json diff=sopsdiffer
bash
# .git/config oder ~/.gitconfig[diff "sopsdiffer"]  textconv = sops --decrypt

Jetzt zeigt git diff secrets.enc.yaml die tatsächlichen Wertänderungen, nicht verschlüsselte Blob-Unterschiede.

Pre-commit Hooks und Validation

Verhindere das Committen entschlüsselter Secrets mit Pre-commit-Hooks:

yaml
# .pre-commit-config.yamlrepos:  - repo: https://github.com/yuvipanda/pre-commit-hook-ensure-sops    rev: v1.1    hooks:      - id: sops-encryption        files: (secrets|prod).*\.(yaml|json)$

Dieser Hook verifiziert, dass Dateien, die dem Pattern entsprechen, verschlüsselt sind, bevor der Commit erlaubt wird.

Füge benutzerdefinierte Validation hinzu:

bash
# .git/hooks/pre-commit#!/bin/bash
# Prüfe auf unverschlüsselte sensitive PatternsFORBIDDEN_PATTERNS="password|api_key|secret_token"
for file in $(git diff --cached --name-only); do  if [[ $file =~ \.(yaml|json)$ ]] && [[ ! $file =~ \.enc\. ]]; then    if grep -qiE "$FORBIDDEN_PATTERNS" "$file"; then      echo "ERROR: Mögliches unverschlüsseltes Secret in $file"      echo "Meintest du ${file%.yaml}.enc.yaml zu committen?"      exit 1    fi  fidone
# Verifiziere, dass verschlüsselte Dateien SOPS-Metadaten habenfor file in $(git diff --cached --name-only | grep '\.enc\.'); do  if ! grep -q "^sops:" "$file"; then    echo "ERROR: $file fehlen SOPS-Metadaten"    exit 1  fidone

Der Hook verhindert sowohl das versehentliche Committen von Plaintext-Secrets als auch das Committen von Dateien, die behaupten verschlüsselt zu sein, es aber nicht sind.

Key-Rotation-Strategien

Age-Keys sollten alle 90 Tage rotiert werden. Die Automatisierung dieses Prozesses verhindert, dass es vergessen wird.

Generiere einen neuen age-Key:

bash
age-keygen -o new-key.txtOLD_KEY=$(grep "public key:" old-key.txt | cut -d' ' -f3)NEW_KEY=$(grep "public key:" new-key.txt | cut -d' ' -f3)

Füge den neuen Key zu allen verschlüsselten Dateien hinzu:

bash
find . -name "*.enc.yaml" -type f | while read file; do  sops --add-age "$NEW_KEY" "$file"done

Rotiere die Data-Keys (generiert neue zufällige Keys):

bash
find . -name "*.enc.yaml" -type f | while read file; do  sops --rotate --in-place "$file"done

Entferne den alten Key:

bash
find . -name "*.enc.yaml" -type f | while read file; do  sops --rm-age "$OLD_KEY" "$file"done

Aktualisiere .sops.yaml:

bash
sed -i "s/$OLD_KEY/$NEW_KEY/g" .sops.yaml

Committe und verteile den neuen Private Key an dein Team über einen sicheren Kanal (Password-Manager, verschlüsselte E-Mail, sicheres Messaging).

Für KMS-Keys ist der Prozess ähnlich, beinhaltet aber das Erstellen eines neuen KMS-Keys, das Hinzufügen zu Dateien, das Rotieren und das Entfernen der alten KMS-Key-ARN.

Multi-Team-Zugriff mit Key Groups

Production-Umgebungen erfordern oft, dass mehrere Teams auf Secrets zugreifen. SOPS unterstützt dies durch Key Groups und Shamir's Secret Sharing.

yaml
# .sops.yamlkeys:  platform: &platform    - &platform_admin age1ql3z7hjy54pw3hyww5ayyfg7zqgvc7w3j2elw8zmrj2kg5sfn9aqmcac8p    - &platform_sre age1cy0su9fwf8gzkdqh3r4r6xgc92fp8jqrjp4fvd4ak6vd3mc0jjpqnhymkw
  security: &security    - &sec_team age1yx3z8r0hnzjy9wh6fq5gldq3p7hxg6nfkz5vgqcdqhsj8tqxj8xq8w6qur
creation_rules:  - path_regex: prod/.*\.enc\.yaml$    key_groups:      - age:          - *platform_admin          - *platform_sre      - age:          - *sec_team    shamir_threshold: 2

Mit shamir_threshold: 2 erfordert die Entschlüsselung Keys von 2 der 3 Gruppen. Dies implementiert Separation of Duties. Ein Platform-Engineer kann Production-Secrets nicht alleine entschlüsseln. Das Security-Team auch nicht. Aber zwei beliebige Gruppen zusammen können entschlüsseln.

Der Data-Key wird mit Shamir's Secret Sharing in Fragmente aufgeteilt. Fragment 1 wird für das Platform-Team verschlüsselt, Fragment 2 für das Security-Team, Fragment 3 für Backup. Zwei beliebige Fragmente können den vollständigen Data-Key rekonstruieren.

Kostenvergleich und Trade-offs

Für ein Szenario mit 100 Secrets:

SOPS mit AWS KMS:

  • KMS-Keys: 3 × 1/Monat=1/Monat = 3
  • API-Aufrufe: ~1,000 Entschlüsselungen/Monat = $0.03
  • Git-Speicher: $0 (existierendes Repository)
  • Gesamt: $3.03/Monat

AWS Secrets Manager:

  • Secrets: 100 × 0.40/Monat=0.40/Monat = 40
  • API-Aufrufe: 10,000/Monat × 0.05/10k=0.05/10k = 0.05
  • Gesamt: $40.05/Monat

Einsparungen: $37/Monat (92% Reduktion)

Aber Kosten sind nicht die einzige Überlegung. Secrets Manager bietet automatisierte Rotation, während SOPS Scripting erfordert. Secrets Manager hat integrierte Audit-Logs über CloudTrail. SOPS verlässt sich auf Git-History.

SOPS gewinnt bei statischen Secrets (API-Keys, OAuth-Credentials, Datenbank-Verbindungsstrings, die sich selten ändern). Secrets Manager gewinnt bei dynamischen Secrets (Datenbank-Passwörter, die wöchentlich rotieren, Service-Credentials mit automatisierter Erneuerung).

Ein hybrider Ansatz funktioniert gut:

  • Development und Staging: SOPS mit age-Keys
  • Production statische Secrets: SOPS mit KMS
  • Production dynamische Secrets: AWS Secrets Manager
  • Datenbank-Root-Passwörter: Secrets Manager
  • Third-Party-API-Keys: SOPS

Häufige Fallstricke und Lösungen

Fallstrick: Entschlüsselte Dateien committen

Füge zu .gitignore hinzu:

secrets.yamlconfig/production.yaml*.decrypted.yaml
# Erlaube verschlüsselte Dateien!*.enc.yaml

Fallstrick: Age Private Keys verlieren

Speichere Backups an mehreren Orten:

  • Password-Manager (1Password, LastPass)
  • Verschlüsseltes USB-Laufwerk im physischen Safe
  • Offline gespeicherter Emergency-Recovery-Key

Erstelle einen Emergency-Key und füge ihn zu allen Production-Secrets hinzu:

bash
age-keygen -o emergency-key.txtAGE_PUBLIC_KEY=$(grep "public key:" emergency-key.txt | cut -d' ' -f3)
find prod/ -name "*.enc.yaml" | while read file; do  sops --add-age "$AGE_PUBLIC_KEY" "$file"done

Fallstrick: KMS-Permission-Probleme

Der Fehler "AccessDeniedException" beim Entschlüsseln bedeutet normalerweise, dass die IAM-Berechtigungen falsch sind. Verifiziere:

bash
aws sts get-caller-identity  # Bestätige assumed roleaws kms describe-key --key-id $KMS_KEY_ID  # Teste KMS-Zugriffsops --decrypt --verbose secrets.enc.yaml  # Siehe detaillierten Fehler

Stelle sicher, dass deine IAM-Rolle sowohl kms:Decrypt als auch kms:DescribeKey-Berechtigungen für den KMS-Key hat.

Fallstrick: Git Merge Conflicts

Wenn zwei Developer gleichzeitig dieselbe verschlüsselte Datei bearbeiten, erstellt Git einen Merge-Konflikt mit verschlüsselten Blobs.

Hinweis: Beim normalen Editieren entschlüsselt sops secrets.enc.yaml die Datei, öffnet sie in deinem Editor und verschlüsselt automatisch neu, wenn du speicherst und beendest. Aber bei Merge-Konflikten musst du zwei verschiedene Versionen vergleichen, daher ist der manuelle decrypt → merge → re-encrypt Workflow erforderlich.

Die Auflösung erfordert:

bash
# Entschlüssele beide Versionensops --decrypt secrets.enc.yaml > mine.yamlgit show origin/main:secrets.enc.yaml | sops --decrypt /dev/stdin > theirs.yaml
# Merge manuellvimdiff mine.yaml theirs.yaml
# Speichere gemergte Versionmv merged.yaml secrets.yaml
# Re-verschlüsselesops --encrypt secrets.yaml > secrets.enc.yamlgit add secrets.enc.yaml

Besser: Kommuniziere beim Bearbeiten gemeinsamer Secrets, oder teile große Dateien in kleinere domänenspezifische Dateien auf, um die Kollisionswahrscheinlichkeit zu reduzieren.

Key Takeaways

SOPS ermöglicht GitOps-Workflows für Serverless ohne externe Secret-Abhängigkeiten. Secrets werden mit Lambda-Code versioniert, zusammen deployed und zusammen zurückgesetzt. Deine Git-History wird zu deinem Audit-Trail.

Age-Verschlüsselung bietet eine moderne, einfache Alternative zu PGP. Die Keys sind kurz genug, um sie im Chat zu teilen. Das Tooling ist minimal. Die Onboarding-Zeit liegt unter 30 Minuten.

Die .sops.yaml-Konfigurationsdatei eliminiert manuelles Key-Management. Pfadbasierte Regeln wählen automatisch die richtigen Keys für jede Umgebung aus. Developer verschlüsseln Dateien, ohne KMS-ARNs zu kennen oder sich zu merken, welche Keys verwendet werden sollen.

AWS CDK ist der empfohlene Ansatz für moderne serverless-Entwicklung. CDK entschlüsselt SOPS während der Synthese, daher sind keine expliziten Decrypt-Schritte in CI/CD-Pipelines erforderlich. Die cdk-sops-secrets Construct-Bibliothek bietet noch elegantere Integration.

Für Lambda-Deployments entschlüsselt SOPS zur Build/Deploy-Zeit, nicht zur Runtime. Dies eliminiert Cold-Start-Overhead durch Secret-Fetching. Umgebungsvariablen werden während des Deployments in die Funktionskonfiguration eingebacken.

CDK, AWS SAM und Serverless Framework integrieren sich alle nahtlos mit SOPS. CDK entschlüsselt während der Synthese. SAM befüllt Secrets Manager aus SOPS-Dateien. Serverless Framework verwendet Plugins oder Pre-Deploy-Scripts.

Für Production-Umgebungen bietet die Kombination von KMS mit age sowohl zentralisiertes Management als auch Emergency Recovery. KMS handhabt die primäre Verschlüsselung mit IAM-basierter Zugriffskontrolle. Age bietet einen Backup-Entschlüsselungspfad, wenn KMS nicht verfügbar ist.

Die Kosteneinsparungen im Vergleich zu AWS Secrets Manager sind für statische Secrets erheblich. SOPS funktioniert am besten für Konfiguration, die sich mit Lambda-Deployments ändert (API-Keys, OAuth-Credentials). Verwende SSM Parameter Store oder Secrets Manager für dynamische Secrets, die sich unabhängig rotieren (Datenbank-Passwörter).

Ein Hybrid-Ansatz maximiert den Wert: SOPS für statische Secrets, SSM für dynamische Secrets. Dies balanciert Kostenoptimierung mit operationeller Flexibilität.

Pre-commit-Hooks und Validation sind obligatorisch, nicht optional. Ohne automatisierte Checks wird irgendwann jemand eine entschlüsselte Datei committen. Richte Guardrails ein, bevor dein Team SOPS in Production verwendet.

Ähnliche Beiträge