Platform Engineering 6 — Secrets & Policy-as-Code

13 thg 7, 2026 3 lượt xem
#devops
#policy-as-code
#secrets
#vault
#opa

Trong Platform Engineering — Tổng quan, platform team dựng golden path để developer tự phục vụ mà vẫn an toàn theo mặc định. Hai thứ quyết định "an toàn theo mặc định" đó không phải là tài liệu hay thiện chí của con người, mà là hai cơ chế kỹ thuật: quản lý bí mật (secrets management) — nơi mật khẩu, khóa, token được sinh ra, lưu, xoay và cấp phát; và policy-as-code — nơi các luật lệ ("cấm image chưa ký", "bắt buộc gắn nhãn phân loại dữ liệu") được viết thành code và máy tự chặn khi vi phạm. Bài này đào sâu cả hai, đặt trong bối cảnh GitOps/Kubernetes đã dựng ở GitOps & Argo CDCI/CD, và khép lại bằng yêu cầu tuân thủ của một ngân hàng như NCB.

Vì sao secret không được nằm trong code, Git hay env thô

Bí mật (secret) là bất kỳ chuỗi nào cho phép truy cập tài nguyên: mật khẩu database, API key, TLS private key, token dịch vụ, chuỗi kết nối. Sai lầm phổ biến nhất là để chúng ở nơi tưởng như tiện lợi nhưng thực ra rò rỉ khắp nơi:

  • Trong source code / Git: Git là bất biến và phân tán. Một secret commit nhầm rồi "xóa" ở commit sau vẫn còn nguyên trong lịch sử, đã nhân bản về mọi máy dev, mọi bản fork, mọi runner CI đã clone. Rất nhiều vụ lộ khóa đám mây bắt nguồn từ chính đây. Không có cách "rút lại" — chỉ có thể xoay (rotate) secret đó, coi như đã lộ.
  • Trong biến môi trường thô (raw env): env dễ rò qua log crash, qua /proc/<pid>/environ, qua child process kế thừa, qua ảnh chụp debug. Env vẫn dùng được như kênh phân phối cuối, nhưng giá trị phải đến từ kho bí mật lúc runtime, không hard-code trong manifest hay Dockerfile.
  • Trong image container: layer image là public trong registry nội bộ; secret nướng vào layer sẽ nằm đó vĩnh viễn kể cả khi lệnh sau xóa file.

Vấn đề gốc: secret rải rác thì không ai xoay được, không ai audit được, không thu hồi được. Lời giải là tập trung hóa vào một kho bí mật.

Kho bí mật: Vault và cloud secret manager

Một secrets manager là dịch vụ lưu bí mật tập trung, mã hóa khi nghỉ (encryption at rest), kiểm soát truy cập chi tiết và ghi audit mọi lần đọc. Hai nhóm chính:

  • HashiCorp Vault — độc lập nền tảng, mạnh nhất về dynamic secrets và engine mã hóa. Vault dùng master key được chia mảnh (Shamir) và phải unseal mới hoạt động — bản thân Vault không tự đọc được bí mật của mình khi niêm phong.
  • Cloud secret manager — AWS Secrets Manager, GCP Secret Manager, Azure Key Vault: tích hợp sâu với IAM của nền tảng, vận hành nhẹ, phù hợp khi đã "all-in" một cloud.

Những năng lực khiến kho bí mật vượt trội so với "file mã hóa":

Năng lựcÝ nghĩa
Mã hóa tập trungBí mật mã hóa khi nghỉ; ứng dụng không giữ khóa gốc.
Rotation (xoay vòng)Đổi mật khẩu định kỳ tự động; giảm cửa sổ khai thác khi lộ.
Dynamic secretsVault sinh mới credential theo yêu cầu (vd một cặp user/pass Postgres) với TTL ngắn rồi tự thu hồi — mỗi service, mỗi phiên một credential riêng.
Lease / thuê ngắn hạnCredential có thời hạn thuê; hết hạn là vô hiệu, không còn "khóa vĩnh viễn".
Audit logMọi thao tác đọc/ghi được ghi lại — ai lấy secret nào, lúc nào.

Truy cập theo danh tính, không phải khóa tĩnh

Điểm chuyển dịch quan trọng nhất: thay khóa tĩnh bằng danh tính (identity-based access). Thay vì phát cho mỗi service một API key sống mãi (mà nếu lộ thì thảm họa), workload tự chứng minh danh tính rồi đổi lấy secret ngắn hạn:

  • Workload identity: pod/service account trong Kubernetes có danh tính gắn với cluster; Vault hoặc cloud IAM xác thực danh tính đó (Kubernetes auth method, IRSA trên AWS, Workload Identity trên GCP) rồi cấp token có phạm vi hẹp.
  • OIDC: danh tính được phát dưới dạng token OIDC ngắn hạn ký bởi nhà cung cấp tin cậy. Cùng cơ chế này áp cho pipeline CI/CD — GitHub Actions phát OIDC token đổi lấy quyền cloud, xóa bỏ long-lived key trong secret của repo (nối CI/CD & chuỗi cung ứng).

Nguyên tắc chung: không có secret trường tồn. Có danh tính đáng tin → cấp bí mật ngắn hạn → hết hạn → xin lại. Đây là hiện thân của zero-trust: không tin theo vị trí mạng, chỉ tin theo danh tính đã xác thực.

Secrets trong GitOps/Kubernetes

GitOps đặt ra một nghịch lý: Argo CD yêu cầu mọi thứ nằm trong Git làm nguồn sự thật, nhưng ta không được để plaintext secret trong Git. Ba lời giải phổ biến:

  • Sealed Secrets (Bitnami): controller trong cluster giữ một cặp khóa. Dev dùng kubeseal mã hóa secret bằng public key thành một SealedSecretchỉ controller trong đúng cluster đó giải mã được. SealedSecret đã mã hóa an toàn để commit vào Git. Đơn giản, hợp cho quy mô nhỏ–vừa.
  • External Secrets Operator (ESO): không lưu bí mật trong Git chút nào. Trong Git chỉ có một ExternalSecret — bản chất là tham chiếu ("lấy key db/password từ Vault"). Operator trong cluster đọc từ Vault/cloud manager rồi tạo ra Secret gốc của Kubernetes để pod dùng. Đây là mẫu được ưa chuộng cho quy mô lớn vì gộp GitOps với một kho bí mật thật.
  • SOPS (Secrets OPerationS) + age/KMS: mã hóa từng giá trị trong file YAML/JSON (khóa vẫn đọc được, giá trị bị mã hóa), giải mã bằng KMS/age lúc apply. Hợp cho việc phiên bản hóa cấu hình nhạy cảm.

Chọn cái nào? ESO khi đã có Vault/cloud manager và cần rotation/audit tập trung; Sealed Secrets khi muốn nhẹ, tự chứa trong cluster; SOPS khi thiên về quản lý file cấu hình.

Sơ đồ: luồng secret Vault → ESO → Pod

Điểm mấu chốt: plaintext không bao giờ chạm Git; Vault là nơi duy nhất giữ giá trị thật, có audit và rotation; danh tính pod (ServiceAccount) là "chìa khóa" thay cho key tĩnh.

Ví dụ minh họa (YAML)

Một ExternalSecret tham chiếu Vault — không chứa giá trị bí mật nào:

# MINH HOẠ — External Secrets Operator
apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
  name: core-db-cred
  namespace: payments
spec:
  refreshInterval: 15m          # tự đồng bộ lại, hỗ trợ rotation
  secretStoreRef:
    name: vault-backend
    kind: SecretStore
  target:
    name: core-db-secret        # Secret K8s sẽ được tạo ra
  data:
    - secretKey: password
      remoteRef:
        key: kv/payments/postgres
        property: password

Một SealedSecret (đã mã hóa, an toàn commit) trông như sau — giá trị chỉ controller giải được:

# MINH HOẠ — Sealed Secret (ciphertext, không phải plaintext)
apiVersion: bitnami.com/v1alpha1
kind: SealedSecret
metadata:
  name: api-token
  namespace: payments
spec:
  encryptedData:
    token: AgBy3i4OJSWK+PiTySYZZA9rO4...   # đã mã hóa bằng public key

Policy-as-code: guardrail thay vì gatekeeping

Cách cũ để đảm bảo tuân thủ là gatekeeping thủ công: một người xét duyệt trước mỗi lần thay đổi. Cách này chậm, không nhất quán, và không mở rộng nổi. Policy-as-code biến luật lệ thành code chạy được, đặt guardrail — hàng rào tự động chặn vi phạm — để dev đi nhanh trong ranh giới an toàn mà không cần người gác cổng.

Hai công cụ chủ đạo trong Kubernetes:

  • OPA (Open Policy Agent) + Rego: engine chính sách tổng quát, ngôn ngữ khai báo Rego. Trong K8s thường dùng qua Gatekeeper (một admission controller). OPA rất mạnh và linh hoạt, nhưng Rego có đường học dốc.
  • Kyverno: engine chính sách thiết kế riêng cho Kubernetes, viết policy bằng chính YAML — dễ tiếp cận hơn nhiều với người đã quen K8s. Hỗ trợ validate, mutate (tự sửa manifest), generate (tự tạo tài nguyên) và verify chữ ký image.

Chặn tại admission controller

Kubernetes có cơ chế admission controller — chốt chặn cuối cùng: mọi yêu cầu tạo/sửa tài nguyên đi qua API server đều được webhook chính sách soi trước khi ghi vào etcd. Nếu vi phạm → từ chối ngay, tài nguyên không bao giờ tồn tại.

Những chính sách hay gặp trong ngân hàng:

  • Cấm image không được ký (chỉ chấp nhận image đã cosign/Sigstore ký, khớp SLSA provenance).
  • Bắt buộc label/annotation bắt buộc: chủ sở hữu, mức phân loại dữ liệu, mã ứng dụng.
  • Chặn quyền quá rộng: cấm privileged: true, hostNetwork, hostPath, container chạy root.
  • Bắt buộc resource limits, cấm image tag latest, chỉ cho pull từ registry nội bộ tin cậy.

Ví dụ minh họa: Kyverno chặn image chưa ký & Rego bắt nhãn

Kyverno verify chữ ký image (chặn deploy image không ký):

# MINH HOẠ — Kyverno: chỉ chấp nhận image đã ký
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
  name: require-signed-images
spec:
  validationFailureAction: Enforce   # Enforce = chặn, Audit = chỉ ghi log
  rules:
    - name: verify-signature
      match:
        any:
          - resources:
              kinds: ["Pod"]
      verifyImages:
        - imageReferences:
            - "registry.ncb.internal/*"
          attestors:
            - entries:
                - keys:
                    publicKeys: |-
                      -----BEGIN PUBLIC KEY-----
                      ... (khóa công khai của platform) ...
                      -----END PUBLIC KEY-----

Rego (OPA) bắt buộc namespace có nhãn phân loại dữ liệu:

# MINH HOẠ — Rego: từ chối nếu thiếu nhãn phân loại
package kubernetes.admission

deny[msg] {
    input.request.kind.kind == "Namespace"
    not input.request.object.metadata.labels["data-classification"]
    msg := "Namespace phải có nhãn 'data-classification' (public|internal|confidential|restricted)"
}

Chính sách nên áp cả hai điểm: tại CI (soi manifest/Terraform trước khi merge — fail sớm, sửa rẻ) và tại admission (chốt chặn runtime, phòng cả những gì lọt qua CI hoặc apply thủ công). Chạy chế độ Audit trước để đo mức vi phạm, rồi chuyển Enforce khi đội đã kịp thích ứng.

Least privilege, RBAC, SoD và zero-trust

Secrets và policy chỉ phát huy khi đặt trên nền nguyên tắc quyền hạn đúng:

  • Least privilege (đặc quyền tối thiểu): mỗi danh tính chỉ được đúng quyền cần để làm việc, không hơn. Một service đọc một secret thì chỉ được đọc đúng path đó trong Vault, không phải toàn bộ kho.
  • RBAC: trong K8s, Role/ClusterRole + RoleBinding gán quyền theo vai trò; tránh gán cluster-admin bừa bãi. Với Vault, policy Vault giới hạn path và capability.
  • Separation of Duties (SoD — phân tách nhiệm vụ): người viết policy khác người phê duyệt; người deploy không đồng thời là người tự cấp quyền cho mình. Bắt buộc trong kiểm soát nội bộ ngân hàng để không một cá nhân nào nắm trọn một luồng nhạy cảm.
  • Zero-trust: không tin theo vị trí mạng ("trong VPN nên an toàn"); mọi truy cập đều phải xác thực danh tính và được cấp quyền tối thiểu, ngắn hạn.

Các chủ đề nhận dạng, phân quyền và mật mã được đào sâu ở Kiểm soát truy cập & mật mã.

Bối cảnh ngân hàng: tuân thủ và audit

Với ngân hàng, kiểm soát bí mật và chính sách không phải tùy chọn mà là điều kiện tuân thủ. Cơ quan quản lý và khung kiểm soát nội bộ đòi hỏi: bí mật được mã hóa và xoay vòng, mọi truy cập bí mật để lại audit trail, phân tách nhiệm vụ rõ ràng, và bằng chứng rằng chính sách được thực thi chứ không chỉ ghi trong tài liệu. Policy-as-code cho ra thứ mà auditor thích nhất: luật lệ ở dạng code trong Git (có lịch sử ai đổi, khi nào), và log admission chứng minh vi phạm đã bị chặn. Xem thêm khung tổng thể ở Quản trị dữ liệu — Tổng quan.

Use case thực tế

Bối cảnh NCB. Platform team NCB dựng một golden path cho các dịch vụ nội bộ (core, payments, data-api) trên Kubernetes, với hai yêu cầu bắt buộc không thể vượt qua.

1. Bí mật lấy từ Vault qua workload identity. Trước đây mỗi team tự để chuỗi kết nối database trong ConfigMap và biến môi trường của manifest — kiểm kê phát hiện ~40 secret plaintext rải trên nhiều repo, không rotation, không audit. Sau khi chuẩn hóa:

  • Vault chạy HA, bật audit device; database credential dùng dynamic secrets với TTL 1 giờ — mỗi pod payments nhận một cặp user/pass Postgres riêng, hết hạn tự thu hồi.
  • Pod xác thực bằng ServiceAccount (Kubernetes auth method), không còn API key tĩnh nào trong Git.
  • External Secrets Operator đồng bộ mỗi 15 phút; trong Git chỉ còn ExternalSecret tham chiếu, 0 plaintext.

