Cách thiết lập Secret bảo mật hơn trong Kubernetes

Các bác đang dùng K8s, khi triển khai Microservice lên Kubernetes (K8s), việc đưa các thông tin nhạy cảm như API Key hay Database Password vào Pod là bắt buộc. Bác nào có chuyên môn tốt rồi thì phần này đã rất hiểu nhưng tôi vẫn thấy có nhiều các bác chưa biết mà dùng trực tiếp Secret trong K8s, và nhiều khi chỉ đơn giản là base64 encode rồi kubectl apply.

Nếu các bác nào vẫn đang làm thế thì chắc chắn luôn: Base64 không phải là bảo mật. Việc này giống như việc các bác cất chìa khóa nhà ở một nơi dễ đoán nhất vậy. Trong bài này tôi chia sẻ một chút để giúp bác nào vẫn làm vậy khắc phục vấn đề này nhé.

Kubernetes Secrets không hề “Secret”

Thực tế là: Secret k8s chỉ là dữ liệu Base64 được lưu trữ trong etcd.

  • Không mã hóa: Trừ khi các bác bật tính năng Encryption at Rest, dữ liệu trong etcd sẽ được lưu dưới dạng plain text. Bất kỳ ai có quyền truy cập vào etcd đều có thể đọc được toàn bộ Secrets.
  • Dễ dàng đọc: Với lệnh đơn giản như kubectl get secret -o yaml và một thao tác base64 decode, mật khẩu quan trọng sẽ hiện ra trên terminal.

Kubernetes chỉ cung cấp cơ chế lưu trữ, chứ không phải giải pháp bảo mật toàn diện. Chúng ta cần phải bổ sung các lớp bảo mật khác.

5 bước mã hóa secret hiệu quả trong k8s

Bước 1: Encryption at Rest

Mặc định, etcd lưu trữ Secrets không mã hóa. Bước đầu tiên và quan trọng nhất là phải kích hoạt mã hóa cho dữ liệu trên etcd. Việc này giúp ngăn chặn việc đọc Secrets nếu kẻ tấn công truy cập được vào chính etcd database files trên ổ cứng.

Các bác cần bật Encryption at Rest trên cluster K8s. Nó không phải cái gì quá phức tạp đâu, chỉ là thêm một chút config trong API Server thôi. Nên dùng AES-CBC hoặc AES-GCM và quan trọng là phải thường xuyên rotate keys.

Config minh họa:

Giả sử các bác cần cấu hình EncryptionConfiguration cho API Server:

# Cấu hình EncryptionConfig cho API Server
apiVersion: apiserver.config.k8s.io/v1
kind: EncryptionConfiguration
resources:
  - resources:
      - secrets # Chỉ định rằng chúng ta muốn mã hóa đối tượng Secrets
    providers:
      - aescbc: # Sử dụng thuật toán AES-CBC
          keys:
            - name: current-key-2025 # Key đang dùng để mã hóa
              secret: NzM2RzkwMzcxODZGOEY5Q0U3MDVGOEZCNkU0MDQwMjM= # Chuỗi Base64 của 32-byte key ngẫu nhiên
            - name: old-key-2024 # Key cũ, dùng để giải mã những Secrets đã mã hóa trước đó 
              secret: NzM2RzkwMzcxODZGOEY5Q0U3MDVGOEZCNkU0MDQwMjM=
      - identity: {} # Provider cuối cùng, dùng để đọc các Secrets KHÔNG được mã hóa bởi bất kỳ Provider nào ở trên 

Giải thích: Tôi sử dụng aescbc là provider mã hóa cho secrets. identity để đảm bảo K8s vẫn có thể đọc được các secrets cũ chưa được mã hóa bằng AES. Đây là bước cơ bản để ngăn chặn việc etcd lưu trữ plain text.

Bước 2: Áp dụng least privilege với RBAC

Không phải ai cũng cần quyền xem Secrets trong môi trường Production.

  • Sử dụng RBAC: Xây dựng các Role (hoặc ClusterRole) chỉ cấp quyền get hoặc list Secrets trong namespace cụ thể mà service hoặc team đó quản lý.
  • Tránh dùng cluster-admin: Hạn chế tối đa việc gán quyền quản trị toàn cụm cho người dùng hoặc tài khoản dịch vụ không cần thiết (cái này thấy nhiều team cứ “TIỆN” là TOANG).
# Role chỉ cho phép get/list Secrets trong namespace 'prod-app-api'
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  namespace: prod-app-api
  name: api-secret-reader
rules:
  - apiGroups: [""]
    resources: ["secrets"]
    verbs: ["get", "list", "watch"] # Chỉ cho phép đọc, không cho sửa/tạo

Bước 3: Đưa secrets ra khỏi Git

Đây là điều bắt buộc trong quy trình GitOps: Không bao giờ Commit Secrets vào Git Kể cả khi nó được Base64 hay là cho môi trường Staging. Một khi đã vào lịch sử Git, nó rất khó để xoá bỏ triệt để.

Giải pháp đề xuất (Sử dụng Vault):

Thay vì lưu trong K8s YAML, các bác nên lưu trữ tập trung tại một Secret Vault và sử dụng công cụ để đồng bộ vào K8s:

  • Lưu trữ: HashiCorp Vault, AWS Secrets Manager, GCP Secret Manager.
  • Đồng bộ hoá: Dùng External Secrets Operator (ESO) hoặc Secrets Store CSI Driver.
# Ví dụ cấu hình ExternalSecret để pull data từ AWS Secrets Manager 
apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
  name: my-app-db-creds
spec:
  refreshInterval: "1h" # Tự động kiểm tra cập nhật mỗi 1 giờ
  secretStoreRef:
    name: aws-sm-store # Tham chiếu đến cấu hình kết nối AWS
    kind: SecretStore
  target:
    name: prod-db-secret # Tên Secret K8s sẽ được tạo ra
    creationPolicy: Owner
  data:
  - secretKey: db_username # Tên Key trong Secret K8s
    remoteRef:
      key: prod/my-database/credentials # Đường dẫn Secret trên AWS Secrets Manager
      property: username # Tên trường trong Secret AWS
  - secretKey: db_password
    remoteRef:
      key: prod/my-database/credentials
      property: password

Lợi ích: Secrets chỉ tồn tại trong Vault và được kéo vào K8s, không bao giờ chạm vào Git repo dưới dạng Plain Text.

Bước 4: Tự động hóa rotation Secrets

Static secrets là lỗ hổng bảo mật tiềm ẩn. Việc rotation tự động định kỳ giúp giảm thiểu rủi ro khi một Secret bị lộ.

  • Sử dụng Vault hoặc các Secret Manager trên Cloud để tự động xoay mật khẩu database.
  • Ưu tiên dùng short-lived tokens, ví dụ: Service Account Token, IAM Role, OIDC.
  • Cấu hình ứng dụng của các bác để có thể tự động làm mới Secrets mà không cần phải redeploy toàn bộ.

    Thiết kế ứng dụng có khả năng làm mới Secrets mà không cần phải Redeploy lại toàn bộ. Nếu app các bác chưa làm được, thì phải sửa nó. Đừng dùng lý do “Nó là Legacy” để bao biện cho sự kém bảo mật.

Bước 5: Audit và giám sát việc sử dụng Secrets

Bảo mật là một quá trình liên tục, không phải là thiết lập một lần. Các bác cần có LogsTooling để giám sát.

  • Kubernetes Audit Logs: Bật và theo dõi nhật ký kiểm toán của K8s API Server để biết ai (User hoặc Service Account) đã cố gắng truy cập (Get/List) các Secret nào.
  • Quét Git: Sử dụng các công cụ như gitleaks hoặc trufflehog để liên tục quét các CommitLịch sử Git nhằm phát hiện các khóa nhạy cảm bị rò rỉ.

Lưu ý thực tế: Các bot trên mạng sẽ tóm lấy Access Key AWS bị lộ trên GitHub chỉ trong vòng chưa đầy 5 phút. Việc kiểm tra này phải được tự động hóa trong CI/CD Pipeline.

Tools đánh giá

Đây là các công cụ tôi đã triển khai thành công cho nhiều team trong thực tế:

Tên Công Cụ Mục Đích Chính Bối Cảnh Sử Dụng
External Secrets Operator Đồng bộ Secrets từ Cloud/Vault vào K8s. K8s cần Secrets từ AWS Secrets Manager, GCP Secret Manager, HashiCorp Vault…
SOPS + KMS Mã hóa Secrets tĩnh trong Git, an toàn cho GitOps. Secrets cần được version control nhưng vẫn phải được mã hóa mạnh.
Vault Agent Injector Inject Secrets trực tiếp vào Pod khi Runtime. Ứng dụng giao tiếp thẳng với Vault, không cần lưu Secret vào etcd.
Sealed Secrets Mã hóa Secret để lưu trữ an toàn trong Git. Giải pháp đơn giản, gọn nhẹ hơn External Secrets nếu không dùng Vault.

Các bác chọn một cái phù hợp với stack của tôi và dùng nhất quán. Setup kiểu “Frankenstein” nửa vời, mỗi chỗ một công cụ, chỉ làm cho cả team chẳng ai hiểu rõ hệ thống hoạt động thế nào.

Lời Kết

Kubernetes không tự động bảo mật Secrets.

Chính chúng ta phải làm điều đó. Các bác cần xây dựng một chiến lược toàn diện:

  1. Mã hóa Etcd (Encryption at Rest).
  2. Siết chặt RBAC theo nguyên tắc Quyền Tối Thiểu.
  3. Dùng Secret Vault (Vault, AWS SM, v.v…) và đồng bộ qua Operator.
  4. Tự động hóa rotation định kỳ.
  5. Audit và giám sát truy cập Secrets.

Làm được 5 điều này, các bác đã phần nào giúp hệ thống của mình lên một nền tảng bảo mật khá ổn rồi đấy.

Chia sẻ bài viết:
Theo dõi
Thông báo của
0 Góp ý
Được bỏ phiếu nhiều nhất
Mới nhất Cũ nhất
Phản hồi nội tuyến
Xem tất cả bình luận