Tự động hóa quản lý Secret trong Kubernetes với External Secrets Operator: Đồng bộ hóa AWS và Vault

DevOps tutorial - IT technology blog
DevOps tutorial - IT technology blog

Vấn đề với Secret mặc định của Kubernetes

`Secrets` mặc định của Kubernetes thực chất là một cái tên gây hiểu lầm. Chúng không hề được mã hóa; chúng chỉ là các chuỗi được encode Base64. Nếu bạn commit chúng vào kho lưu trữ Git, bất kỳ ai có quyền truy cập đọc đều có thể chạy lệnh echo 'encoded-string' | base64 --decode và chiếm quyền điều khiển cơ sở dữ liệu production của bạn chỉ trong ba giây. Tôi đã chứng kiến nhiều đội ngũ phải trả giá đắt sau khi một lập trình viên sơ ý push file .yaml chứa Stripe API key lên một repo công khai.

Hầu hết các tổ chức cuối cùng đều chuyển sang sử dụng các công cụ chuyên dụng như AWS Secrets Manager hoặc HashiCorp Vault. Khó khăn bắt đầu nảy sinh khi bạn cố gắng đưa các giá trị đó vào pod. Mặc dù bạn có thể viết script Python tùy chỉnh hoặc sử dụng các init-container cồng kềnh, nhưng những phương pháp này làm tăng đáng kể chi phí bảo trì. Làm chủ cầu nối giữa các vault bên ngoài và Kubernetes là một kỹ năng bắt buộc để xây dựng một pipeline CI/CD hiện đại và bảo mật.

External Secrets Operator (ESO) lấp đầy khoảng trống này một cách hoàn hảo. Nó hoạt động như một controller chạy ngầm, thực hiện lấy dữ liệu từ provider của bạn và đẩy chúng vào các Secret mặc định của Kubernetes để ứng dụng có thể sử dụng.

Hiểu các khái niệm cốt lõi của ESO

ESO dựa trên hai custom resource chính để xử lý các tác vụ nặng:

  • SecretStore / ClusterSecretStore: Chúng đóng vai trò như chuỗi kết nối. Chúng định nghĩa cách thức giao tiếp với AWS, Vault hoặc GCP. Sử dụng SecretStore để cô lập trong một namespace duy nhất hoặc ClusterSecretStore để chia sẻ một kết nối cho toàn bộ cluster.
  • ExternalSecret: Đây là manifest định nghĩa những gì cần lấy. Nó chỉ thị cho operator tìm một key cụ thể, ví dụ prod/db/password, và map nó vào một Kubernetes Secret cục bộ.

Tôi thường xuyên chuyển đổi giữa định dạng YAML và JSON khi debug các manifest này để đảm bảo cấu trúc dữ liệu lồng nhau là chính xác. Việc chuẩn bị sẵn một công cụ Chuyển đổi YAML ↔ JSON là rất hữu ích. Sử dụng công cụ phía client đảm bảo bạn không làm rò rỉ các mô hình kiến trúc nhạy cảm cho máy chủ bên thứ ba trong quá trình chuyển đổi.

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

Helm là lựa chọn tiêu chuẩn để cài đặt. Nó đơn giản hóa việc quản lý phiên bản và giúp quá trình dọn dẹp trở nên dễ dàng nếu bạn cần di chuyển sau này.

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

helm install external-secrets \
  external-secrets/external-secrets \
  -n external-secrets \
  --create-namespace

Xác nhận việc triển khai bằng cách kiểm tra trạng thái của các pod controller:

kubectl get pods -n external-secrets

Bước 2: Kết nối với AWS Secrets Manager

Hãy tránh sử dụng các AWS access key tĩnh bằng mọi giá. Thay vào đó, hãy sử dụng IAM Roles for Service Accounts (IRSA) để cấp quyền cho cluster đọc secret. Bạn sẽ cần một IAM policy cho phép cụ thể hành động secretsmanager:GetSecretValue cho các tài nguyên mục tiêu.

Sau khi IAM role của bạn được liên kết với một Kubernetes ServiceAccount, hãy áp dụng một ClusterSecretStore như sau:

apiVersion: external-secrets.io/v1beta1
kind: ClusterSecretStore
metadata:
  name: aws-secretsmgr
spec:
  provider:
    aws:
      service: SecretsManager
      region: us-east-1
      auth:
        jwt:
          serviceAccountRef:
            name: eso-service-account
            namespace: external-secrets

Bước 3: Kết nối với HashiCorp Vault

Việc thiết lập Vault thường yêu cầu cấu hình nhiều hơn một chút. Bạn có thể sử dụng Token hoặc AppRole, nhưng Kubernetes Auth Method là cách sạch sẽ nhất cho lưu lượng nội bộ cluster. Dưới đây là cấu hình tiêu chuẩn cho một mount xác thực kiểu Kubernetes-native:

apiVersion: external-secrets.io/v1beta1
kind: SecretStore
metadata:
  name: vault-backend
  namespace: my-app
spec:
  provider:
    vault:
      server: "https://vault.example.com"
      path: "secret"
      version: "v2"
      auth:
        kubernetes:
          mountPath: "kubernetes"
          role: "my-role"
          serviceAccountRef:
            name: "my-app-sa"

Lỗi xác thực rất phổ biến trong quá trình thiết lập ban đầu. Nếu operator không đồng bộ được, tôi sử dụng Trình giải mã JWT để kiểm tra thời gian hết hạn và các claim của token service account. Việc này giúp nhanh chóng phát hiện xem vấn đề là do token hết hạn hay do tên role không khớp.

Bước 4: Đồng bộ Secret đầu tiên của bạn

Hãy bắt tay vào thực hành. Giả sử bạn có một secret trên AWS tên là /production/api-key. Bạn muốn secret này có sẵn trong namespace my-app dưới tên api-credentials.

Áp dụng manifest ExternalSecret này:

apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
  name: api-key-sync
  namespace: my-app
spec:
  refreshInterval: 1h
  secretStoreRef:
    name: aws-secretsmgr
    kind: ClusterSecretStore
  target:
    name: api-credentials
    creationPolicy: Owner
  data:
  - secretKey: API_TOKEN
    remoteRef:
      key: /production/api-key
      property: api_token_value

ESO sẽ giờ đây sẽ lấy giá trị và tạo ra một Kubernetes Secret tiêu chuẩn. Các pod ứng dụng của bạn có thể mount secret này dưới dạng biến môi trường hoặc file volume. Nếu bạn đang di chuyển các secret cũ, hãy sử dụng Trình giải mã Base64 để so sánh các giá trị mới được đồng bộ với các giá trị thủ công cũ. Điều này đảm bảo ứng dụng sẽ không bị crash do các thay đổi định dạng ngoài ý muốn.

Lời khuyên thực tế từ kinh nghiệm thực chiến

Việc quản lý hàng trăm secret trên các cluster production đã dạy cho tôi một vài bài học xương máu:

  1. Theo dõi chi phí API: AWS Secrets Manager tốn 0,40 USD mỗi secret/tháng cộng với 0,05 USD cho mỗi 10.000 lệnh gọi API. Đừng đặt refreshInterval là 10 giây. Khoảng thời gian 1 giờ thường là đủ cho hầu hết các trường hợp sử dụng và giúp giữ hóa đơn của bạn ở mức thấp.
  2. Chuẩn hóa đường dẫn: Sử dụng phân cấp như /env/namespace/app/key. Điều này cho phép bạn viết các chính sách IAM hoặc Vault chi tiết nhằm ngăn chặn việc một nhóm vô tình xem được thông tin đăng nhập của nhóm khác.
  3. Theo dõi trạng thái: Nếu quá trình đồng bộ thất bại, kubectl describe externalsecret <name> là người bạn tốt nhất của bạn. Nó cung cấp nhật ký sự kiện rõ ràng, chi tiết xem vấn đề là lỗi 403 Forbidden hay do timeout mạng.
  4. Quyền tối thiểu: Đừng bao giờ cấp cho ESO quyền admin toàn bộ cluster đối với vault của bạn. Hãy chỉ cấp quyền đọc cho các đường dẫn cụ thể để giảm thiểu phạm vi ảnh hưởng trong trường hợp bị tấn công.

Tổng kết

Việc loại bỏ các lệnh kubectl create secret thủ công là một thắng lợi lớn cho cả tính bảo mật lẫn sự ổn định. Bằng cách triển khai External Secrets Operator, bạn duy trì được một nguồn dữ liệu gốc duy nhất (single source of truth) trong một vault được bảo mật nghiêm ngặt trong khi vẫn giữ cho các manifest GitOps sạch sẽ. Nó tự động hóa việc xoay vòng (rotation), giảm thiểu sai sót do con người và đảm bảo dữ liệu nhạy cảm không bao giờ xuất hiện dưới dạng văn bản thuần túy trong hệ thống quản lý mã nguồn.

Việc thiết lập ban đầu mất khoảng một giờ. Tuy nhiên, thời gian tiết kiệm được cho việc khắc phục sự cố và sự an tâm về bảo mật mà nó mang lại có giá trị lớn hơn nhiều. Một khi bạn thấy các secret của mình tự động cập nhật trên nhiều môi trường, bạn sẽ không bao giờ muốn quay lại cách encode thủ công nữa.

Share: