Bối cảnh & Lý do: Giải bài toán bảo mật Secret trong GitOps
GitOps coi Git repository là nguồn chân lý duy nhất (single source of truth) cho hạ tầng của bạn. Các công cụ continuous delivery như ArgoCD hay Flux liên tục đồng bộ trạng thái cluster dựa trên các manifest được push lên nhánh main. Tuy nhiên, các manifest Secret tiêu chuẩn của Kubernetes lại tồn tại một lỗ hổng nghiêm trọng: mã hóa base64 thực chất chỉ là làm mờ (obfuscation), chứ không phải mã hóa bảo mật (encryption). Việc đẩy các manifest secret chưa mã hóa lên Git sẽ làm lộ mật khẩu database production, Stripe API key hay TLS private certificate cho bất kỳ ai có quyền clone repository.
Các kiến trúc phức tạp như HashiCorp Vault đi kèm sidecar injector hay External Secrets Operator hoạt động rất tốt trên các nền tảng quy mô lớn, nhưng lại mang đến gánh nặng vận hành không nhỏ. Bitnami Sealed Secrets giải quyết bài toán này bằng mô hình mã hóa bất đối xứng tinh gọn và hiệu quả.
Developer mã hóa Secret Kubernetes tiêu chuẩn ở môi trường local bằng RSA public key 4096-bit của cluster controller. Custom resource sinh ra, gọi là SealedSecret, hoàn toàn an toàn để commit lên GitHub repository dù là public hay private. Chỉ controller đang chạy bên trong cluster của bạn mới nắm giữ private key cần thiết để giải mã (unseal) nó trở lại thành một Kubernetes Secret hoạt động bình thường.
Trong các production pipeline, mô hình này cho phép lưu trữ secret đã mã hóa ngay cạnh manifest deployment của ứng dụng. Quá trình rollback luôn đảm bảo tính nguyên tử (atomic). Bạn loại bỏ được các script tạo secret thủ công tiềm ẩn nhiều rủi ro, đồng thời ngăn chặn triệt để tình trạng lệch cấu hình (configuration drift) giữa các môi trường.
Cài đặt: Triển khai Controller và Client
Sealed Secrets bao gồm hai thành phần gọn nhẹ: operator trên cluster và CLI (kubeseal) trên máy trạm.
1. Triển khai Controller
Khi khởi động lần đầu, controller sẽ kiểm tra xem cặp key đã tồn tại hay chưa. Nếu chưa, nó sẽ tạo mới một cặp RSA key 4096-bit và lưu dưới dạng một Kubernetes Secret thông thường bên trong namespace triển khai. Hãy cài đặt controller vào một system namespace như kube-system bằng Helm:
# Thêm Helm repository của Sealed Secrets
helm repo add sealed-secrets https://bitnami-labs.github.io/sealed-secrets
helm repo update
# Cài đặt controller vào kube-system
helm install sealed-secrets-controller sealed-secrets/sealed-secrets \
--namespace kube-system \
--set-string ingress.enabled=false
Chờ khoảng 10 giây để pod ổn định, sau đó kiểm tra xác nhận controller đã tạo cặp key ban đầu:
kubectl get pods -n kube-system -l app.kubernetes.io/name=sealed-secrets
kubectl logs -n kube-system -l app.kubernetes.io/name=sealed-secrets | grep -i "key"
2. Cài đặt kubeseal CLI
Kỹ sư có thể chạy binary kubeseal ở local hoặc bên trong GitHub Actions runner để mã hóa payload mà không cần gọi trực tiếp tới Kubernetes API server.
# Cài đặt kubeseal trên Linux (AMD64)
KUBESEAL_VERSION="0.28.0"
curl -L "https://github.com/bitnami-labs/sealed-secrets/releases/download/v${KUBESEAL_VERSION}/kubeseal-${KUBESEAL_VERSION}-linux-amd64.tar.gz" | tar -xvzf -
sudo install -m 755 kubeseal /usr/local/bin/kubeseal
# Kiểm tra binary đã hoạt động
kubeseal --version
Mẹo chuyên nghiệp: Tuyệt đối không yêu cầu developer phải có quyền truy cập trực tiếp vào cluster chỉ để mã hóa các file cấu hình. Hãy lấy public certificate một lần rồi commit vào repository hoặc nhúng sẵn vào image của CI runner:
kubeseal --fetch-cert \
--controller-name=sealed-secrets-controller \
--controller-namespace=kube-system > pub-sealed-secrets.pem
Cấu hình: Mã hóa và phân vùng Scope cho Secret
Các lỗi liên quan đến scope là nguyên nhân phổ biến nhất dẫn đến sự cố giải mã. Bitnami Sealed Secrets cung cấp ba chế độ scope bảo mật khác nhau để kiểm soát phạm vi giải mã của secret:
- strict (mặc định): Khóa chặt phạm vi giải mã theo đúng tên secret và namespace đích. Việc đổi tên secret hoặc chuyển manifest sang namespace khác sẽ khiến quá trình giải mã thất bại ngay lập tức.
- namespace-wide: Cho phép đổi tên secret, nhưng bắt buộc phải nằm trong đúng namespace chỉ định.
- cluster-wide: Cho phép áp dụng secret đã giải mã dưới bất kỳ tên nào trên mọi namespace trong cluster. Nên hạn chế dùng và chỉ sử dụng cho các cấu hình chia sẻ chung như wildcard SSL certificate.
Ví dụ mã hóa từng bước
Đầu tiên, tạo một manifest Secret tiêu chuẩn bằng lệnh dry-run phía client ở local. Tránh apply trực tiếp manifest dạng thô (raw) này lên cluster.
# Tạo manifest thô ở local mà không gửi request tới API server
kubectl create secret generic db-credentials \
--from-literal=DB_USER=postgres \
--from-literal=DB_PASSWORD="4f8a9e2b!C7x#9Lq" \
--namespace=backend \
--dry-run=client -o yaml > raw-secret.yaml
Tiếp theo, mã hóa file thành một SealedSecret bằng public certificate đã lưu. Ở đây, chúng ta cấu hình tường minh --scope=strict:
kubeseal --format=yaml \
--cert=pub-sealed-secrets.pem \
--scope=strict < raw-secret.yaml > sealed-db-credentials.yaml
# Ghi đè và xóa ngay file chưa mã hóa
shred -u raw-secret.yaml
File đầu ra sealed-db-credentials.yaml chứa các ciphertext được mã hóa bằng AES-GCM và bọc trong envelope RSA:
apiVersion: bitnami.com/v1alpha1
kind: SealedSecret
metadata:
name: db-credentials
namespace: backend
spec:
encryptedData:
DB_PASSWORD: AgB+x0aB8...[TRUNCATED_CIPHERTEXT]...
DB_USER: AgCv19Pz...[TRUNCATED_CIPHERTEXT]...
template:
metadata:
name: db-credentials
namespace: backend
Cập nhật một SealedSecret hiện có
Bạn cần rotate một mật khẩu riêng lẻ? Bạn không cần phải dựng lại toàn bộ manifest từ đầu. Hãy dùng cờ --merge-into để cập nhật trực tiếp từng key tại chỗ:
kubectl create secret generic db-credentials \
--from-literal=DB_PASSWORD="UpdatedPass_2026_xYz" \
--namespace=backend \
--dry-run=client -o yaml | \
kubeseal --format=yaml --merge-into sealed-db-credentials.yaml --cert=pub-sealed-secrets.pem
Kiểm tra & Giám sát: Xác minh giải mã và xoay vòng Key
Sau khi công cụ GitOps apply manifest lên cluster, hãy kiểm tra xem controller đã giải mã payload thành công và tạo object Secret tương ứng hay chưa.
1. Kiểm tra hoạt động của Controller
Kiểm tra condition của custom resource:
kubectl get sealedsecret db-credentials -n backend -o yaml
Quan sát khối status.conditions. Quá trình unseal thành công sẽ hiển thị Type: Synced và Status: "True" trong vòng 2 đến 5 giây. Nếu unseal thất bại—thường do gõ nhầm namespace khi dùng strict scope—phần condition sẽ ghi rõ chi tiết lỗi giải mã.
Xác nhận secret đích đã được tạo và kiểm tra nội dung sau khi giải mã:
kubectl get secret db-credentials -n backend
kubectl get secret db-credentials -n backend -o jsonpath="{.data.DB_USER}" | base64 --decode
2. Sao lưu Key và Runbook vận hành
Hãy coi private key của controller là tài sản khắc phục thảm họa (disaster recovery) cấp độ 1 (Tier-1). Nếu bạn làm mất private key đang hoạt động trong quá trình khôi phục hệ thống, mọi manifest SealedSecret trong lịch sử Git của bạn sẽ trở thành dữ liệu rác không thể cứu vãn.
- Sao lưu private key ngay lập tức: Xuất secret chứa active key và lưu trữ trong trình quản lý mật khẩu offline hoặc secret vault chuyên dụng:
kubectl get secret -n kube-system \
-l sealedsecrets.bitnami.com/sealed-secret-key=active \
-o yaml > sealed-secrets-key-backup.yaml
- Tự động xoay vòng Key (Key Rotation): Sealed Secrets mặc định tự động xoay vòng signing key đang hoạt động sau mỗi 30 ngày. Controller vẫn duy trì các private key cũ trên cluster để giải mã các payload cũ trong lịch sử. Khi khôi phục cluster từ đầu (bare metal), hãy khôi phục toàn bộ các private key secret đã sao lưu trước khi deploy repository của ứng dụng.
- Giám sát với Prometheus: Controller xuất metric tại cổng
8080/metrics. Hãy cấu hình một alert rule với biểu thứcrate(sealed_secrets_controller_unseal_errors_total[5m]) > 0. Quy tắc này giúp phát hiện sớm các manifest bị hỏng hoặc thiếu key giải mã trước khi ảnh hưởng đến người dùng cuối.

