External Secrets Operator

External Secrets Operator เป็น Kubernetes Operator ที่ช่วย Integrate Kubernetes Secret กับ KMS ภายนอกโดย Operator จะทำการ sync credential จาก KMS ภายนอกและทำการ update secret ของ Kubernetes อัตโนมัติ ช่วยให้การจัดการ credential สามารถจัดการแก้ไขจากระบบ KMS ได้จากที่เดียว

สำหรับ NKP สามารถ Enable ได้จากหน้า Applications Catalog

หลังจาก enable แล้ว เราจะต้องทำให้ Vault trust Kubernetes จากนั้นสร้าง SecretStore เพื่อเป็นสะพานเชื่อมระหว่าง Kubernetes และ Vault หลังจากนั้นสร้าง ExternalSecret เพื่อ Sync Secret ให้กับ Kubernetes

ขั้นตอนการทำให้ Vault trust Kubernetes

Shell
# Exec into your vault-0 pod
kubectl exec -it vault-0 -n vault -- sh
vault login hvs.SMpvYTqteXW6Vjup8HWIB972
# 1. Enable Kubernetes auth
vault auth enable kubernetes
# 2. Configure it to talk to the local K8s API
vault write auth/kubernetes/config \
kubernetes_host="https://kubernetes.default.svc:443"
# 3. Create a policy for ESO to read secrets
vault policy write eso-read-policy - <<EOF
path "secret/data/*" {
capabilities = ["read"]
}
EOF
# 4. Create a role that binds the ESO ServiceAccount to the policy
# Assuming ESO is in 'external-secrets' namespace with SA name 'external-secrets'
vault write auth/kubernetes/role/eso-role \
bound_service_account_names=external-secrets \
bound_service_account_namespaces=external-secrets \
policies=eso-read-policy \
ttl=1h

สร้าง SecretStore ด้วย yaml file ดังนี้

YAML
apiVersion: external-secrets.io/v1
kind: ClusterSecretStore
metadata:
name: vault-backend
spec:
provider:
vault:
server: "http://vault.vault.svc.cluster.local:8200"
path: "secret"
version: "v2"
auth:
kubernetes:
# This must match the role name you created in Vault Step 1.4
role: "eso-role"
mountPath: "kubernetes"
serviceAccountRef:
name: "external-secrets"
namespace: "external-secrets"

สร้าง External Secret store สำหรับ sync secret กับ Vault มายัง Kubernetes ด้วย yaml file ดังนี้

YAML
apiVersion: external-secrets.io/v1
kind: ExternalSecret
metadata:
name: my-app-secret
namespace: default
spec:
refreshInterval: "1h" # How often to sync
secretStoreRef:
name: vault-backend
kind: ClusterSecretStore
target:
name: k8s-app-secret # The name of the resulting K8s Secret
creationPolicy: Owner
data:
- secretKey: DATABASE_PASSWORD # Key in the K8s Secret
remoteRef:
key: my-app/db-creds # Path in Vault
property: password # Key inside the Vault JSON

ตรวจสอบว่ามีการสร้าง secret k8s-app-secret ที่ namespace default จากคำสั่งข้างต้นจริง

kubectl get secret
NAME TYPE DATA AGE
k8s-app-secret Opaque 1 31s

ดูข้อมูลของ secret จะพบว่ามีการ sync DATABASE_PASSWORD จาก Vault มาให้

kubectl get secret k8s-app-secret -o yaml
apiVersion: v1
data:
DATABASE_PASSWORD: cGFzc3dvcmQxMjM=
kind: Secret
metadata:
annotations:
kubectl.kubernetes.io/last-applied-configuration: |
{"apiVersion":"external-secrets.io/v1","kind":"ExternalSecret","metadata":{"annotations":{},"name":"my-app-secret","namespace":"default"},"spec":{"data":[{"remoteRef":{"key":"my-app/db-creds","property":"password"},"secretKey":"DATABASE_PASSWORD"}],"refreshInterval":"1h","secretStoreRef":{"kind":"ClusterSecretStore","name":"vault-backend"},"target":{"creationPolicy":"Owner","name":"k8s-app-secret"}}}
reconcile.external-secrets.io/data-hash: 69b12d606fda3589324b709f341cf77986d7128c2fb089486fe84d76
creationTimestamp: "2026-02-05T11:21:32Z"
labels:
reconcile.external-secrets.io/created-by: ea4461eb1de01e986cc8315e7fc9b7bdbdf0204590c521bfc076aa6e
reconcile.external-secrets.io/managed: "true"
name: k8s-app-secret
namespace: default
ownerReferences:
- apiVersion: external-secrets.io/v1
blockOwnerDeletion: true
controller: true
kind: ExternalSecret
name: my-app-secret
uid: 084595bf-cef5-417f-9a98-2f77b82512ec
resourceVersion: "949692"
uid: c0a8c2aa-e0c7-44c3-86bc-dcfacc7190a0
type: Opaque