SiamCafe · Blog
SOPS Encryption กับ Post-mortem Analysis —
บทความ

SOPS Encryption กับ Post-mortem Analysis —

เผยแพร่ 28 พฤษภาคม 2569

SOPS Encryption

SOPS Encryption กับ Post-mortem Analysis —

SOPS Secrets OPerationS เข้ารหัสไฟล์ Secrets Mozilla YAML JSON ENV INI เข้ารหัสเฉพาะ Value ใช้ AWS KMS GCP KMS Azure Key Vault age PGP Git ปลอดภัย

Post-mortem Analysis วิเคราะห์หลัง Incident Root Cause Timeline Impact Action Items Blameless ไม่โทษบุคคล โฟกัสระบบกระบวนการ

อ่านเพิ่ม: LXC vs Docker เลือก Container Technology อะไรดี · อ่านเพิ่ม: Proxmox VE Cluster ทำ High Availability สำหรับ Home Lab · อ่านเพิ่ม: MinIO S3 Compatible Storage self-hosted ทดแทน AWS S3

SOPS Configuration

=== SOPS Setup และ Configuration ===

1. ติดตั้ง SOPS

macOS: brew install sops

Linux: wget https://github.com/getsops/sops/releases/download/v3.8.1/sops-v3.8.1.linux.amd64 -O /usr/local/bin/sops && chmod +x /usr/local/bin/sops

Windows: scoop install sops

2. ติดตั้ง age (Key Management)

brew install age

age-keygen -o ~/.sops/age/keys.txt

export SOPS_AGE_KEY_FILE=~/.sops/age/keys.txt

3. .sops.yaml — Configuration File

creation_rules:

  • path_regex: \.enc\.yaml$

age: >-

age1ql3z7hjy54pw3hyww5ayyfg7zqgvc7w3j2elw8zmrj2kg5sfn9aqmcac8p

  • path_regex: secrets/.*\.yaml$

kms: arn:aws:kms:us-east-1:123456789:key/abcd-1234-efgh

  • path_regex: prod/.*\.yaml$

gcp_kms: projects/my-project/locations/global/keyRings/sops/cryptoKeys/sops-key

age: >-

age1ql3z7hjy54pw3hyww5ayyfg7zqgvc7w3j2elw8zmrj2kg5sfn9aqmcac8p

4. เข้ารหัสไฟล์

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

sops --encrypt --in-place secrets.yaml

5. แก้ไขไฟล์เข้ารหัส

sops secrets.enc.yaml # เปิด Editor decrypt อัตโนมัติ

6. ถอดรหัสไฟล์

sops --decrypt secrets.enc.yaml > secrets.yaml

sops --decrypt --output-type json secrets.enc.yaml

7. Rotate Keys

sops --rotate --in-place secrets.enc.yaml

8. ใช้กับ Kubernetes

sops --decrypt secrets.enc.yaml | kubectl apply -f -

9. ใช้กับ Terraform

data "sops_file" "secrets" {

source_file = "secrets.enc.yaml"

}

sops_commands = {

"sops --encrypt": "เข้ารหัสไฟล์",

"sops --decrypt": "ถอดรหัสไฟล์",

"sops <file>": "เปิด Editor แก้ไข (decrypt อัตโนมัติ)",

"sops --rotate": "หมุนเวียน Key",

"sops --set": "เปลี่ยนค่าเฉพาะ Key",

"sops updatekeys": "อัปเดต Key Recipients",

}

backends = {

"age": "ง่าย ไม่ต้องมี Cloud, ใช้ Local Key File",

"AWS KMS": "ใช้ IAM Roles, Envelope Encryption",

"GCP KMS": "ใช้ Service Account, Cloud KMS",

"Azure Key Vault": "ใช้ Azure AD, Managed Identity",

"PGP": "ใช้ GPG Keys, Legacy แต่ยังรองรับ",

}

print("SOPS Commands:")

for cmd, desc in sops_commands.items():

print(f" {cmd}")

print(f" -> {desc}")

print(f"\nEncryption Backends:")

for backend, desc in backends.items():

print(f" {backend}: {desc}")

Post-mortem Template

# post_mortem.py — Post-mortem Analysis Framework
from dataclasses import dataclass, field
from typing import List, Dict
from datetime import datetime

@dataclass
class TimelineEvent:
    time: str
    description: str
    actor: str

@dataclass
class ActionItem:
    description: str
    owner: str
    priority: str  # P0, P1, P2
    deadline: str
    status: str = "Open"

@dataclass
class PostMortem:
    title: str
    incident_id: str
    severity: str  # SEV1, SEV2, SEV3
    date: str
    duration: str
    impact: str
    summary: str
    root_cause: str
    timeline: List[TimelineEvent] = field(default_factory=list)
    action_items: List[ActionItem] = field(default_factory=list)
    what_went_well: List[str] = field(default_factory=list)
    what_went_wrong: List[str] = field(default_factory=list)
    where_we_got_lucky: List[str] = field(default_factory=list)

    def show(self):
        print(f"\n{'='*60}")
        print(f"POST-MORTEM: {self.title}")
        print(f"{'='*60}")
        print(f"  Incident: {self.incident_id} | Severity: {self.severity}")
        print(f"  Date: {self.date} | Duration: {self.duration}")
        print(f"  Impact: {self.impact}")
        print(f"\n  Summary: {self.summary}")
        print(f"\n  Root Cause: {self.root_cause}")

        print(f"\n  Timeline:")
        for event in self.timeline:
            print(f"    [{event.time}] {event.description} ({event.actor})")

        print(f"\n  What went well:")
        for item in self.what_went_well:
            print(f"    + {item}")

        print(f"\n  What went wrong:")
        for item in self.what_went_wrong:
            print(f"    - {item}")

        print(f"\n  Action Items:")
        for item in self.action_items:
            print(f"    [{item.priority}] {item.description}")
            print(f"      Owner: {item.owner} | Deadline: {item.deadline}")

# ตัวอย่าง Post-mortem
pm = PostMortem(
    title="Database Credentials Leaked in Git Repository",
    incident_id="INC-2024-042",
    severity="SEV1",
    date="2024-01-15",
    duration="4 ชั่วโมง 23 นาที",
    impact="Production Database Exposed, 2,300 Users Potentially Affected",
    summary="Database credentials ถูก Commit เข้า Public Git Repository โดยไม่ได้เข้ารหัส ทำให้ถูกเข้าถึงจากภายนอก",
    root_cause="Developer commit .env file ที่มี credentials โดยไม่ได้ใส่ใน .gitignore และไม่ได้ใช้ SOPS เข้ารหัส",
)

pm.timeline = [
    TimelineEvent("09:15", "Developer push code + .env to GitHub", "Dev Team"),
    TimelineEvent("10:30", "GitHub Secret Scanning Alert triggered", "GitHub"),
    TimelineEvent("10:35", "Security team notified via PagerDuty", "Security"),
    TimelineEvent("10:45", "Incident Commander assigned", "On-call"),
    TimelineEvent("11:00", "Database credentials rotated", "DBA"),
    TimelineEvent("11:30", "Git history rewritten to remove secrets", "DevOps"),
    TimelineEvent("12:00", "Audit logs reviewed no unauthorized access", "Security"),
    TimelineEvent("13:38", "All-clear declared incident closed", "IC"),
]

pm.what_went_well = [
    "GitHub Secret Scanning ตรวจพบเร็ว",
    "Incident Response ทำงานตามขั้นตอน",
    "Credentials rotated ภายใน 30 นาที",
]

pm.what_went_wrong = [
    "ไม่มี SOPS หรือ Secret Management",
    ".gitignore ไม่ครอบคลุม .env",
    "Pre-commit hook ไม่ได้ตรวจ Secrets",
]

pm.action_items = [
    ActionItem("ติดตั้ง SOPS สำหรับทุก Repository", "DevOps", "P0", "2024-01-22"),
    ActionItem("เพิ่ม Pre-commit hook ตรวจ Secrets (gitleaks)", "DevOps", "P0", "2024-01-19"),
    ActionItem("อบรม Security Awareness ทีม Dev", "Security", "P1", "2024-02-01"),
    ActionItem("Audit .gitignore ทุก Repository", "DevOps", "P1", "2024-01-26"),
]

pm.show()

SOPS กับ CI/CD Pipeline

sops_cicd.py — SOPS in CI/CD Pipeline

GitHub Actions workflow

.github/workflows/deploy.yml

name: Deploy

on:

push:

SOPS Encryption กับ Post-mortem Analysis —

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@v4

with:

role-to-arn: arn:aws:iam::123456789:role/deploy-role

aws-region: us-east-1

  • name: Install SOPS

run: |

wget -qO /usr/local/bin/sops \

https://github.com/getsops/sops/releases/download/v3.8.1/sops-v3.8.1.linux.amd64

chmod +x /usr/local/bin/sops

  • name: Decrypt Secrets

run: sops --decrypt k8s/secrets.enc.yaml | kubectl apply -f -

  • name: Deploy

run: kubectl apply -k k8s/overlays/production/

GitOps with Flux CD + SOPS

flux create source git my-app \

--url=https://github.com/org/my-app \

--branch=main

flux create kustomization my-app \

--source=my-app \

--path=./k8s/overlays/production \

--decryption-provider=sops \

--decryption-secret=sops-age

cicd_patterns = {

"GitHub Actions + SOPS": {

"flow": "Push -> Actions -> SOPS Decrypt -> kubectl apply",

"auth": "OIDC + IAM Role (ไม่ต้องเก็บ AWS Keys)",

},

"Flux CD + SOPS": {

"flow": "Git Push -> Flux detect -> SOPS Decrypt -> Apply",

"auth": "age Key ใน Kubernetes Secret",

},

"ArgoCD + SOPS": {

"flow": "Git Push -> ArgoCD Sync -> SOPS Plugin Decrypt",

"auth": "argocd-vault-plugin หรือ KSOPS",

},

"Terraform + SOPS": {

"flow": "terraform plan -> SOPS Provider Decrypt -> Apply",

"auth": "AWS KMS / GCP KMS via Provider",

},

}

print("SOPS CI/CD Patterns:")

for pattern, info in cicd_patterns.items():

print(f"\n [{pattern}]")

print(f" Flow: {info['flow']}")

print(f" Auth: {info['auth']}")

Secret Management Comparison

comparison = {

"SOPS": {"type": "File Encryption", "cost": "Free", "complexity": "Low"},

"HashiCorp Vault": {"type": "Secret Server", "cost": "Free/Enterprise", "complexity": "High"},

"AWS Secrets Manager": {"type": "Cloud Service", "cost": "$0.40/secret/mo", "complexity": "Low"},

"Sealed Secrets": {"type": "K8s Native", "cost": "Free", "complexity": "Medium"},

"External Secrets": {"type": "K8s Operator", "cost": "Free", "complexity": "Medium"},

}

print(f"\n\nSecret Management Comparison:")

for tool, info in comparison.items():

print(f" {tool}: {info['type']} | Cost: {info['cost']} | {info['complexity']}")

Best Practices

  • SOPS + age: ใช้ age สำหรับ Local Development AWS KMS สำหรับ Production
  • Pre-commit: ใช้ gitleaks Pre-commit Hook ตรวจ Secrets ก่อน Commit
  • Key Rotation: หมุนเวียน Keys ทุก 90 วัน sops --rotate
  • Blameless: Post-mortem ต้อง Blameless ไม่โทษบุคคล
  • Action Items: ทุก Post-mortem ต้องมี Action Items พร้อม Owner และ Deadline
  • 5 Whys: ใช้ 5 Whys หา Root Cause ที่แท้จริง

SOPS คืออะไร

Secrets OPerationS เข้ารหัสไฟล์ Secrets Mozilla YAML JSON ENV เข้ารหัสเฉพาะ Value AWS KMS GCP KMS age PGP Git ปลอดภัย