SOPS + Pod Scheduling
SOPS Encryption Pod Scheduling Kubernetes Secrets GitOps Flux ArgoCD age AWS KMS Node Affinity Taints RBAC Secret Store CSI
| Tool | Encryption | GitOps | Key Management | Complexity |
|---|---|---|---|---|
| SOPS | Encrypt Values in YAML/JSON | Flux native / ArgoCD plugin | age KMS Vault PGP | ปานกลาง |
| Sealed Secrets | Encrypt whole Secret | kubectl apply | Controller Key | ต่ำ |
| External Secrets | Sync from External Store | Operator | AWS SM / Vault / GCP SM | ปานกลาง |
| Vault CSI | Mount from Vault | CSI Driver | HashiCorp Vault | สูง |
SOPS Configuration
# === SOPS Encrypt/Decrypt ===
# Install
# brew install sops age
# # or download from GitHub releases
# Generate age key
# age-keygen -o key.txt
# # Public key: age1ql3z7hjy54pw3hyww5ayyfg7zqgvc7w3j2elw8zmrj2kg5sfn9aqmcac8p
# # Private key in key.txt: AGE-SECRET-KEY-xxx
# .sops.yaml (Project Root)
# creation_rules:
# - path_regex: secrets\..*\.yaml$
# key_groups:
# - age:
# - age1ql3z7hjy54pw3hyww5ayyfg7zqgvc7w3j2elw8zmrj2kg5sfn9aqmcac8p
# - path_regex: production/secrets\..*\.yaml$
# key_groups:
# - age:
# - age1ql3z7hjy54pw3hyww5ayyfg7zqgvc7w3j2elw8zmrj2kg5sfn9aqmcac8p
# aws_kms:
# - arn: arn:aws:kms:ap-southeast-1:123456:key/xxx
# Encrypt
# sops --encrypt secrets.dev.yaml > secrets.dev.enc.yaml
# # or with .sops.yaml: sops --encrypt --in-place secrets.dev.yaml
# Decrypt
# export SOPS_AGE_KEY_FILE=key.txt
# sops --decrypt secrets.dev.enc.yaml
# Edit (decrypt → edit → re-encrypt automatically)
# sops secrets.dev.enc.yaml
from dataclasses import dataclass
@dataclass
class SOPSConfig:
setting: str
value: str
purpose: str
security_note: str
configs = [
SOPSConfig(".sops.yaml creation_rules",
"path_regex + key_groups",
"กำหนด Rule Encrypt อัตโนมัติตาม Path",
"แยก Key ตาม Environment dev/staging/prod"),
SOPSConfig("age key",
"age1xxx (public) AGE-SECRET-KEY-xxx (private)",
"Encrypt/Decrypt Key ง่าย เร็ว",
"เก็บ Private Key ปลอดภัย ไม่ Commit ไป Git"),
SOPSConfig("AWS KMS",
"arn:aws:kms:region:account:key/id",
"ใช้ AWS KMS จัดการ Key ไม่ต้องเก็บ Key เอง",
"IAM Role กำหนดสิทธิ์ Encrypt/Decrypt"),
SOPSConfig("Multiple Keys",
"key_groups with age + kms",
"Encrypt ด้วยหลาย Key ถ้า Key หนึ่งหาย ใช้อีกตัว",
"Production ควรใช้อย่างน้อย 2 Key"),
SOPSConfig("encrypted_regex",
"^(data|stringData)$",
"Encrypt เฉพาะ Field ที่ต้องการ",
"ลด Noise ไม่ Encrypt metadata"),
]
print("=== SOPS Config ===")
for c in configs:
print(f" [{c.setting}] = {c.value}")
print(f" Purpose: {c.purpose}")
print(f" Security: {c.security_note}")
GitOps + Flux Integration
# === Flux CD + SOPS ===
# Store age private key as Kubernetes Secret
# kubectl create secret generic sops-age \
# --namespace=flux-system \
# --from-file=age.agekey=key.txt
# Flux Kustomization with SOPS decryption
# apiVersion: kustomize.toolkit.fluxcd.io/v1
# kind: Kustomization
# metadata:
# name: my-app
# namespace: flux-system
# spec:
# interval: 5m
# path: ./clusters/production
# prune: true
# sourceRef:
# kind: GitRepository
# name: my-repo
# decryption:
# provider: sops
# secretRef:
# name: sops-age
# Encrypted Secret in Git
# apiVersion: v1
# kind: Secret
# metadata:
# name: app-secrets
# stringData:
# DATABASE_URL: ENC[AES256_GCM, data:xxx, type:str]
# API_KEY: ENC[AES256_GCM, data:yyy, type:str]
# sops:
# age:
# - recipient: age1ql3z7hjy54pw3hyww5ayyfg7zqgvc7w3j2elw8zmrj2kg5sfn9aqmcac8p
# enc: |
# -----BEGIN AGE ENCRYPTED FILE-----
@dataclass
class GitOpsWorkflow:
step: int
action: str
who: str
command: str
security: str
workflow = [
GitOpsWorkflow(1, "Developer แก้ไข Secret",
"Developer",
"sops edit secrets.enc.yaml",
"Decrypt ใน Memory แก้ Re-encrypt อัตโนมัติ"),
GitOpsWorkflow(2, "Commit Encrypted File",
"Developer",
"git add secrets.enc.yaml && git commit && git push",
"Git เก็บ Encrypted Values CI ไม่เห็น Plain Text"),
GitOpsWorkflow(3, "Flux Detect Change",
"Flux Controller",
"GitRepository reconcile (poll/webhook)",
"Flux ดึง Encrypted File จาก Git"),
GitOpsWorkflow(4, "Flux Decrypt Secrets",
"Flux + SOPS",
"Decrypt ด้วย age key ใน sops-age Secret",
"Decrypt เกิดขึ้นใน Flux Controller เท่านั้น"),
GitOpsWorkflow(5, "Apply to Kubernetes",
"Flux Controller",
"kubectl apply (Decrypted Secret)",
"Secret ถูกเก็บใน etcd (Encrypted at Rest)"),
GitOpsWorkflow(6, "Pod Mount Secret",
"Kubelet",
"Mount Secret เป็น Volume หรือ Env",
"Pod อ่าน Plain Text Secret ได้"),
]
print("=== GitOps Workflow ===")
for w in workflow:
print(f" Step {w.step}: {w.action} ({w.who})")
print(f" Command: {w.command}")
print(f" Security: {w.security}")
Pod Scheduling & Security
# === Secure Pod Scheduling ===
# Node Affinity for Sensitive Workloads
# spec:
# affinity:
# nodeAffinity:
# requiredDuringSchedulingIgnoredDuringExecution:
# nodeSelectorTerms:
# - matchExpressions:
# - key: security-tier
# operator: In
# values: ["high"]
# tolerations:
# - key: "sensitive"
# operator: "Equal"
# value: "true"
# effect: "NoSchedule"
# RBAC - Least Privilege for Secrets
# apiVersion: rbac.authorization.k8s.io/v1
# kind: Role
# metadata:
# name: secret-reader
# namespace: my-app
# rules:
# - apiGroups: [""]
# resources: ["secrets"]
# resourceNames: ["app-secrets"] # specific secret only
# verbs: ["get"]
@dataclass
class SecurityControl:
control: str
mechanism: str
config: str
protects_against: str
controls = [
SecurityControl("Node Affinity",
"Schedule sensitive Pods on labeled Nodes only",
"nodeAffinity: security-tier=high",
"Secrets ไม่ถูกส่งไป Node ที่ไม่ปลอดภัย"),
SecurityControl("Taints/Tolerations",
"Dedicated Nodes for sensitive workloads",
"taint: sensitive=true:NoSchedule",
"Non-sensitive Pods ไม่รันบน Secure Nodes"),
SecurityControl("RBAC Least Privilege",
"ServiceAccount อ่านเฉพาะ Secret ที่ต้องการ",
"Role: get specific secret by resourceNames",
"Pod อื่นอ่าน Secrets ของ App นี้ไม่ได้"),
SecurityControl("Network Policy",
"จำกัด Network ของ Pod ที่ใช้ Secrets",
"NetworkPolicy: ingress/egress rules",
"ป้องกัน Data Exfiltration"),
SecurityControl("Pod Security Standards",
"Restricted Mode สำหรับ Sensitive Pods",
"PodSecurity: restricted",
"ป้องกัน Container Escape Privilege Escalation"),
SecurityControl("etcd Encryption",
"Encrypt Secrets at Rest ใน etcd",
"EncryptionConfiguration: aescbc/secretbox",
"ถ้า etcd ถูก Compromise Secrets ยังปลอดภัย"),
]
print("=== Security Controls ===")
for s in controls:
print(f" [{s.control}] {s.mechanism}")
print(f" Config: {s.config}")
print(f" Protects: {s.protects_against}")
เคล็ดลับ
- Multiple Keys: ใช้อย่างน้อย 2 Key (age + KMS) สำหรับ Production
- .sops.yaml: กำหนด Rule ชัดเจน แยก Key ตาม Environment
- Flux: ใช้ Flux SOPS Integration Decrypt อัตโนมัติ
- RBAC: ตั้ง Least Privilege resourceNames เฉพาะ Secret ที่ต้องการ
- Rotate: หมุน Key เป็นระยะ sops updatekeys re-encrypt ทุกไฟล์
SOPS คืออะไร
Secrets OPerationS Encrypt Decrypt YAML JSON age AWS KMS Vault PGP Git Safe Encrypt Values Only .sops.yaml Multiple Keys Mozilla
Encrypt/Decrypt ทำอย่างไร
sops --encrypt --decrypt --edit age-keygen .sops.yaml creation_rules path_regex key_groups SOPS_AGE_KEY_FILE Multiple Keys AWS KMS
GitOps Integration ทำอย่างไร
Flux CD decryption provider sops secretRef age Secret ArgoCD Plugin Helm Secrets Commit Encrypted Git CI ไม่เห็น Plain Text
Pod Scheduling เกี่ยวอย่างไร
Node Affinity security-tier Taints Tolerations RBAC Least Privilege Network Policy Pod Security etcd Encryption Secret Store CSI
สรุป
SOPS Encryption Pod Scheduling Kubernetes age KMS GitOps Flux ArgoCD Node Affinity RBAC etcd Encryption Secret Store Production
