0

Lab 9 — 🔐 GitOps Secrets

🎯 Mục tiêu

Sau lab này, bạn sẽ:

  • Hiểu tại sao Kubernetes Secret không nên được commit trực tiếp vào Git.
  • Hiểu sự khác nhau giữa Secret, SealedSecret và secret thật trong Kubernetes.
  • Biết cách dùng Sealed Secrets để đưa secret vào GitOps một cách an toàn hơn.
  • Deploy secret thông qua ArgoCD.
  • Biết cách xử lý secret trong môi trường dev / staging / production.

🤔 1. Vấn đề thực tế

Giả sử Todo App của chúng ta cần PostgreSQL:

POSTGRES_USER=todo
POSTGRES_PASSWORD=todo123
POSTGRES_DB=todo

Nếu deploy thủ công, bạn có thể làm:

kubectl create secret generic todo-db-secret \
  -n todo-app \
  --from-literal=username=todo \
  --from-literal=password=todo123

Cách này chạy được.

Nhưng GitOps có một vấn đề:

ArgoCD cần biết toàn bộ desired state từ Git.

Bạn có thể nghĩ đến việc tạo file:

apiVersion: v1
kind: Secret
metadata:
  name: todo-db-secret
stringData:
  username: todo
  password: todo123

Sau đó:

git add .
git commit -m "add database secret"
git push

💥 Đây là một lỗi rất nguy hiểm.

Git repository bây giờ chứa password.

Ngay cả khi bạn xóa file ở commit sau:

Commit 1
  ↓
password123

Commit 2
  ↓
xóa password

password vẫn có thể tồn tại trong Git history.


🧠 2. Hiểu nhanh

2.1. Kubernetes Secret có thực sự "secret"?

Đây là một hiểu lầm rất phổ biến.

Kubernetes Secret dùng để lưu password, token, key... nhưng giá trị trong data thường chỉ được Base64 encode, không phải encryption. Kubernetes cũng lưu Secret trong etcd theo dạng không mã hóa mặc định nếu cluster chưa cấu hình encryption at rest. (Kubernetes)

Ví dụ:

data:
  password: dG9kbzEyMw==

Decode:

echo "dG9kbzEyMw==" | base64 -d

Kết quả:

todo123

Vì vậy:

❌ Base64 ≠ Encryption ❌ Base64 ≠ Security


🔐 3. Sealed Secrets giải quyết vấn đề gì?

Ý tưởng của Sealed Secrets rất đơn giản:

Secret thật
    │
    │ encrypt
    ▼
SealedSecret
    │
    │ Git
    ▼
GitHub
    │
    │ ArgoCD
    ▼
Kubernetes
    │
    │ decrypt
    ▼
Secret

Thay vì commit:

kind: Secret

chúng ta commit:

kind: SealedSecret

SealedSecret chứa dữ liệu đã được mã hóa.

ArgoCD có thể sync file này từ Git.

Sau đó controller của Sealed Secrets trong Kubernetes sẽ giải mã và tạo ra Kubernetes Secret.


🏗️ 4. Architecture

Luồng hoàn chỉnh của Lab:

                    GitOps Repository
                    ┌─────────────────┐
                    │                 │
                    │ SealedSecret    │
                    │ encrypted data  │
                    │                 │
                    └────────┬────────┘
                             │
                             │ Git
                             ▼
                       ┌───────────┐
                       │  ArgoCD   │
                       └─────┬─────┘
                             │
                             │ Sync
                             ▼
                 ┌───────────────────────┐
                 │ Kubernetes Cluster    │
                 │                       │
                 │  Sealed Secrets      │
                 │     Controller        │
                 │          │            │
                 │          │ decrypt    │
                 │          ▼            │
                 │   Kubernetes Secret   │
                 │          │            │
                 │          ▼            │
                 │      Todo Backend     │
                 └───────────────────────┘

Điểm quan trọng:

Git
 │
 │ Không chứa password plaintext
 ▼
SealedSecret
 │
 │ ArgoCD
 ▼
Kubernetes
 │
 ▼
Secret thật

🧪 5. Chuẩn bị Lab

Giả sử chúng ta đang sử dụng:

Namespace:
todo-app

Application:
todo-app

GitOps repository:
todo-gitops/

Kiểm tra cluster:

kubectl get nodes

Kiểm tra ArgoCD:

kubectl get pods -n argocd

Kiểm tra namespace:

kubectl get namespace todo-app

Nếu chưa có:

kubectl create namespace todo-app

🔧 6. Cài đặt Sealed Secrets Controller

6.1. Tại sao cần Controller?

SealedSecret không phải Kubernetes Secret thông thường.

Kubernetes API mặc định không biết cách xử lý:

kind: SealedSecret

Do đó chúng ta cần một controller.

Controller sẽ:

SealedSecret
     │
     ▼
Sealed Secrets Controller
     │
     │ decrypt
     ▼
Secret

6.2. Cài bằng Helm

Thêm repository:

helm repo add sealed-secrets https://bitnami-labs.github.io/sealed-secrets

Update:

helm repo update

Cài controller:

helm install sealed-secrets \
  sealed-secrets/sealed-secrets \
  -n kube-system

Kiểm tra:

kubectl get pods -n kube-system | grep sealed

Bạn cần thấy controller đang Running.

Ví dụ:

sealed-secrets-xxxxx   1/1   Running

🔑 7. Cài kubeseal

kubeseal là CLI dùng để mã hóa Kubernetes Secret thành SealedSecret.

Kiểm tra:

kubeseal --version

Nếu chưa có, cài theo cách phù hợp với OS của bạn.

Trên macOS với Homebrew:

brew install kubeseal

Kiểm tra:

kubeseal --version

🧪 8. Tạo Kubernetes Secret

Bây giờ chúng ta tạo một Secret nhưng chưa commit nó vào Git.

Tạo:

kubectl create secret generic todo-db-secret \
  -n todo-app \
  --from-literal=username=todo \
  --from-literal=password='todo123' \
  --dry-run=client \
  -o yaml > secret.yaml

Kiểm tra:

cat secret.yaml

Bạn sẽ thấy:

apiVersion: v1
kind: Secret
metadata:
  name: todo-db-secret
  namespace: todo-app
type: Opaque
data:
  password: ...
  username: ...

Đây chính là Secret thật.


⚠️ 9. Không commit secret.yaml

Đây là bước rất quan trọng.

File:

secret.yaml

chứa credential của chúng ta.

Không được:

git add secret.yaml

Thay vào đó:

echo "secret.yaml" >> .gitignore

Kiểm tra:

git status

Mục tiêu:

secret.yaml

không xuất hiện trong danh sách file chuẩn bị commit.

💡 Production tip: Nếu credential thật đã từng được commit vào Git, đừng chỉ xóa file. Hãy rotate credential — tức tạo credential mới và vô hiệu hóa credential cũ.


🔐 10. Encrypt Secret thành SealedSecret

Bây giờ chúng ta dùng:

kubeseal

để mã hóa Secret.

kubeseal \
  --format yaml \
  < secret.yaml \
  > sealed-secret.yaml

Kiểm tra:

cat sealed-secret.yaml

Bạn sẽ thấy:

apiVersion: bitnami.com/v1alpha1
kind: SealedSecret
metadata:
  name: todo-db-secret
  namespace: todo-app
spec:
  encryptedData:
    password: Ag...
    username: Ag...

Điểm quan trọng:

password: todo123

đã biến thành:

password: Ag...

🧠 11. Điều gì vừa xảy ra?

Hãy nhìn vào flow:

secret.yaml
    │
    │ chứa
    │ password = todo123
    ▼
 kubeseal
    │
    │ encrypt
    ▼
sealed-secret.yaml
    │
    │ password = Ag...
    ▼
   Git

Bây giờ file:

sealed-secret.yaml

có thể được commit vào Git.

Nhưng:

secret.yaml

thì không.


📦 12. Đưa SealedSecret vào GitOps Repository

Giả sử cấu trúc repository của bạn:

todo-gitops/
├── base/
│   ├── deployment.yaml
│   ├── service.yaml
│   └── kustomization.yaml
│
└── overlays/
    └── dev/
        ├── kustomization.yaml
        └── sealed-secret.yaml

Copy file:

cp sealed-secret.yaml \
  todo-gitops/overlays/dev/

Sau đó:

cd todo-gitops

Commit:

git add overlays/dev/sealed-secret.yaml
git commit -m "add sealed secret for todo database"
git push

👀 13. See Result — Kiểm tra Git

Mở GitHub repository.

Bạn có thể nhìn thấy:

kind: SealedSecret

