Quản lý Kubernetes Secret với External Secrets Operator (ESO): Đồng bộ Secret từ AWS Secrets Manager và HashiCorp Vault

Lưu trữ API key, thông tin đăng nhập cơ sở dữ liệu hoặc chứng chỉ TLS dưới dạng văn bản thuần túy (plaintext) bên trong manifest Kubernetes luôn là con đường ngắn nhất dẫn đến sự cố bảo mật. Một sai lầm phổ biến là commit Kubernetes Secret đã mã hóa base64 lên Git vì lầm tưởng rằng làm mờ dữ liệu (obfuscation) đồng nghĩa với mã hóa (encryption). Thực tế hoàn toàn không phải vậy. Base64 đơn thuần chỉ là một phương thức mã hóa định dạng (encoding scheme). Bất kỳ ai có quyền đọc repository đều có thể giải mã thông tin đăng nhập production của bạn chỉ bằng một lệnh shell trong chưa đầy hai giây.

Điều này làm bộc lộ mâu thuẫn rõ rệt trong hạ tầng hiện đại. GitOps đòi hỏi trạng thái mang tính khai báo (declarative) và được quản lý theo phiên bản (version-controlled). Trong khi đó, các tiêu chuẩn tuân thủ bảo mật lại yêu cầu secret phải nằm trong các vault chuyên dụng kiên cố, có nhật ký kiểm toán (audit logs) chi tiết và hỗ trợ tự động xoay vòng khóa (rotation). Để giải quyết mâu thuẫn này mà không làm lộ thông tin đăng nhập vào lịch sử commit, chúng ta cần một tầng đồng bộ hóa đáng tin cậy.

So sánh các phương pháp quản lý Secret trong Kubernetes

Các platform engineer thường cân nhắc ba chiến lược chính khi xử lý secret trong cụm:

1. Sealed Secrets (Bitnami)

Sealed Secrets sử dụng mật mã học bất đối xứng. Bạn mã hóa các giá trị thô phía client bằng public key được liên kết với một controller chạy trong cụm, sau đó commit tài nguyên SealedSecret đã tạo lên Git. Controller sẽ dùng private key của nó để giải mã payload thành Kubernetes Secret gốc.

2. Secrets Store CSI Driver

Secrets Store CSI Driver gắn (mount) trực tiếp các secret từ bên ngoài vào volume của Pod thông qua các plugin Container Storage Interface (CSI). Secret được truyền thẳng từ các backend bên ngoài—chẳng hạn như AWS Secrets Manager, HashiCorp Vault, Azure Key Vault hoặc Google Secret Manager—vào bộ nhớ khả biến (volatile memory) bên trong pod.

3. External Secrets Operator (ESO)

External Secrets Operator là một Kubernetes operator chuyên dụng. Nó liên tục thăm dò (poll) metadata từ các API bên ngoài và tạo ra các tài nguyên v1/Secret tiêu chuẩn bên trong namespace đích của bạn.

Ưu & Nhược điểm của từng chiến lược

Mỗi mô hình đều đi kèm với những đánh đổi vận hành cụ thể:

Sealed Secrets

  • Ưu điểm: Hoạt động độc lập khép kín trong Git; không cần cấu hình IAM đám mây phức tạp; không bị phụ thuộc vào nhà cung cấp (vendor lock-in).
  • Nhược điểm: Việc xoay vòng secret yêu cầu phải mã hóa lại thủ công và tạo commit git mới; việc sao lưu private key của controller tạo ra điểm lỗi đơn lẻ (single point of failure) khi khôi phục sau thảm họa; không hỗ trợ tích hợp với các secret engine của doanh nghiệp.

Secrets Store CSI Driver

  • Ưu điểm: Thông tin xác thực bỏ qua etcd theo mặc định; payload nằm hoàn toàn trong các volume tmpfs được hỗ trợ bởi bộ nhớ bên trong pod.
  • Nhược điểm: Gắn chặt với vòng đời của pod; bắt buộc phải cấu hình mount volume ngay cả với các ứng dụng chỉ cần biến môi trường đơn giản; việc đồng bộ ngược giá trị về Secret gốc đòi hỏi phải kích hoạt các cờ controller dạng alpha/beta, gây thêm phức tạp cho cấu hình.

External Secrets Operator (ESO)

  • Ưu điểm: Tạo ra các đối tượng v1/Secret tiêu chuẩn, đảm bảo tương thích hoàn toàn với các Helm chart và Deployment hiện có; kết nối đồng thời tới nhiều backend; tích hợp sẵn tính năng tạo khuôn mẫu (templating) và chu kỳ thăm dò linh hoạt; hoạt động tách biệt hoàn toàn khỏi chuỗi khởi động của pod.
  • Nhược điểm: Secret vẫn được lưu trữ trong etcd (đòi hỏi phải cấu hình mã hóa etcd at-rest bằng KMS); yêu cầu vận hành và giám sát thêm một controller hoạt động thường trực.

Kiến trúc đề xuất: ESO kết hợp với Backend doanh nghiệp

Trên các cụm production quản lý hàng trăm microservice, ESO luôn mang lại ranh giới phân tách rõ ràng và mạch lạc nhất giữa đội ngũ vận hành và phát triển. Đội ngũ bảo mật quản lý tập trung các chính sách IAM và quy tắc vòng đời trong AWS Secrets Manager hoặc Vault. Trong khi đó, các lập trình viên ứng dụng chỉ cần viết các manifest ExternalSecret ngắn gọn song hành cùng workload tiêu chuẩn của họ.

Khi soạn thảo các định nghĩa YAML cho SecretStore và ExternalSecret, việc xác thực trước cấu trúc phân cấp key sẽ giúp tiết kiệm nhiều thời gian. Nếu cần chuyển đổi payload JSON sang YAML hoặc kiểm tra các dictionary có cấu trúc, hãy trải nghiệm ToolCraft YAML to JSON Converter.

ToolCraft thực thi toàn bộ quá trình chuyển đổi phía client ngay trên trình duyệt mà không gửi payload đi đâu, rất an toàn để thử nghiệm các schema phi-production. Để nhanh chóng kiểm tra các secret hiện có trong cụm hoặc xác thực chuỗi base64 cục bộ, công cụ Base64 Encoder/Decoder cũng mang lại tốc độ và tính bảo mật tương tự.

Hướng dẫn triển khai từng bước

Bước 1: Cài đặt External Secrets Operator

Triển khai Helm chart của ESO vào cụm của bạn:

helm repo add external-secrets https://charts.external-secrets.io
helm repo update

helm install external-secrets \
  external-secrets/external-secrets \
  -n external-secrets \
  --create-namespace \
  --set installCRDs=true

Xác nhận rằng các pod controller, webhook và cert-controller đã khởi động và hoạt động ổn định:

kubectl get pods -n external-secrets

Bước 2: Kết nối AWS Secrets Manager thông qua SecretStore

IAM Roles for Service Accounts (IRSA) cung cấp cơ chế trao đổi token an toàn trên AWS EKS mà không cần hardcode thông tin xác thực. Hãy cấp quyền secretsmanager:GetSecretValue và secretsmanager:DescribeSecret cho IAM role của bạn, liên kết role đó với ServiceAccount, rồi khai báo một SecretStore:

cat <<EOF | kubectl apply -f -
apiVersion: external-secrets.io/v1beta1
kind: SecretStore
metadata:
  name: aws-secrets-backend
  namespace: default
spec:
  provider:
    aws:
      service: SecretsManager
      region: us-east-1
      auth:
        jwt:
          serviceAccountRef:
            name: eso-aws-sa
EOF

Bước 3: Kết nối HashiCorp Vault thông qua SecretStore

Đối với HashiCorp Vault, phương thức xác thực Kubernetes gốc sẽ ánh xạ trực tiếp service account của pod tới các policy của Vault. Điều này giúp loại bỏ hoàn toàn việc phải quản lý root token tĩnh:

cat <<EOF | kubectl apply -f -
apiVersion: external-secrets.io/v1beta1
kind: SecretStore
metadata:
  name: vault-backend
  namespace: default
spec:
  provider:
    vault:
      server: "https://vault.internal.corp:8200"
      path: "secret"
      version: "v2"
      auth:
        kubernetes:
          mountPath: "kubernetes"
          role: "app-role"
          serviceAccountRef:
            name: default
EOF

Bước 4: Khai báo tài nguyên ExternalSecret

Tiếp theo, hãy định nghĩa một manifest ExternalSecret để lấy giá trị về và cấu trúc nên Kubernetes Secret đầu ra. Nếu vault thượng nguồn của bạn lưu trữ các dictionary JSON, hãy định dạng và kiểm tra các key bằng ToolCraft JSON Formatter trước khi cấu hình mapping.

cat <<EOF | kubectl apply -f -
apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
  name: payment-service-secret
  namespace: default
spec:
  refreshInterval: "1h"
  secretStoreRef:
    name: aws-secrets-backend
    kind: SecretStore
  target:
    name: payment-api-secret
    creationPolicy: Owner
  data:
    - secretKey: DB_PASSWORD
      remoteRef:
        key: production/payment-service
        property: password
    - secretKey: API_KEY
      remoteRef:
        key: production/payment-service
        property: api_key
EOF

Bước 5: Xác minh đồng bộ và liên kết vào Pod

Kiểm tra trạng thái đồng bộ của tài nguyên:

kubectl get externalsecret payment-service-secret
kubectl describe externalsecret payment-service-secret

Tìm dòng Status: SecretSynced trong kết quả trả về. Sau đó xác nhận rằng ESO đã tạo secret gốc thành công:

kubectl get secret payment-api-secret -o yaml

Giờ đây, ứng dụng của bạn có thể liên kết các key đã đồng bộ dưới dạng các biến môi trường chuẩn:

cat <<EOF | kubectl apply -f -
apiVersion: apps/v1
kind: Deployment
metadata:
  name: payment-service
  namespace: default
spec:
  replicas: 2
  selector:
    matchLabels:
      app: payment-service
  template:
    metadata:
      labels:
        app: payment-service
    spec:
      containers:
        - name: app
          image: nginx:alpine
          env:
            - name: DB_PASSWORD
              valueFrom:
                secretKeyRef:
                  name: payment-api-secret
                  key: DB_PASSWORD
EOF

Bất cứ khi nào thông tin xác thực được xoay vòng ở upstream, ESO sẽ tự động kéo các giá trị mới về trong khoảng thời gian refreshInterval đã thiết lập. Bạn có thể kết hợp kiến trúc này với một controller như Stakater Reloader để tự động kích hoạt cập nhật rolling update cho pod ngay khi secret downstream thay đổi.

Share: