İçeriğe atla

2025-12-18

Mozilla SOPS: GitOps için Gerçekten İşe Yarayan Secret Encryption

Git repository'lerinde encrypted secret'ları yönetmek için Mozilla SOPS rehberi. Age encryption, AWS CDK pattern'leri, AWS Lambda entegrasyonu ve serverless workflow'lar için production-ready security stratejileri.

Özet

Mozilla SOPS (Secrets OPerationS), GitOps’ta temel bir sorunu çözüyor: secret’ları version control’e güvenli bir şekilde nasıl commit edebiliriz ama aynı zamanda developer productivity’yi nasıl koruruz. Cloud-native secret store’lardan farklı olarak, SOPS dosyaları doğrudan Git repository’lerinde encrypt ediyor, YAML/JSON yapısını korurken hassas değerleri şifreliyor. Bu rehber age encryption, AWS Lambda entegrasyonu, AWS CDK workflow’ları, AWS SAM pattern’leri ve GitHub Actions, GitLab CI, Jenkins’te serverless deployment’lar için CI/CD otomasyonu gibi pratik implementation pattern’lerini kapsıyor.

GitOps Secret Management Challenge

Infrastructure as Code ile çalışırken hemen bir sorunla karşılaşıyorsun. Serverless configuration dosyalarını, Terraform variable’larını ve environment config’lerini version-control altına almanız gerekiyor. Ama bu dosyalar database password’leri, API key’leri ve service credential’ları içeriyor. Secret’ları Git’e commit ettiğin anda bir security vulnerability yaratmış oluyorsun.

Geleneksel çözümler friction yaratıyor. HashiCorp Vault infrastructure çalıştırmayı ve deployment zamanında API call’ları gerektiriyor. AWS Secrets Manager secret başına ayda $0.40’a mal oluyor ve Lambda function’larına runtime API call’ları ekliyor. AWS Systems Manager Parameter Store ücretsiz ama yine de runtime fetching gerektiriyor. Her yaklaşım secret’ları GitOps workflow’undan çıkarıp external system’lere taşıyor.

SOPS farklı bir yaklaşım benimsiyor. Dosyaları doğrudan Git repository’nde encrypt ediyor, secret’ları kodunla birlikte versioned tutuyor. Bir API key’i değiştirdiğinde ve Lambda function’ını güncellediğinde, her iki değişiklik de aynı commit’e giriyor. Rollback yaptığında, ikisi de birlikte geri dönüyor. Git history’n audit trail’in oluyor.

SOPS Architecture’ını Anlamak

SOPS envelope encryption kullanıyor. Bir dosyayı encrypt ettiğinde, SOPS rastgele bir 256-bit data key oluşturuyor ve dosya içeriğini AES256-GCM ile encrypt ediyor. Sonra bu data key’i bir veya daha fazla master key (AWS KMS, age, PGP, GCP KMS veya Azure Key Vault) ile encrypt ediyor ve encrypted data key’i dosyanın metadata’sında saklıyor. Age, SOPS data key’ini X25519 + ChaCha20-Poly1305 kullanarak encrypt ediyor.

Plaintext Dosya

Rastgele Data Key Olustur

Icerigi AES-256-GCM ile Sifrele

Data Key'i Master Key'lerle Sifrele

Sifreli Icerik + Metadata Kaydet

Git'e Commit Et

Git'ten Pull Et

Sifreli Dosyayi Oku

Data Key'i Master Key ile Coz

Icerigi Data Key ile Coz

Plaintext Cikti

YAML ve JSON gibi structured format’lar için SOPS sadece value’ları encrypt ediyor, key’leri değil. Bu dosya yapısını code review için görünür tutuyor ve tool’ların value’lar encrypted olsa bile schema’yı parse etmesine izin veriyor.

Encrypted bir YAML dosyası şöyle görünüyor:

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

Database configuration yapısını hala görebiliyorsun. Host, port ve password field’ları olduğunu biliyorsun. Ama gerçek değerler encrypted. Git diff hangi field’ların değiştiğini gösteriyor, sadece “encrypted blob değişti” demiyor.

Installation ve Setup

Installation platform’a göre değişiyor ama beş dakikadan az sürüyor:

# macOS with Homebrew
brew install sops

# Linux - en son release'i indir
wget 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

# Installation'ı doğrula
sops --version

Container environment’lar için official image’i kullan:

FROM mozilla/sops:v3.11.0

# Encrypted dosyalarını kopyala
COPY secrets.enc.yaml /app/

# Runtime'da decrypt et
CMD ["sops", "--decrypt", "/app/secrets.enc.yaml"]

Age: Modern Encryption Seçimi

SOPS birden fazla key management system’i destekliyor. Team environment’ları için age (h-age gibi okunur), PGP’ye göre önerilen seçim haline geldi.

Age public key’leri 62 karakter, private key’leri 74 karakter uzunluğunda. PGP key’leri 4096 karakter. Age public key’i Slack’te kopyalayıp yapıştırabilirsin. PGP key’leri satırlar arası kırılıyor. Age modern kriptografi kullanıyor (X25519 + ChaCha20-Poly1305). PGP, GPG keyring system’den gelen on yıllarca complexity ile geliyor.

Age key pair oluştur:

# Age'i install et (v1.2.0 veya üzeri)
brew install age  # macOS
apt install age  # Ubuntu

# Key pair oluştur
age-keygen -o ~/.config/sops/age/keys.txt

# Output public key'i gösteriyor
# Public key: age1ql3z7hjy54pw3hyww5ayyfg7zqgvc7w3j2elw8zmrj2kg5sfn9aqmcac8p

Private key ~/.config/sops/age/keys.txt dosyasında saklanıyor. Public key team member’larla paylaştığın ve SOPS’ta configure ettiğin şey.

Age ile dosya encrypt etmek için:

# Private key lokasyonunu ayarla
export SOPS_AGE_KEY_FILE=$HOME/.config/sops/age/keys.txt

# Public key kullanarak encrypt et
sops --age age1ql3z7hjy54pw3hyww5ayyfg7zqgvc7w3j2elw8zmrj2kg5sfn9aqmcac8p \
  --encrypt secrets.yaml > secrets.enc.yaml

# Decrypt et (SOPS_AGE_KEY_FILE'dan private key kullanır)
sops --decrypt secrets.enc.yaml > secrets.yaml

Team distribution için, herkes kendi age key’ini oluşturuyor ve public key’ini paylaşıyor. SOPS’u tüm team member’ların public key’leri ile encrypt edecek şekilde configure ediyorsun. Private key’i olan herkes decrypt edebilir.

.sops.yaml Configuration Dosyası

Repository root’unda .sops.yaml dosyası oluşturmak manuel key management’ı ortadan kaldırıyor. SOPS bu dosyayı okuyarak dosya path’lerine göre hangi key’leri kullanacağını belirliyor.

İşte production-ready bir configuration:

creation_rules:
  # Development - tüm developer'lar için basit age key'leri
  - path_regex: \.dev\.yaml$
    age: >-
      age1ql3z7hjy54pw3hyww5ayyfg7zqgvc7w3j2elw8zmrj2kg5sfn9aqmcac8p,
      age1cy0su9fwf8gzkdqh3r4r6xgc92fp8jqrjp4fvd4ak6vd3mc0jjpqnhymkw

  # Staging - production flow'u test etmek için AWS KMS
  - path_regex: \.staging\.yaml$
    kms: arn:aws:kms:us-west-2:111111111111:key/staging-key-id
    age: age1ql3z7hjy54pw3hyww5ayyfg7zqgvc7w3j2elw8zmrj2kg5sfn9aqmcac8p

  # Production - redundancy ile birden fazla KMS key
  - 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 secret'ları - AWS KMS
  - path_regex: serverless/.*\.yaml$
    kms: arn:aws:kms:us-east-1:222222222222:key/serverless-key-id

  # Terraform variable'ları - infrastructure için
  - path_regex: terraform/.*\.tfvars$
    kms: arn:aws:kms:us-east-1:222222222222:key/terraform-key-id

  # Match olmayan dosyalar için default fallback
  - age: age1ql3z7hjy54pw3hyww5ayyfg7zqgvc7w3j2elw8zmrj2kg5sfn9aqmcac8p

Artık encryption otomatik hale geliyor:

# SOPS key'leri bulmak için .sops.yaml'i okuyor
sops --encrypt config.dev.yaml > config.dev.enc.yaml  # Age key'leri kullanır
sops --encrypt config.staging.yaml > config.staging.enc.yaml  # AWS KMS kullanır
sops --encrypt config.prod.yaml > config.prod.enc.yaml  # KMS + age kullanır

Path-based rule’lar human error’u ortadan kaldırıyor. Developer’lar hangi environment için hangi key’leri kullanacaklarını hatırlamak zorunda kalmıyor.

AWS KMS Entegrasyonu

Production environment’lar için AWS KMS, IAM-based access control ve CloudTrail üzerinden audit logging ile centralized key management sağlıyor.

KMS key oluştur:

# Production için SOPS encryption key'i oluştur
aws kms create-key \
  --description "SOPS encryption key for production" \
  --key-usage ENCRYPT_DECRYPT

# Daha kolay referans için alias oluştur
aws kms create-alias \
  --alias-name alias/sops-production \
  --target-key-id <key-id-from-previous-command>

# Key ARN'i al
aws kms describe-key --key-id alias/sops-production

SOPS’u KMS kullanacak şekilde configure et:

export SOPS_KMS_ARN="arn:aws:kms:us-east-1:123456789012:key/abc-123-def"

# Dosyayı encrypt et
sops --kms $SOPS_KMS_ARN --encrypt secrets.yaml > secrets.enc.yaml

CI/CD environment’lar için credential’ları saklamak yerine IAM role’leri kullan:

# OIDC ile GitHub Actions
- 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: KMS ile Decrypt Et
  run: sops --decrypt secrets.enc.yaml > secrets.yaml

IAM role KMS decrypt permission’larına ihtiyaç duyuyor:

{
  "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 environment’lar nadiren tek bir AWS account içinde çalışıyor. Development, staging ve production environment’lar security isolation ve blast radius containment için ayrı account’larda çalışıyor. SOPS bu architecture’ı environment-specific KMS key’leri ve cross-account IAM permission’ları ile destekliyor.

Architecture’a Genel Bakış

Tipik bir multi-account setup, environment’ları dedicated KMS key’leri olan farklı AWS account’larına ayırıyor. Bu developer’ların production secret’lara yanlışlıkla erişmesini önlüyor ve net security boundary’leri sağlıyor.

Role Assume Eder

Role Assume Eder

Role Assume Eder

KMS Key

dev-key-id

KMS Key

staging-key-id

KMS Key

prod-key-id

Dev Role Assume Et

Staging Role Assume Et

Prod Role Assume Et

Developer Workstation

Dev Account

111111111111

Staging Account

222222222222

Prod Account

333333333333

secrets/dev/*.yaml

secrets/staging/*.yaml

secrets/prod/*.yaml

CI/CD Pipeline

Bu architecture birkaç security principle’ı enforce ediyor. Developer’ların her environment’a erişmek için explicit cross-account role assumption’a ihtiyacı var. KMS key’leri account-local, yani bir environment’ın compromise olması diğerlerini expose etmiyor. Her account’taki CloudTrail log’ları bağımsız audit trail’ler sağlıyor.

Environment Account Başına KMS Key

Her AWS account kendi KMS key’ini maintain ediyor. .sops.yaml configuration dosya path’lerini account-specific KMS key’lere map ediyor.

# .sops.yaml - Multi-account configuration
creation_rules:
  # Development account secret'ları
  - path_regex: secrets/dev/.*\.yaml$
    kms: arn:aws:kms:eu-central-1:111111111111:key/aaaaaaaa-dev-1111-1111-111111111111

  # Staging account secret'ları
  - path_regex: secrets/staging/.*\.yaml$
    kms: arn:aws:kms:eu-central-1:222222222222:key/bbbbbbbb-stg-2222-2222-222222222222

  # Production account secret'ları
  - path_regex: secrets/prod/.*\.yaml$
    kms: arn:aws:kms:eu-central-1:333333333333:key/cccccccc-prd-3333-3333-333333333333

  # Development için age ile fallback
  - age: age1ql3z7hjy54pw3hyww5ayyfg7zqgvc7w3j2elw8zmrj2kg5sfn9aqmcac8p

Directory yapısı account separation’ı yansıtıyor:

secrets/
├── dev/
│  ├── database.yaml  # Dev account KMS ile encrypted
│  └── api-keys.yaml
├── staging/
│  ├── database.yaml  # Staging account KMS ile encrypted
│  └── api-keys.yaml
└── prod/
    ├── database.yaml  # Prod account KMS ile encrypted
    └── api-keys.yaml

secrets/prod/ içinde bir dosyayı encrypt ettiğinde, SOPS otomatik olarak production account KMS key’ini kullanıyor. Manuel key selection gerekmez.

Cross-Account KMS Access

CI/CD pipeline’larının account’lar arası secret’ları decrypt edebilmesi için KMS key policy’leri cross-account access’e izin vermeli. Bu hem KMS key policy’sinde hem IAM role permission’larında configuration gerektiriyor.

Production account’taki (333333333333) KMS key policy:

{
  "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"
          ]
        }
      }
    }
  ]
}

Condition KMS kullanımını specific AWS service’leri ile kısıtlıyor, legitimate deployment context’leri dışında direct key access’i önlüyor.

CI/CD account’taki (444444444444) IAM role:

{
  "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"
      ]
    }
  ]
}

Bu IAM policy CI/CD role’ünün hem KMS key’leri ile decrypt etmesine hem de target account’larda deployment role’leri assume etmesine izin veriyor.

Role Assumption ile CI/CD

GitHub Actions workflow’ları farklı environment’lar için farklı role’leri assume ediyor. AWS credentials action multi-account deployment’lar için role chaining’i destekliyor.

name: Multi-Account Lambda Deploy
on:
  push:
    branches: [main]

jobs:
  deploy-dev:
    runs-on: ubuntu-latest
    permissions:
      id-token: write
      contents: read
    steps:
      - uses: actions/checkout@v4

      - name: Dev için AWS Credential'larini Configure Et
        uses: aws-actions/configure-aws-credentials@v5
        with:
          role-to-assume: arn:aws:iam::111111111111:role/GitHubActionsDeployRole
          aws-region: eu-central-1

      - name: SOPS Install Et
        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: Dev Secret'lari Decrypt Et
        run: sops --decrypt secrets/dev/database.yaml > /tmp/secrets.yaml

      - name: Dev Lambda'ya Deploy Et
        run: |
          # Deployment command'ları /tmp/secrets.yaml kullanır
          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: Prod için AWS Credential'larini Configure Et
        uses: aws-actions/configure-aws-credentials@v5
        with:
          role-to-assume: arn:aws:iam::333333333333:role/GitHubActionsDeployRole
          aws-region: eu-central-1

      - name: SOPS Install Et
        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: Prod Secret'lari Decrypt Et
        run: sops --decrypt secrets/prod/database.yaml > /tmp/secrets.yaml

      - name: Prod Lambda'ya Deploy Et
        run: |
          # Deployment command'ları /tmp/secrets.yaml kullanır
          serverless deploy --stage prod

Workflow deployment job’larını environment’a göre ayırıyor. Her job secret’ları decrypt etmeden önce uygun account role’ünü assume ediyor. needs dependency dev’in production’dan önce deploy olmasını garantiliyor. environment: production manuel approval gate’leri ekliyor.

AWS Profile’ları ile Developer Workflow

Local çalışan developer’ların her account için AWS profile configuration’larına ihtiyacı var. ~/.aws/config dosyası role assumption chain’lerini define ediyor.

# ~/.aws/config
[profile dev]
role_arn = arn:aws:iam::111111111111:role/DeveloperRole
source_profile = default
region = eu-central-1
output = json

[profile staging]
role_arn = arn:aws:iam::222222222222:role/DeveloperRole
source_profile = default
region = eu-central-1
output = json

[profile prod]
role_arn = arn:aws:iam::333333333333:role/DeveloperRole
source_profile = default
region = eu-central-1
output = json
mfa_serial = arn:aws:iam::444444444444:mfa/ayhan.sipahi

Production access MFA gerektiriyor. Developer production secret’ları için SOPS command’ları çalıştırdığında, AWS MFA token soruyor.

Farklı environment’lar için secret’ları encrypt etmek:

# Dev secret'ı encrypt et
AWS_PROFILE=dev sops --encrypt secrets/dev/database.yaml > secrets/dev/database.enc.yaml

# Staging secret'ı encrypt et
AWS_PROFILE=staging sops --encrypt secrets/staging/database.yaml > secrets/staging/database.enc.yaml

# Prod secret'ı encrypt et (MFA sorar)
AWS_PROFILE=prod sops --encrypt secrets/prod/database.yaml > secrets/prod/database.enc.yaml

Local test için decrypt etmek:

# Dev secret'larını locally decrypt et
AWS_PROFILE=dev sops --decrypt secrets/dev/database.enc.yaml > .env.dev

# Staging'i decrypt et (staging role ile)
AWS_PROFILE=staging sops --decrypt secrets/staging/database.enc.yaml > .env.staging

Profile seçimi AWS_PROFILE environment variable üzerinden gerçekleşiyor. SOPS dosya path’ine göre otomatik olarak doğru KMS key’i kullanıyor ve aktif profile’a göre uygun role’ü assume ediyor.

Security Consideration’ları

Multi-account SOPS deployment’ları IAM policy’leri ve organizational control’ler ile enforce edilmesi gereken birkaç security requirement getiriyor.

Principle of Least Privilege: Developer’lar sadece aktif olarak çalıştıkları environment’lara erişmeli. Development environment’larında çalışan junior bir developer production KMS decrypt permission’larına sahip olmamalı. Role policy’leri bu segregation’ı yansıtmalı.

{
  "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"
      ]
    }
  ]
}

Explicit deny higher-level policy’ler access grant etse bile bu developer’ın production secret’larını decrypt edemeyeceğini garantiliyor.

CloudTrail ile Audit Logging: Her AWS account CloudTrail enabled olmalı ve log’lar centralized bir security account’a ship edilmeli. Bu tüm KMS operation’larının immutable audit trail’ini oluşturuyor.

{
  "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 log’ları kimin hangi KMS key’e ne zaman eriştiğini gösteriyor. Bu unauthorized access attempt’lerin tespitini veya compliance audit’leri mümkün kılıyor.

Key Policy’ler vs IAM Policy’ler: Defense in depth için her ikisini de kullan. KMS key policy’leri resource level’da key’i kimin kullanabileceğini define ediyor. IAM policy’leri identity’nin ne yapabileceğini define ediyor. Operation başarılı olması için her ikisi de allow etmeli.

Production KMS key restrictive bir key policy’ye sahip olmalı ki sadece specific deployment role’lere izin versin, başka yerde daha geniş IAM policy’ler olsa bile. Bu IAM policy değişiklikleri ile privilege escalation’ı tek başına önlüyor.

Break-Glass Procedure’ları: MFA ve restrictive policy’lerle bile emergency’ler rapid production access gerektiriyor. Kullanıldığında otomatik alert’le time-limited credential’lara sahip emergency access role maintain et.

{
  "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"
        }
      }
    }
  ]
}

Bu policy emergency access’e izin veriyor ama sadece bir time window içinde. Role assume edildiğinde CloudWatch Events security team’lere ve management’a alert’ler trigger ediyor.

SOPS ile AWS Lambda Entegrasyonu

Lambda function’lar runtime’da secret’lara ihtiyaç duyuyor, ama bu secret’ları function kodunla birlikte version-control altında tutmak istiyorsun. SOPS bunu runtime’da değil deployment sırasında secret’ları decrypt ederek mümkün kılıyor.

Sifreli Secret'lar

age/KMS ile Decrypt

Environment Variable'lar

Runtime Overhead Yok

Git Repository

CI/CD Pipeline

Serverless Deploy

Lambda Function

Application Logic

AWS CDK Entegrasyonu

CDK synthesis time’da decrypt edilmiş SOPS dosyalarını okuyup environment variable olarak inject edebiliyor. Bu en temiz SOPS integration pattern’i çünkü secret’lar build-time’da handle ediliyor.

Pattern 1: Direct Environment Variable Injection

En basit yaklaşım synthesis sırasında SOPS decrypt edip environment variable olarak inject etmek:

// lib/lambda-stack.ts
import * 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);

    // Synthesis time'da SOPS dosyasını decrypt et
    const decrypted = execSync('sops --decrypt secrets/prod.enc.yaml', {
      encoding: 'utf-8'
    });
    const secrets = yaml.load(decrypted) as any;

    // Decrypt edilmiş secret'larla Lambda oluştur
    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,
      },
    });
  }
}

Synthesize edip deploy et:

# CDK synthesis sırasında SOPS'u decrypt ediyor
cdk synth
cdk deploy

Pattern 2: SSM Parameter Store Population

Daha advanced bir yaklaşım synthesis sırasında SOPS secret’larını decrypt edip AWS Systems Manager Parameter Store’a populate etmek:

// lib/secrets-stack.ts
import * 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);

    // SOPS dosyasını decrypt et
    const decrypted = execSync('sops --decrypt secrets/prod.enc.yaml', {
      encoding: 'utf-8'
    });
    const secrets = yaml.load(decrypted) as any;

    // SSM Parameter'ları oluştur
    new ssm.StringParameter(this, 'DbHost', {
      parameterName: '/prod/database/host',
      stringValue: secrets.database.host,
    });

    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 function’ları bu parameter’ları runtime’da okuyabilir:

// Lambda runtime code
import { 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
    })
  );

  // Database connection string'i secret ile oluştur
  const connectionString = `postgresql://admin:${dbPassword.Parameter?.Value}@...`;
};

Pattern 3: cdk-sops-secrets Construct Kullanmak

Community-maintained cdk-sops-secrets construct daha elegant bir integration sağlıyor:

npm install cdk-sops-secrets
// lib/lambda-stack.ts
import * 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);

    // SOPS secret'ı Secrets Manager'a import et
    const secret = new SopsSecret(this, 'AppSecrets', {
      sopsFilePath: 'secrets/prod.enc.yaml',
    });

    // Lambda environment variable olarak kullan
    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

Birden fazla stack için secret’ları paylaşmak:

// lib/shared-secrets-stack.ts
export 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.ts
export 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.ts
const app = new cdk.App();
const secretsStack = new SharedSecretsStack(app, 'SharedSecrets');
new LambdaStack(app, 'LambdaStack', secretsStack);

AWS SAM Entegrasyonu

AWS SAM dosyalardan environment variable’ları destekliyor. Deployment sırasında SOPS secret’larını decrypt et:

# template.yaml
AWSTemplateFormatVersion: '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}}'

Decrypt edilmiş secret’larla deploy et:

# Decrypt edip SSM/Secrets Manager'a populate et
sops exec-file secrets/prod.enc.yaml 'aws secretsmanager create-secret \
  --name prod/database \
  --secret-string file://{}'

# Sonra SAM'i deploy et
sam deploy --stack-name my-api --capabilities CAPABILITY_IAM

Local Development Workflow

CDK projelerinde local test için secret’ları geçici olarak decrypt et:

# Local development için decrypt et
sops --decrypt secrets/dev.enc.yaml > .env.local

# Decrypt edilmiş secret'larla locally çalıştır
npm run dev

# Veya CDK synth/deploy
cdk synth
cdk deploy --profile dev

# Temizle
rm .env.local

SOPS exec mode kullanarak command’ları decrypt edilmiş environment’ta çalıştır:

# CDK synthesis sırasında SOPS otomatik decrypt ediyor
sops exec-env secrets/dev.enc.yaml 'cdk synth'

# Next.js development server için
sops exec-env secrets/dev.enc.yaml 'npm run dev'

SSM Parameter Store vs SOPS Karşılaştırması

SOPS kullan:

  • Secret’lar code deployment’larla değişiyor
  • Git-based audit trail istiyorsun
  • Secret’lar static (API key’leri, OAuth credential’lar)
  • Team collaboration önemli
  • Maliyet optimizasyonu priority

SSM Parameter Store kullan:

  • Secret’lar deployment’lardan bağımsız rotate oluyor
  • Birden fazla servis aynı secret’ları paylaşıyor
  • AWS-native secret rotation lazım
  • Redeployment olmadan runtime secret update’leri
  • Cross-region secret replication gerekiyor

Hybrid yaklaşım:

// CDK: Bazı secret'lar SOPS'tan, diğerleri SSM'den
const sopsSecrets = loadSopsSecrets('secrets/prod.enc.yaml');

new lambda.Function(this, 'ApiFunction', {
  environment: {
    // SOPS'tan static secret'lar
    STRIPE_PUBLIC_KEY: sopsSecrets.stripe.public_key,
    OAUTH_CLIENT_ID: sopsSecrets.oauth.client_id,

    // SSM'den dynamic secret'lar
    DB_PASSWORD: cdk.aws_ssm.StringParameter.valueForStringParameter(
      this, '/prod/database/password'
    ),
  },
});

Terraform Entegrasyonu

Terraform SOPS provider, state file’ını temiz tutarken encrypted variable dosyalarını okumayı mümkün kılıyor.

Provider’ı configure et:

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

provider "sops" {}

Encrypted variable dosyası oluştur:

# secrets.enc.yaml
database:
  username: postgres_admin
  password: super_secret_password
  host: prod-db.example.com
  port: 5432

aws:
  access_key: AKIAIOSFODNN7EXAMPLE
  secret_key: wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY

Encrypt et:

sops --encrypt secrets.yaml > secrets.enc.yaml
git add secrets.enc.yaml

Terraform’da referans et:

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
}

Password hiçbir zaman Terraform state dosyanda plaintext olarak görünmüyor çünkü ignore_changes kullanıyoruz. İlk creation için SOPS değeri decrypt ediyor. Sonraki apply’larda Terraform password değişikliklerini ignore ediyor.

Daha iyi bir pattern SOPS’u AWS Secrets Manager’ı populate etmek için kullanıp sonra secret ARN’ini reference etmek:

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

Artık application runtime’da secret’ları Secrets Manager’dan fetch ediyor, ama initial secret değerleri SOPS ile version-controlled.

CI/CD Entegrasyon Pattern’leri

CDK ile GitHub Actions (Primary Pattern)

AWS CDK synthesis sırasında SOPS’u otomatik decrypt ediyor, bu modern serverless deployment’lar için önerilen yaklaşım:

name: SOPS ile CDK Lambda Deploy
on:
  push:
    branches: [main]

jobs:
  deploy:
    runs-on: ubuntu-latest
    permissions:
      id-token: write
      contents: read
    steps:
      - uses: actions/checkout@v4

      - name: AWS Credential'larini Configure Et
        uses: aws-actions/configure-aws-credentials@v5
        with:
          role-to-assume: arn:aws:iam::123456789012:role/GitHubActionsCDK
          aws-region: us-east-1

      - name: SOPS Install Et
        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: Node.js Setup
        uses: actions/setup-node@v4
        with:
          node-version: '20'

      - name: Dependency'leri Install Et
        run: npm ci

      - name: CDK Synth (SOPS synthesis sirasinda decrypt ediyor)
        run: npx cdk synth

      - name: CDK Deploy
        run: npx cdk deploy --all --require-approval never

CDK kodu synthesis sırasında SOPS’u decrypt ediyor, bu yüzden CI/CD’de explicit decrypt step’i gerekmiyor.

IAM role CDK deployment permission’larına artı KMS decrypt’e ihtiyaç duyuyor:

{
  "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

Farklı environment’lar için environment-specific secret’larla deploy et:

name: Multi-Environment CDK Deploy
on:
  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: AWS Credential'larini Configure Et
        uses: aws-actions/configure-aws-credentials@v5
        with:
          role-to-assume: arn:aws:iam::111111111111:role/GitHubActionsCDK
          aws-region: us-east-1

      - name: SOPS Install Et
        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: Node.js Setup
        uses: actions/setup-node@v4
        with:
          node-version: '20'

      - name: Dependency'leri Install Et
        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: AWS Credential'larini Configure Et
        uses: aws-actions/configure-aws-credentials@v5
        with:
          role-to-assume: arn:aws:iam::222222222222:role/GitHubActionsCDK
          aws-region: us-east-1

      - name: SOPS Install Et
        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: Node.js Setup
        uses: actions/setup-node@v4
        with:
          node-version: '20'

      - name: Dependency'leri Install Et
        run: npm ci

      - name: CDK Deploy Prod
        run: npx cdk deploy ProdStack --require-approval never

AWS SAM ile GitHub Actions (Alternative Pattern)

AWS SAM deployment’ları için:

name: SOPS ile SAM Deploy
on:
  push:
    branches: [main]

jobs:
  deploy:
    runs-on: ubuntu-latest
    permissions:
      id-token: write
      contents: read
    steps:
      - uses: actions/checkout@v4

      - name: AWS Credential'larini Configure Et
        uses: aws-actions/configure-aws-credentials@v5
        with:
          role-to-assume: arn:aws:iam::123456789012:role/GitHubActionsSAM
          aws-region: us-east-1

      - name: SOPS Install Et
        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: SAM CLI Setup
        uses: aws-actions/setup-sam@v2

      - name: Decrypt edip Secrets Manager'a populate et
        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 benzer pattern’ler kullanıyor:

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

Decrypt edilmiş secret’lar artifact’i sonraki stage’lerde available ama 10 dakika sonra expire oluyor.

Developer Experience ve IDE Entegrasyonu

Encrypted dosyaları manuel olarak edit etmek zor olurdu. SOPS encryption’ı transparent şekilde handle eden bir edit mode sağlıyor.

Editor’ünü ayarla:

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

Encrypted dosyayı edit et:

sops secrets.enc.yaml

SOPS dosyayı decrypt ediyor, editor’ünde açıyor, save edip kapatmanı bekliyor, sonra updated value’larla re-encrypt ediyor. Edit sırasında hiçbir zaman encrypted content’i görmüyorsun.

VS Code için SOPS extension’ı install et:

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

Extension dosyaları VS Code’da açtığında otomatik decrypt ediyor ve save’de re-encrypt ediyor.

Anlamlı Git diff’leri için custom differ configure et:

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

Artık git diff secrets.enc.yaml encrypted blob farklarını değil gerçek value değişikliklerini gösteriyor.

Pre-commit Hook’ları ve Validation

Pre-commit hook’ları ile decrypt edilmiş secret’ları commit etmeyi önle:

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

Bu hook pattern’e match eden dosyaların commit’e izin vermeden önce encrypted olduğunu doğruluyor.

Custom validation ekle:

# .git/hooks/pre-commit
#!/bin/bash

# Encrypted olmayan sensitive pattern'leri kontrol et
FORBIDDEN_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: $file'da encrypted olmayan secret olabilir"
      echo "${file%.yaml}.enc.yaml yapmayı mı kastettin?"
      exit 1
    fi
  fi
done

# Encrypted dosyaların SOPS metadata'sı olduğunu doğrula
for file in $(git diff --cached --name-only | grep '\.enc\.'); do
  if ! grep -q "^sops:" "$file"; then
    echo "ERROR: $file SOPS metadata'sı eksik"
    exit 1
  fi
done

Hook hem plaintext secret’ları yanlışlıkla commit etmeyi hem de encrypted olduğunu iddia eden ama olmayan dosyaları commit etmeyi önlüyor.

Key Rotation Stratejileri

Age key’leri her 90 günde bir rotate edilmeli. Bu süreci otomatikleştirmek unutulmayı önlüyor.

Yeni age key oluştur:

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

Yeni key’i tüm encrypted dosyalara ekle:

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

Data key’leri rotate et (yeni rastgele key’ler oluşturur):

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

Eski key’i kaldır:

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

.sops.yaml’i güncelle:

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

Commit et ve yeni private key’i secure bir kanal üzerinden (password manager, encrypted email, secure messaging) team’e dağıt.

KMS key’leri için süreç benzer ama yeni KMS key oluşturmayı, dosyalara eklemeyi, rotate etmeyi ve eski KMS key ARN’ini kaldırmayı içeriyor.

Key Group’lar ile Multi-Team Access

Production environment’lar genellikle birden fazla team’in secret’lara erişimini gerektiriyor. SOPS bunu key group’lar ve Shamir’s Secret Sharing ile destekliyor.

# .sops.yaml
keys:
  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

shamir_threshold: 2 ile decryption 3 group’tan 2’sinden key gerektiriyor. Bu separation of duties implement ediyor. Platform engineer tek başına production secret’larını decrypt edemiyor. Security team de edemiyor. Ama herhangi iki group birlikte decrypt edebilir.

Data key Shamir’s Secret Sharing kullanarak fragment’lere bölünüyor. Fragment 1 platform team için encrypted, fragment 2 security team için, fragment 3 backup için. Herhangi 2 fragment complete data key’i reconstruct edebilir.

Maliyet Karşılaştırması ve Trade-off’lar

100 secret’lı bir senaryo için:

SOPS with AWS KMS:

  • KMS key’leri: 3 × 1/ay=1/ay = 3
  • API call’ları: ~1,000 decryption/ay = $0.03
  • Git storage: $0 (mevcut repository)
  • Toplam: $3.03/ay

AWS Secrets Manager:

  • Secret’lar: 100 × 0.40/ay=0.40/ay = 40
  • API call’ları: 10,000/ay × 0.05/10k=0.05/10k = 0.05
  • Toplam: $40.05/ay

Tasarruf: $37/ay (%92 azalma)

Ama maliyet tek consideration değil. Secrets Manager automated rotation sağlıyor, SOPS scripting gerektiriyor. Secrets Manager CloudTrail üzerinden built-in audit log’lara sahip. SOPS Git history’ye güveniyor.

SOPS static secret’lar için kazanıyor (seyrek değişen API key’leri, OAuth credential’ları, database connection string’leri). Secrets Manager dynamic secret’lar için kazanıyor (haftalık rotate olan database password’leri, automated renewal’lı service credential’ları).

Hybrid yaklaşım iyi çalışıyor:

  • Development ve staging: SOPS with age key’leri
  • Production static secret’lar: SOPS with KMS
  • Production dynamic secret’lar: AWS Secrets Manager
  • Database root password’leri: Secrets Manager
  • Third-party API key’leri: SOPS

Yaygın Tuzaklar ve Çözümler

Tuzak: Decrypt Edilmiş Dosyaları Commit Etmek

.gitignore’a ekle:

secrets.yaml
config/production.yaml
*.decrypted.yaml

# Encrypted dosyalara izin ver
!*.enc.yaml

Tuzak: Age Private Key’leri Kaybetmek

Backup’ları birden fazla yerde sakla:

  • Password manager’da (1Password, LastPass)
  • Physical safe’te encrypted USB drive’da
  • Offline saklanan emergency recovery key

Emergency key oluştur ve tüm production secret’larına ekle:

age-keygen -o emergency-key.txt
AGE_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

Tuzak: KMS Permission Sorunları

Decrypt ederken “AccessDeniedException” hatası genellikle IAM permission’larının yanlış olduğu anlamına geliyor. Doğrula:

aws sts get-caller-identity  # Assumed role'u onayla
aws kms describe-key --key-id $KMS_KEY_ID  # KMS access'i test et
sops --decrypt --verbose secrets.enc.yaml  # Detaylı hatayı gör

IAM role’ünün KMS key için hem kms:Decrypt hem de kms:DescribeKey permission’larına sahip olduğundan emin ol.

Tuzak: Git Merge Conflict’leri

İki developer aynı anda aynı encrypted dosyayı edit ettiğinde, Git encrypted blob’larla merge conflict oluşturuyor.

Not: Normal editing için sops secrets.enc.yaml komutu dosyayı decrypt edip editörde açar, kaydedip çıktığında otomatik re-encrypt eder. Ama merge conflict’lerde iki farklı versiyonu karşılaştırman gerektiği için manuel decrypt → merge → re-encrypt workflow’u zorunlu.

Resolve etmek için:

# İki versiyonu da decrypt et
sops --decrypt secrets.enc.yaml > mine.yaml
git show origin/main:secrets.enc.yaml | sops --decrypt /dev/stdin > theirs.yaml

# Manuel olarak merge et
vimdiff mine.yaml theirs.yaml

# Merge edilmiş versiyonu kaydet
mv merged.yaml secrets.yaml

# Re-encrypt et
sops --encrypt secrets.yaml > secrets.enc.yaml
git add secrets.enc.yaml

Daha iyi: shared secret’ları edit ederken communicate et, veya collision olasılığını azaltmak için büyük dosyaları daha küçük domain-specific dosyalara böl.

Önemli Çıkarımlar

SOPS external secret dependency’leri olmadan serverless için GitOps workflow’larını mümkün kılıyor. Secret’lar Lambda kodu ile versioned, birlikte deploy ediliyor ve birlikte rollback ediliyor. Git history’n audit trail’in oluyor.

Age encryption PGP’ye modern, basit bir alternatif sağlıyor. Key’ler chat’te paylaşacak kadar kısa. Tooling minimal. Onboarding süresi 30 dakikanın altında.

.sops.yaml configuration dosyası manuel key management’ı ortadan kaldırıyor. Path-based rule’lar her environment için otomatik olarak doğru key’leri seçiyor. Developer’lar KMS ARN’lerini bilmeden veya hangi key’leri kullanacaklarını hatırlamadan dosyaları encrypt ediyor.

AWS CDK modern serverless development için önerilen yaklaşım oluyor. CDK synthesis sırasında SOPS’u decrypt ediyor, bu yüzden CI/CD pipeline’larında explicit decrypt step’lerine gerek yok. cdk-sops-secrets construct library daha da elegant integration sağlıyor.

Lambda deployment’ları için SOPS build/deploy time’da decrypt ediyor, runtime’da değil. Bu secret fetch’ten gelen cold start overhead’ini ortadan kaldırıyor. Environment variable’lar deployment sırasında function configuration’a bake ediliyor.

CDK, AWS SAM ve Serverless Framework hepsi SOPS ile seamless integrate oluyor. CDK synthesis sırasında decrypt ediyor. SAM SOPS dosyalarından Secrets Manager’ı populate ediyor. Serverless Framework plugin’ler veya pre-deploy script’ler kullanıyor.

Production environment’lar için KMS’i age ile birleştirmek hem centralized management hem de emergency recovery sağlıyor. KMS IAM-based access control ile primary encryption’ı handle ediyor. Age KMS unavailable olduğunda backup decryption path’i sağlıyor.

AWS Secrets Manager’a göre maliyet tasarrufu static secret’lar için önemli. SOPS Lambda deploy’ları ile değişen configuration için en iyi çalışıyor (API key’leri, OAuth credential’ları). Bağımsız rotate olan dynamic secret’lar için SSM Parameter Store veya Secrets Manager kullan (database password’leri).

Hybrid yaklaşım value’yi maksimize ediyor: static secret’lar için SOPS, dynamic secret’lar için SSM. Bu maliyet optimizasyonu ile operational flexibility’yi balance ediyor.

Pre-commit hook’ları ve validation opsiyonel değil, zorunlu. Automated check’ler olmadan birisi eninde sonunda decrypt edilmiş bir dosyayı commit edecek. Team’in production’da SOPS kullanmaya başlamadan önce guardrail’leri kur.

İlgili yazılar

Amazon Cognito Derinlemesine: Temel Authentication'ın Ötesinde

Amazon Cognito'nun gelişmiş özellikleri üzerine kapsamlı teknik kılavuz: özel authentication akışları, federation pattern'leri, multi-tenancy mimarileri, migration stratejileri ve production-grade güvenlik implementasyonu.

awscognitoauthentication+7
SNS/SQS Cross-Account Fan-Out: AWS'de Multi-Account Event Dağıtımı

Amazon SNS ve SQS kullanarak güvenli cross-account event dağıtımı nasıl yapılır öğrenin. IAM policy'leri, KMS şifreleme, AWS CDK implementasyonu ve production'da karşılaşılan yaygın sorunları kapsıyor.

awsaws-snsaws-sqs+6
TypeScript Geliştiricilerin Monolitten Lambda'ya Taşıdığı Beş Anti-Pattern

DI container'lar, monolitik SDK'lar, god-handler'lar, modül üstü secret çağrıları ve ağır ORM'ler - soğuk başlatmada bedeli ve yerine geçen fonksiyonel yapı.

aws-lambdatypescriptserverless+2
AWS Control Tower Çoklu Hesap Stratejisi: Landing Zone'dan Kurumsal Governance'a

OU yapısı, SCP, RCP, Account Factory for Terraform, IAM Identity Center ve merkezi güvenlik mimarisi konularını kapsayan AWS Control Tower çoklu hesap stratejisi tasarımı ve uygulaması için pratik bir rehber.

awsaws-control-towermulti-account+6
AWS Lambda'da Bun ve Alternatif JavaScript Runtime'ları Çalıştırma

Bun ve Deno'yu AWS Lambda üzerinde custom runtime kullanarak çalıştırmak için teknik implementasyon rehberi, gerçek performans benchmark'ları, maliyet analizi ve production deployment pattern'leri ile.

aws-lambdabundeno+4