spec:
  encryptedData:
    password: Ag...
    username: Ag...

Nhưng bạn không thấy:

todo123

Đây chính là mục tiêu.

GitHub
  │
  │ encrypted
  ▼
SealedSecret

🚀 14. Deploy bằng ArgoCD

Bây giờ ArgoCD sẽ đọc:

Git
 │
 ▼
SealedSecret

và apply nó vào Kubernetes.

Nếu Application đã cấu hình Auto Sync:

Git push
   │
   ▼
ArgoCD detects change
   │
   ▼
Sync
   │
   ▼
SealedSecret created

Nếu chưa bật Auto Sync, có thể sync thủ công:

argocd app sync todo-app

Hoặc vào:

ArgoCD UI
   ↓
todo-app
   ↓
SYNC

🔎 15. Kiểm tra SealedSecret

Chạy:

kubectl get sealedsecret -n todo-app

Kết quả:

NAME              STATUS
todo-db-secret    True

Tiếp tục kiểm tra Secret:

kubectl get secret -n todo-app

Bạn sẽ thấy:

todo-db-secret

Điều này có nghĩa:

Git
 │
 ▼
SealedSecret
 │
 ▼
Sealed Secrets Controller
 │
 ▼
Secret

🎉 GitOps Secrets đã hoạt động.


🧪 16. Kiểm tra Secret được tạo

Xem Secret:

kubectl describe secret todo-db-secret -n todo-app

Bạn sẽ thấy:

Name:         todo-db-secret
Namespace:    todo-app
Type:         Opaque

Data
====
password:  7 bytes
username:  4 bytes

Kubernetes không hiển thị plaintext khi dùng describe.

Nếu thực sự cần kiểm tra:

kubectl get secret todo-db-secret \
  -n todo-app \
  -o jsonpath='{.data.password}' | base64 -d

Kết quả:

todo123

⚠️ Chỉ decode khi debug. Đừng đưa command kiểu này vào script/log production nếu không cần thiết.


🔌 17. Cho Application sử dụng Secret

Secret chỉ hữu ích khi Application sử dụng nó.

Ví dụ Deployment:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: todo-backend
  namespace: todo-app
spec:
  template:
    spec:
      containers:
        - name: backend
          image: todo-backend:1.0
          env:
            - name: DB_USERNAME
              valueFrom:
                secretKeyRef:
                  name: todo-db-secret
                  key: username

            - name: DB_PASSWORD
              valueFrom:
                secretKeyRef:
                  name: todo-db-secret
                  key: password

Flow:

SealedSecret
     │
     ▼
Secret
     │
     ▼
Deployment
     │
     ▼
Pod
     │
     ▼
DB_PASSWORD

Kubernetes hỗ trợ sử dụng Secret thông qua environment variable hoặc volume mount. (Kubernetes)


🧪 18. See Result — Kiểm tra Pod

Kiểm tra:

kubectl get pods -n todo-app

Sau đó:

kubectl describe pod <pod-name> -n todo-app

Bạn sẽ thấy:

Environment:
  DB_USERNAME:  <set to the key 'username' in secret 'todo-db-secret'>
  DB_PASSWORD:  <set to the key 'password' in secret 'todo-db-secret'>

Điều đó chứng minh:

Git
 ↓
ArgoCD
 ↓
SealedSecret
 ↓
Secret
 ↓
Pod
 ↓
Application

🧠 19. Hiểu bản chất: Secret vs SealedSecret

Đây là phần quan trọng nhất của Lab.

Secret SealedSecret
Kubernetes hiểu trực tiếp?
Có thể chứa plaintext?
Base64? Không phải cơ chế bảo mật chính
Commit vào Git?
Controller cần thiết?
Dùng cho Application?
Mục đích Runtime Secret GitOps representation

Có thể hiểu đơn giản:

SealedSecret
     │
     │ "bản đã khóa"
     ▼
Secret
     │
     │ "bản đã mở khóa"
     ▼
Application

🔐 20. Public Key và Private Key

Sealed Secrets hoạt động dựa trên encryption key.

Có thể hình dung:

             Public Key
                │
                ▼
Secret ──────► Encrypt
                │
                ▼
          SealedSecret
                │
                │ Git
                ▼
             GitHub


Kubernetes
    │
    ▼
Private Key
    │
    ▼
Decrypt
    │
    ▼
 Secret

Điểm quan trọng:

