SOPS Encryption Backup Recovery Strategy คืออะไร
SOPS (Secrets OPerationS) เป็น open source tool จาก Mozilla สำหรับเข้ารหัสไฟล์ secrets เช่น YAML, JSON, ENV และ INI โดยเข้ารหัสเฉพาะ values ไม่เข้ารหัส keys ทำให้ยัง diff ได้ใน Git รองรับ encryption backends หลากหลาย ทั้ง AWS KMS, GCP KMS, Azure Key Vault, HashiCorp Vault และ age/PGP Backup Recovery Strategy คือแผนสำรองและกู้คืน encryption keys และ encrypted secrets เพื่อป้องกันการสูญเสียข้อมูลสำคัญเมื่อเกิดเหตุฉุกเฉิน
SOPS พื้นฐาน
# sops_basics.py — SOPS fundamentals
import json
class SOPSBasics:
ENCRYPTION_BACKENDS = {
"age": {
"name": "age (recommended)",
"description": "Modern encryption tool, simple, fast",
"setup": "age-keygen -o key.txt",
"use_case": "Personal projects, small teams",
},
"aws_kms": {
"name": "AWS KMS",
"description": "AWS managed key service",
"setup": "ใช้ KMS key ARN",
"use_case": "AWS infrastructure, team access via IAM",
},
"gcp_kms": {
"name": "GCP Cloud KMS",
"description": "Google Cloud managed key service",
"setup": "ใช้ KMS resource ID",
"use_case": "GCP infrastructure",
},
"azure_kv": {
"name": "Azure Key Vault",
"description": "Azure managed key service",
"setup": "ใช้ Key Vault URL + key name",
"use_case": "Azure infrastructure",
},
"pgp": {
"name": "PGP/GPG",
"description": "Classic encryption, widely supported",
"setup": "gpg --gen-key",
"use_case": "Legacy systems, cross-platform",
},
}
USAGE = """
# SOPS Basic Usage
# Encrypt a file
sops --encrypt --age age1ql3z7hjy54pw3hyww5ayyfg7zqgvc7w3j2elw8zmrj2kg5sfn9aqmcac8p secrets.yaml > secrets.enc.yaml
# Decrypt a file
sops --decrypt secrets.enc.yaml > secrets.yaml
# Edit encrypted file in-place
sops secrets.enc.yaml # Opens in $EDITOR, decrypts → edit → re-encrypts
# Encrypt specific keys only
sops --encrypt --encrypted-regex '^(password|api_key|secret)$' config.yaml
# .sops.yaml — Project configuration
creation_rules:
- path_regex: secrets/.*\\.yaml$
age: age1ql3z7hjy54pw3hyww5ayyfg7zqgvc7w3j2elw8zmrj2kg5sfn9aqmcac8p
- path_regex: prod/.*\\.yaml$
kms: arn:aws:kms:ap-southeast-1:123456:key/abc-def
- path_regex: dev/.*\\.yaml$
age: age1dev...
"""
def show_backends(self):
print("=== Encryption Backends ===\n")
for key, backend in self.ENCRYPTION_BACKENDS.items():
print(f"[{backend['name']}]")
print(f" {backend['description']}")
print(f" Use: {backend['use_case']}")
print()
def show_usage(self):
print("=== SOPS Usage ===")
print(self.USAGE[:500])
sops = SOPSBasics()
sops.show_backends()
sops.show_usage()
Backup Strategy
# backup.py — SOPS backup strategy
import json
class BackupStrategy:
KEY_BACKUP = {
"age_keys": {
"what": "age private key (key.txt)",
"location": "~/.config/sops/age/keys.txt",
"backup_to": ["Password manager (1Password, Bitwarden)", "Encrypted USB drive (offline)", "Printed paper key (safe deposit box)"],
"frequency": "เมื่อสร้าง key ใหม่",
"critical": "ถ้าหาย = ถอดรหัส secrets ไม่ได้อีกเลย",
},
"kms_keys": {
"what": "KMS key metadata + IAM policies",
"location": "AWS/GCP/Azure console",
"backup_to": ["Terraform/IaC state", "Key policy document backup", "Cross-region key replica"],
"frequency": "เมื่อ key policy เปลี่ยน",
"critical": "KMS key ลบแล้ว = มี 7-30 day waiting period",
},
"sops_config": {
"what": ".sops.yaml configuration",
"location": "Git repository root",
"backup_to": ["Git (version controlled)", "Separate backup repo"],
"frequency": "ทุกครั้งที่เปลี่ยน",
"critical": "ต้องรู้ว่า file ไหนใช้ key ไหน encrypt",
},
}
BACKUP_PLAN = """
# backup_plan.py — Automated backup script
import subprocess
import shutil
from datetime import datetime
from pathlib import Path
class SOPSBackup:
def __init__(self, backup_dir="/secure-backup/sops"):
self.backup_dir = Path(backup_dir)
self.timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
def backup_age_keys(self):
src = Path.home() / ".config/sops/age/keys.txt"
if src.exists():
dest = self.backup_dir / f"age-keys-{self.timestamp}.txt.enc"
# Encrypt backup with secondary key
subprocess.run([
'age', '--encrypt',
'--recipient', 'age1backup...',
'-o', str(dest),
str(src)
])
print(f"Age keys backed up: {dest}")
def backup_sops_config(self):
src = Path(".sops.yaml")
if src.exists():
dest = self.backup_dir / f"sops-config-{self.timestamp}.yaml"
shutil.copy2(src, dest)
print(f"SOPS config backed up: {dest}")
def verify_backup(self):
backups = list(self.backup_dir.glob("*"))
print(f"Total backups: {len(backups)}")
for b in sorted(backups)[-5:]:
print(f" {b.name} ({b.stat().st_size} bytes)")
backup = SOPSBackup()
backup.backup_age_keys()
backup.backup_sops_config()
backup.verify_backup()
"""
def show_key_backup(self):
print("=== Key Backup Strategy ===\n")
for key, item in self.KEY_BACKUP.items():
print(f"[{item['what']}]")
print(f" Location: {item['location']}")
print(f" Backup to: {', '.join(item['backup_to'][:2])}")
print(f" ⚠ {item['critical']}")
print()
def show_script(self):
print("=== Backup Script ===")
print(self.BACKUP_PLAN[:500])
backup = BackupStrategy()
backup.show_key_backup()
backup.show_script()
Recovery Procedures
# recovery.py — SOPS recovery procedures
import json
class RecoveryProcedures:
SCENARIOS = {
"lost_age_key": {
"scenario": "สูญเสีย age private key",
"severity": "Critical",
"recovery": [
"1. ค้นหา key จาก backup (password manager, USB, paper)",
"2. วาง key ที่ ~/.config/sops/age/keys.txt",
"3. ทดสอบ: sops --decrypt test-secret.enc.yaml",
"4. ถ้าไม่มี backup: secrets สูญเสียถาวร ต้อง rotate ทั้งหมด",
],
},
"kms_key_deleted": {
"scenario": "AWS KMS key ถูก schedule for deletion",
"severity": "High",
"recovery": [
"1. Cancel deletion: aws kms cancel-key-deletion --key-id ",
"2. Re-enable key: aws kms enable-key --key-id ",
"3. ถ้าเลย waiting period: ต้อง re-encrypt ทุก secrets ด้วย key ใหม่",
],
},
"corrupted_file": {
"scenario": "Encrypted file เสียหาย",
"severity": "Medium",
"recovery": [
"1. Restore จาก Git history: git show HEAD~1:secrets.enc.yaml",
"2. ถ้า Git ไม่มี: restore จาก backup",
"3. Decrypt → verify → re-encrypt",
],
},
"key_rotation": {
"scenario": "ต้อง rotate encryption key",
"severity": "Low (planned)",
"recovery": [
"1. สร้าง key ใหม่: age-keygen -o new-key.txt",
"2. Update .sops.yaml ด้วย key ใหม่",
"3. Re-encrypt: sops updatekeys secrets.enc.yaml",
"4. Verify: sops --decrypt secrets.enc.yaml",
"5. Backup key ใหม่",
],
},
}
def show_scenarios(self):
print("=== Recovery Scenarios ===\n")
for key, scenario in self.SCENARIOS.items():
print(f"[{scenario['scenario']}] Severity: {scenario['severity']}")
for step in scenario["recovery"][:3]:
print(f" {step}")
print()
def rotation_script(self):
print("=== Key Rotation Script ===")
script = """
# rotate_keys.sh
#!/bin/bash
NEW_KEY=$(age-keygen 2>&1 | grep "public key:" | awk '{print $3}')
# Update .sops.yaml with new key
sed -i "s/age1old.../age1/" .sops.yaml
# Re-encrypt all secrets
for f in $(find . -name "*.enc.yaml"); do
sops updatekeys "$f" --yes
echo "Updated: $f"
done
echo "Key rotation complete. Don't forget to backup new key!"
"""
print(script[:400])
recovery = RecoveryProcedures()
recovery.show_scenarios()
recovery.rotation_script()
CI/CD Integration
# cicd.py — SOPS in CI/CD pipelines
import json
class CICDIntegration:
GITHUB_ACTIONS = """
# .github/workflows/deploy.yml
name: Deploy with SOPS
on:
push:
branches: [main]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install SOPS
run: |
curl -LO https://github.com/getsops/sops/releases/download/v3.9.0/sops-v3.9.0.linux.amd64
chmod +x sops-v3.9.0.linux.amd64
sudo mv sops-v3.9.0.linux.amd64 /usr/local/bin/sops
- name: Decrypt secrets
env:
SOPS_AGE_KEY: }
run: |
sops --decrypt secrets/prod.enc.yaml > secrets/prod.yaml
- name: Deploy
run: |
# Use decrypted secrets
export DB_PASSWORD=$(yq '.database.password' secrets/prod.yaml)
./deploy.sh
- name: Cleanup
if: always()
run: rm -f secrets/prod.yaml
"""
ARGOCD = """
# ArgoCD + SOPS (ksops plugin)
# Install ksops as ArgoCD plugin
# secret-generator.yaml
apiVersion: viaduct.ai/v1
kind: ksops
metadata:
name: secret-generator
files:
- secrets/database.enc.yaml
- secrets/api-keys.enc.yaml
# kustomization.yaml
generators:
- secret-generator.yaml
"""
def show_github(self):
print("=== GitHub Actions ===")
print(self.GITHUB_ACTIONS[:500])
def show_argocd(self):
print(f"\n=== ArgoCD + SOPS ===")
print(self.ARGOCD[:300])
cicd = CICDIntegration()
cicd.show_github()
cicd.show_argocd()
Best Practices
# best_practices.py — SOPS best practices
import json
import random
class SOPSBestPractices:
PRACTICES = {
"multi_key": {
"name": "ใช้หลาย keys (Multi-recipient)",
"detail": "Encrypt ด้วยหลาย keys เพื่อ redundancy",
"config": "age: key1, key2, key3 ใน .sops.yaml",
},
"env_separation": {
"name": "แยก keys ตาม environment",
"detail": "Dev key ≠ Staging key ≠ Prod key",
"config": "path_regex ใน .sops.yaml กำหนด key ตาม path",
},
"git_hooks": {
"name": "Pre-commit hooks ป้องกัน plaintext leak",
"detail": "ตรวจว่าไม่มี unencrypted secrets ใน commit",
"config": "detect-secrets, gitleaks, trufflehog",
},
"audit_log": {
"name": "Audit logging",
"detail": "Log ทุกครั้งที่ decrypt/encrypt",
"config": "KMS CloudTrail, custom logging wrapper",
},
}
def show_practices(self):
print("=== Best Practices ===\n")
for key, practice in self.PRACTICES.items():
print(f"[{practice['name']}]")
print(f" {practice['detail']}")
print(f" Config: {practice['config']}")
print()
def security_score(self):
print("=== Security Checklist ===")
checks = [
("Multi-key encryption", random.choice([True, True, False])),
("Key backup verified", random.choice([True, True, False])),
("Env separation", True),
("Pre-commit hooks", random.choice([True, False])),
("Key rotation < 90 days", random.choice([True, False])),
("Audit logging enabled", random.choice([True, True, False])),
]
score = sum(1 for _, v in checks if v)
for name, status in checks:
icon = "✓" if status else "✗"
print(f" [{icon}] {name}")
print(f"\n Score: {score}/{len(checks)}")
bp = SOPSBestPractices()
bp.show_practices()
bp.security_score()
FAQ - คำถามที่พบบ่อย
Q: SOPS กับ Sealed Secrets อันไหนดีกว่า?
A: SOPS: ใช้ได้ทุกที่ (ไม่จำกัด K8s), encrypt ที่ values, ดี diff ใน Git Sealed Secrets: Kubernetes-only, encrypt ทั้ง Secret object ใช้ SOPS: multi-platform, GitOps (ArgoCD/Flux), non-K8s secrets ใช้ Sealed Secrets: K8s only, simple setup, ไม่ต้อง manage keys เอง
Q: age กับ PGP อันไหนดีกว่า?
A: age: modern, simple, fast, recommended สำหรับ projects ใหม่ PGP/GPG: legacy, complex, widely supported ใช้ age: projects ใหม่, personal/team use ใช้ PGP: legacy systems, compliance ที่ต้องการ PGP SOPS recommend age เป็น default ตั้งแต่ v3.7+
Q: ถ้าสูญเสีย key ทั้งหมดจะทำอย่างไร?
A: ถ้าไม่มี backup key: secrets สูญเสียถาวร (encrypted data ถอดรหัสไม่ได้) ต้อง: 1) สร้าง key ใหม่ 2) Rotate ทุก secrets (passwords, API keys, tokens) 3) Re-encrypt ด้วย key ใหม่ ป้องกัน: multi-key encryption, multiple backup locations, test recovery regularly
Q: SOPS ช้าไหม?
A: เร็วมาก Encrypt/decrypt ไฟล์ YAML ขนาดปกติ: < 100ms age: เร็วที่สุด KMS: เพิ่ม network latency (~50-200ms) ไม่มี impact ต่อ CI/CD pipeline ที่มีนัยสำคัญ