2. Policy chặn tại admission. Kyverno + OPA Gatekeeper áp hai luật cứng ở chế độ Enforce:

  • Chỉ deploy image đã ký cosign từ registry.ncb.internal — image chưa ký bị từ chối.
  • Mọi namespace phải có nhãn data-classification; namespace xử lý dữ liệu khách hàng phải là confidential/restricted, nếu thiếu nhãn thì Deployment bị chặn ngay.

Kết quả (số liệu ước lượng, minh họa):

Chỉ sốTrướcSau
Secret plaintext trong Git~400
TTL credential DBvĩnh viễn1 giờ (dynamic)
Thời gian xoay khóa khi nghi lộ~2 ngày thủ côngtự động, tức thời
Deploy vi phạm chính sách lọt runtimekhông đo được~0 (chặn tại admission)
Bằng chứng cho auditorthu thập thủ cônglog Vault + admission sẵn có

Đúng một lần trong quý đầu, một team lỡ push image build tay chưa ký lên môi trường staging — admission webhook từ chối, pipeline báo lỗi rõ ràng, sự cố khép trong vài phút mà không cần ai canh gác thủ công. Đó chính là giá trị của guardrail: dev đi nhanh, ranh giới do máy giữ.

Ghi nhớ

  • Không để secret trong code/Git/env thô. Git bất biến — commit nhầm là coi như đã lộ, chỉ còn cách rotate.
  • Kho bí mật tập trung (Vault, cloud secret manager): mã hóa, rotation, dynamic secrets TTL ngắn, audit mọi lần đọc.
  • Truy cập theo danh tính, không khóa tĩnh: workload identity + OIDC cấp secret ngắn hạn — hiện thân của zero-trust.
  • Secrets trong GitOps: không commit plaintext. Sealed Secrets (mã hóa, commit được), External Secrets Operator (chỉ lưu tham chiếu, đọc từ Vault), SOPS (mã hóa từng giá trị).
  • Policy-as-code (OPA/Rego, Kyverno) là guardrail tự động thay cho gatekeeping thủ công: cấm image chưa ký, bắt buộc nhãn, chặn quyền quá rộng.
  • Áp policy ở cả CI và admission controller — fail sớm khi rẻ, chặn chắc lúc runtime. Chạy Audit trước, Enforce sau.
  • Least privilege, RBAC, SoD, zero-trust là nền cho mọi thứ trên; ngân hàng bắt buộc phân tách nhiệm vụ và audit trail.
  • Policy-as-code sinh ra bằng chứng tuân thủ tự nhiên: luật ở dạng code trong Git + log admission chứng minh vi phạm đã bị chặn.

Nguồn tham khảo

Bài viết liên quan

Phân biệt Continuous Integration, Delivery và Deployment; giải phẫu một pipeline (trigger, stage, job, step, artifact) và các lớp kiểm thử xếp theo chi phí. Bài viết một workflow GitHub Actions hoàn chỉnh (build, test, Docker), so sánh với GitLab CI, quản lý secrets an toàn và chọn chiến lược release rolling/blue-green/canary kèm cách rollback.

13 thg 7, 2026 5

Bài mở đầu series Linux cho data engineer: vì sao mọi hạ tầng dữ liệu đều chạy Linux, triết lý Unix, phân biệt terminal/shell/kernel, cấu trúc một lệnh, chuẩn thư mục FHS và cách đăng nhập server qua SSH.

13 thg 7, 2026 4

Bản đồ Kubernetes: control plane (API server, etcd, scheduler, controller manager), node (kubelet, kube-proxy, container runtime), mô hình khai báo & vòng điều khiển (reconciliation), và cách một Pod được tạo ra.

13 thg 7, 2026 4

Lập lịch job trích xuất dữ liệu trên Linux: cú pháp cron 5 trường, crontab, /etc/cron.d, MAILTO, khoá chống chồng job bằng flock; job một lần với at; systemd service & timer (OnCalendar, Persistent) như thay thế cron hiện đại, journalctl và logrotate. Bài mổ xẻ vì sao script chạy tay OK mà cron fail — sự khác biệt login/non-login shell và biến môi trường — cùng cách quản lý package (apt/yum/dnf) và so sánh cron vs systemd timer vs Airflow.

13 thg 7, 2026 4

Cảm nhận của bạn

Bình luận

Bạn cần để viết bình luận.

Chưa có bình luận. Hãy là người đầu tiên chia sẻ!