Developer có thể dùng public key để seal secret, nhưng private key dùng để giải mã phải được bảo vệ trong cluster.

Vì vậy, đừng đưa private key của Sealed Secrets controller vào Git.


⚠️ 21. Một vấn đề production rất quan trọng

Giả sử cluster bị mất.

Bạn dựng cluster mới:

Old Cluster
    │
    └── Sealed Secrets private key

Nếu private key cũng mất:

Git
 │
 ▼
SealedSecret
 │
 X
Cannot decrypt

Do đó production cần có chiến lược:

Sealed Secrets private key
        │
        ▼
Secure Backup
        │
        ├── encrypted backup
        ├── restricted access
        └── disaster recovery

Đừng nhầm:

Backup Git repository ≠ backup secret decryption key.


🌎 22. Secrets giữa Dev / Staging / Production

Một lỗi phổ biến là dùng chung secret:

dev
 │
 └── DB_PASSWORD=abc

staging
 │
 └── DB_PASSWORD=abc

production
 │
 └── DB_PASSWORD=abc

Không nên.

Nên có credential riêng:

             GitOps
               │
       ┌───────┼────────┐
       ▼       ▼        ▼
      dev   staging    prod
       │       │        │
     Secret  Secret   Secret
       │       │        │
      DB     DB       DB

Ví dụ:

dev
  todo-db-secret

staging
  todo-db-secret

production
  todo-db-secret

Tên có thể giống nhau, nhưng giá trị secret khác nhau.


🧩 23. GitOps Repository nên tổ chức thế nào?

Một cấu trúc đơn giản:

todo-gitops/
│
├── base/
│   ├── deployment.yaml
│   ├── service.yaml
│   └── kustomization.yaml
│
└── overlays/
    │
    ├── dev/
    │   ├── kustomization.yaml
    │   └── sealed-secret.yaml
    │
    ├── staging/
    │   ├── kustomization.yaml
    │   └── sealed-secret.yaml
    │
    └── production/
        ├── kustomization.yaml
        └── sealed-secret.yaml

Điểm quan trọng:

base/
   │
   └── Application configuration

overlays/
   │
   ├── dev/
   │
   ├── staging/
   │
   └── production/
          │
          └── Environment-specific secrets

🆚 24. Sealed Secrets vs External Secrets

Sealed Secrets không phải giải pháp duy nhất.

Trong production, bạn sẽ thường gặp thêm External Secrets Operator (ESO).

ESO cho phép Kubernetes lấy secret từ external secret manager như AWS Secrets Manager, Google Secret Manager, Azure Key Vault, HashiCorp Vault... rồi tạo Kubernetes Secret. (External Secrets)

Sealed Secrets

Git
 │
 ▼
Encrypted Secret
 │
 ▼
ArgoCD
 │
 ▼
Kubernetes

External Secrets

Git
 │
 ▼
ExternalSecret
 │
 ▼
ArgoCD
 │
 ▼
External Secret Manager
 │
 ▼
Kubernetes Secret

Có thể hiểu:

Sealed Secrets External Secrets
Secret thật nằm ở đâu? Kubernetes Secret Manager
Git chứa gì? Encrypted secret Reference
Dễ setup Lab? ⭐⭐⭐⭐⭐ ⭐⭐⭐
Phù hợp Local Lab
Production lớn ⭐⭐⭐⭐⭐
Rotation Hạn chế hơn Tốt
Cloud Secret Manager Không bắt buộc Rất phù hợp

👉 Với Lab 9, Sealed Secrets giúp bạn hiểu bản chất GitOps Secrets trước.

Trong hệ thống production lớn, đặc biệt khi đã sử dụng AWS/Azure/GCP, External Secrets + Secret Manager thường là hướng đáng cân nhắc hơn.


🧪 25. Exercise — Tự thực hành

Bây giờ hãy thử tự làm lại toàn bộ flow.

Exercise 1 — Tạo Secret

Tạo:

todo-api-secret

với:

API_KEY=dev-secret-key

Exercise 2 — Seal Secret

Tạo:

sealed-api-secret.yaml

và đảm bảo:

dev-secret-key

không xuất hiện trong file.


Exercise 3 — GitOps

Commit:

sealed-api-secret.yaml

vào GitOps repository.

Sau đó để ArgoCD tự sync.


Exercise 4 — Verify

Kiểm tra:

kubectl get sealedsecret -n todo-app

và:

kubectl get secret -n todo-app

Kết quả mong muốn:

SealedSecret
     │
     ▼
Secret
     │
     ▼
Pod

🧯 26. Troubleshooting

❌ SealedSecret không tạo Secret

Kiểm tra:

kubectl get sealedsecret -n todo-app

Sau đó:

kubectl describe sealedsecret todo-db-secret \
  -n todo-app

Kiểm tra controller:

kubectl get pods -n kube-system | grep sealed

Xem log:

kubectl logs \
  -n kube-system \
  -l app.kubernetes.io/name=sealed-secrets

❌ ArgoCD báo OutOfSync

Kiểm tra:

argocd app get todo-app

Hoặc xem trực tiếp trên ArgoCD UI:

Application
    ↓
Sync Status
    ↓
Diff

Đặc biệt kiểm tra:

namespace
name
kind
apiVersion

kubeseal không seal được Secret

Kiểm tra controller có hoạt động:

kubectl get pods -n kube-system | grep sealed

Sau đó kiểm tra certificate:

kubeseal --fetch-cert

Nếu cluster có vấn đề về connectivity, kubeseal có thể không lấy được certificate của controller.


🧠 27. Production Tips

🔥 Tip 1 — Đừng commit Secret plaintext

Sai:

secret.yaml
password: production123

Đúng hơn:

sealed-secret.yaml
encryptedData:
  password: Ag...

🔥 Tip 2 — Base64 không bảo vệ secret

Đừng nghĩ:

data:
  password: cHJvZHVjdGlvbjEyMw==

là an toàn.

Base64 chỉ là encoding. Kubernetes cũng cảnh báo rằng Base64 không cung cấp confidentiality. (Kubernetes)


🔥 Tip 3 — Không dùng chung credential giữa environment

Không nên:

dev password = prod password

Nếu dev bị compromise thì production cũng có nguy cơ.


🔥 Tip 4 — Rotate credential

Secret production nên có lifecycle:

Create
  ↓
Use
  ↓
Rotate
  ↓
Revoke old credential

Không nên để một password production tồn tại mãi mãi.


🔥 Tip 5 — GitOps không có nghĩa là "mọi thứ đều phải nằm trong Git"

GitOps nói rằng Git là source of truth cho desired state, nhưng không có nghĩa bạn phải đưa plaintext credentials vào Git.

ArgoCD được thiết kế để lấy desired application state từ Git và đồng bộ state đó vào Kubernetes. (Argo CD)

Với secret, ta có thể lưu:

Encrypted representation

hoặc:

Reference → External Secret Manager

thay vì:

Plaintext password

🎯 28. Tổng kết

Trước Lab:

Git
 │
 ├── deployment.yaml
 ├── service.yaml
 └── ❌ password

Sau Lab:

Git
 │
 ├── deployment.yaml
 ├── service.yaml
 └── sealed-secret.yaml
          │
          │ encrypted
          ▼
       ArgoCD
          │
          ▼
   SealedSecret Controller
          │
          │ decrypt
          ▼
        Secret
          │
          ▼
        Pod

Điều quan trọng nhất cần nhớ:

Không commit Kubernetes Secret plaintext vào Git.

Và cũng đừng học thuộc rằng:

"SealedSecret = Secret an toàn tuyệt đối."

Hãy nhớ bản chất:

GitOps
  +
Secret Management
  =
Encrypted Secret / External Secret Reference
  +
ArgoCD
  +
Kubernetes Secret

Đó mới là cách tư duy khi xây dựng GitOps thực tế.

🏁 Sau Lab 9

Bạn đã có:

Lab 1  → GitOps Fundamentals
Lab 2  → GitOps Repository
Lab 3  → ArgoCD
Lab 4  → Deploy Application
Lab 5  → Sync & Self-Healing
Lab 6  → Kustomize
Lab 7  → Helm
Lab 8  → Multi-Environment
Lab 9  → 🔐 GitOps Secrets

Lab tiếp theo — Lab 10: ArgoCD Application & Project sẽ đi vào một vấn đề production quan trọng hơn: làm sao quản lý nhiều team, nhiều application và giới hạn quyền deploy của từng team bằng AppProject + RBAC.


All rights reserved

Viblo
Hãy đăng ký một tài khoản Viblo để nhận được nhiều bài viết thú vị hơn.
Đăng kí