Skip to content

Secret Manager — Model, Versioning & Replication Internals

Tại sao quan trọng trong production

Trước khi Secret Manager tồn tại, các team thường lưu credentials theo một trong những cách sau: hardcode vào source code (đặc biệt phổ biến trong config files), truyền qua biến môi trường trong Kubernetes manifests, hoặc lưu trong etcd dưới dạng Kubernetes Secrets (base64, không mã hoá theo mặc định). Tất cả các phương pháp này đều có cùng vấn đề cốt lõi: secret không có lifecycle — không có audit trail, không có rotation history, không có cơ chế revoke một phiên bản cụ thể mà không ảnh hưởng đến toàn bộ hệ thống.

Secret Manager giải quyết vấn đề này bằng cách đưa secret quản lý dưới dạng first-class resource với versioning, access control ở cấp độ resource, replication policy, và audit logging. Nhưng để dùng đúng Secret Manager trong production, cần hiểu object model bên trong — đặc biệt là sự khác biệt giữa secret (metadata container) và secret version (nơi chứa giá trị thực).


Internal model — Secret object và Secret Version

Tầng phân cấp tài nguyên

Secret Manager tổ chức dữ liệu theo hai tầng rõ ràng:

Project
└── Secret (metadata container)
    ├── name: "projects/my-project/secrets/db-password"
    ├── replication policy
    ├── labels & annotations
    ├── rotation schedule
    ├── CMEK config
    └── Secret Versions (immutable payload)
        ├── version/1  → {data: "old-password", state: DISABLED}
        ├── version/2  → {data: "current-password", state: ENABLED}
        └── version/3  → {data: "...", state: DESTROYED}

Secret là một resource container — nó lưu metadata, IAM policies, replication configuration, và danh sách versions. Secret không chứa giá trị nhạy cảm trực tiếp.

Secret Version là nơi chứa payload thực tế (secret data). Mỗi version là immutable — một khi tạo, giá trị không thể thay đổi. Để cập nhật secret, bạn tạo version mới. Đây là thiết kế có chủ đích: nó bảo đảm audit trail hoàn chỉnh và khả năng rollback.

Resource path format

Secret:         projects/{project}/secrets/{secret-id}
Secret Version: projects/{project}/secrets/{secret-id}/versions/{version}

Ví dụ:
projects/my-project/secrets/database-password/versions/3
projects/my-project/secrets/database-password/versions/latest

Đối với regional secrets (secrets với user-managed replication chỉ một region):

projects/{project}/locations/{location}/secrets/{secret-id}/versions/{version}

Version lifecycle states

Mỗi secret version có một trong bốn trạng thái sau, và sự chuyển đổi giữa chúng là một chiều theo hướng destructive:

                     AddSecretVersion()


                       ENABLED ◄──────── UpdateSecretVersion(ENABLED)

                   UpdateSecretVersion(DISABLED)


                       DISABLED

                   DestroySecretVersion()


                 DESTROY_SCHEDULED (không tồn tại thật sự)

                            ▼ (immediate cho Secret Manager)
                       DESTROYED

ENABLED

Version đang active. Có thể được truy cập qua AccessSecretVersion(). Đây là trạng thái duy nhất mà payload có thể được đọc.

Một điểm quan trọng cần nhớ: không có giới hạn số version ENABLED đồng thời. Một secret có thể có 10 version đều ở trạng thái ENABLED. Đây không phải bug — đây là feature cho phép gradual rollout (một số service đọc version cũ, một số đọc version mới trong khi migration).

DISABLED

Version bị vô hiệu hóa nhưng payload vẫn còn. Không thể đọc giá trị, nhưng có thể re-enable. DISABLED thường dùng trong hai tình huống:

  1. Emergency disablement: Khi suspect một version bị lộ, disable ngay lập tức trong khi điều tra (không cần destroy — có thể cần payload cho forensics)
  2. Gradual deprecation: Disable các version cũ trước khi destroy để test xem có service nào còn phụ thuộc vào chúng không

DESTROYED

Version đã bị xóa vĩnh viễn — payload không thể phục hồi. Đây là hành động không thể đảo ngược.

Quan trọng: Cloud Secret Manager không có DESTROY_SCHEDULED period như Cloud KMS. Khi bạn gọi DestroySecretVersion(), deletion xảy ra ngay lập tức (không có grace period). Đây khác với Cloud KMS keys có 30 ngày scheduled-for-destruction mặc định.

DESTROY_SCHEDULED — chỉ tồn tại trong policy, không phải state thực

Secret Manager có khái niệm automatic destroy policy thông qua expire_time hoặc ttl trên version. Khi thời hạn đến, Secret Manager tự động destroy version đó. Đây không phải một "state" mà là một scheduled operation.


Alias latest — tiện lợi và rủi ro

Secret Manager cung cấp alias đặc biệt latest luôn trỏ đến version có số thứ tự cao nhất ở trạng thái ENABLED.

bash
# Đọc version cụ thể (recommended cho production)
gcloud secrets versions access 3 --secret="database-password"

# Đọc qua alias latest (tiện nhưng không deterministic)
gcloud secrets versions access latest --secret="database-password"

Cơ chế hoạt động của latest

latest không phải một pointer tĩnh mà được resolve dynamically tại thời điểm request. Cụ thể:

  1. Khi tạo version mới (AddSecretVersion), version đó tự động ở ENABLED
  2. latest resolve sang version ENABLED có số thứ tự cao nhất
  3. Nếu version đó bị disable, latest tự động chuyển sang version ENABLED cao nhất tiếp theo
versions: [1(DISABLED), 2(ENABLED), 3(ENABLED), 4(DISABLED)]
latest → 3 (không phải 4 vì 4 đã DISABLED)

Tại sao production không nên dùng latest cho critical paths

Google docs chính thức khuyến cáo tham chiếu bằng version number cụ thể trong production:

"Reference secrets by specific version numbers rather than the latest alias. This approach allows you to validate and rollback updates using established release processes."

Lý do kỹ thuật: nếu một secret version bị push nhầm (với value sai hoặc corrupted), tất cả service đang dùng latest sẽ ngay lập tức bị ảnh hưởng mà không có warning. Với version number cụ thể, deployment pipeline phải chủ động cập nhật config để dùng version mới — cho phép controlled rollout và easy rollback.

Tuy nhiên, latest hoàn toàn hợp lý cho:

  • Development environments
  • Non-critical configuration values
  • Rotation workflows nơi application cần tự động nhận value mới nhất

Replication — Automatic vs User-Managed

Replication là một trong những khái niệm bị hiểu sai nhiều nhất trong Secret Manager. Nhiều engineer mặc định dùng automatic replication mà không hiểu trade-off thực sự.

Secret Manager tồn tại ở đâu về mặt vật lý?

Secret Manager là global service từ góc nhìn API — bạn tạo secret trong một project và truy cập nó từ bất kỳ đâu. Nhưng secret data (payload) phải được lưu ở đâu đó vật lý, và đây là nơi replication policy có ý nghĩa.

Automatic Replication

yaml
replication:
  automatic: {}  # Không cần config gì thêm

Với automatic replication:

  • Google tự quyết định lưu secret data ở nhiều region trong Google's infrastructure
  • Bạn không biết data nằm ở region nào
  • Google có thể thay đổi location theo thời gian
  • Giá: một location (không tính theo số region thực tế lưu)

Cơ chế bên trong: Automatic replication sử dụng Google's distributed storage infrastructure, tương tự cách Spanner lưu data. Metadata về replication decisions không expose ra API. Google optimize để maximize availability và minimize latency từ vị trí của caller.

Khi nào dùng: Default choice cho hầu hết các secrets không có yêu cầu data residency.

User-Managed Replication

yaml
replication:
  userManaged:
    replicas:
    - location: us-central1
    - location: europe-west1
    - location: asia-northeast1

Với user-managed replication:

  • Bạn chỉ định chính xác các region lưu data
  • Secret data được replicated đồng bộ đến tất cả các regions được chỉ định
  • Giá: per-location (nếu chỉ định 3 regions = tính 3 locations)
  • Đảm bảo data residency compliance (GDPR, PCI-DSS...)

Cơ chế bên trong: Mỗi replica là một independent copy của secret payload. Write operations (tạo/update version) phải đến tất cả replicas trước khi được confirm. Read operations có thể serve từ bất kỳ replica nào gần caller nhất.

Hạn chế quan trọng: User-managed replication policy không thể thay đổi sau khi tạo secret. Nếu bạn tạo secret với user-managed replication và sau đó muốn thêm/xóa region, bạn phải tạo secret mới và migrate.

Regional Secrets — khái niệm khác biệt

Bên cạnh automatic và user-managed, Secret Manager còn có regional secrets — đây là loại secret khác về bản chất, không phải chỉ là cấu hình replication:

Endpoint: secretmanager.{REGION}.rep.googleapis.com
Resource: projects/{project}/locations/{region}/secrets/{secret-id}

Regional secrets:

  • Được enforce data-at-rest tại một region cụ thể (không replicate sang region khác)
  • Data in-transit cũng được xử lý trong region đó
  • Phục vụ các compliance requirement nghiêm ngặt nhất (data sovereignty)
  • Có regional endpoint thay vì global endpoint secretmanager.googleapis.com

Lưu ý: Regional secrets là preview ở thời điểm viết, không available ở tất cả regions.


Encryption at rest và CMEK integration

Theo mặc định, Secret Manager mã hoá tất cả secret data ở rest bằng Google-managed AES-256 keys. Đây là mã hoá server-side, tự động, không cần cấu hình gì thêm.

Với CMEK (Customer-Managed Encryption Keys):

yaml
replication:
  userManaged:  # CMEK chỉ available với user-managed replication
    replicas:
    - location: us-central1
      customerManagedEncryption:
        kmsKeyName: "projects/my-project/locations/us-central1/keyRings/my-ring/cryptoKeys/my-key"

CMEK cho Secret Manager sử dụng mô hình envelope encryption: Secret Manager sinh ra một DEK ngẫu nhiên cho mỗi secret version, mã hoá payload bằng DEK đó, rồi wrap DEK bằng KEK từ Cloud KMS. (Chi tiết về envelope encryption được phân tích ở file 04.envelope-encryption-cmek.md.)

Hạn chế quan trọng: CMEK với Secret Manager chỉ available cho user-managed replication, không hỗ trợ automatic replication. Đây là lý do tại sao nhiều compliance workload buộc phải dùng user-managed replication ngay cả khi không có yêu cầu data residency cụ thể.


IAM model cho Secrets

Secret Manager sử dụng resource-level IAM — IAM policy có thể được gắn ở cấp độ Secret, không chỉ ở cấp Project/Folder/Organization.

bash
# Grant access ở cấp Secret (fine-grained)
gcloud secrets add-iam-policy-binding database-password \
  --member="serviceAccount:app-sa@my-project.iam.gserviceaccount.com" \
  --role="roles/secretmanager.secretAccessor"

# Role phổ biến:
# roles/secretmanager.secretAccessor    → chỉ đọc secret values
# roles/secretmanager.secretVersionAdder → thêm version mới
# roles/secretmanager.admin              → full control

IAM conditions hoạt động với Secret Manager và cho phép các pattern như:

  • Chỉ cho phép đọc secret trong giờ làm việc (time-based)
  • Chỉ cho phép đọc secrets với label cụ thể (resource-based)

Một điều quan trọng: IAM trên Secret apply cho tất cả versions của secret đó. Bạn không thể grant access cho "chỉ version 3" mà không grant access cho tất cả versions. Nếu cần isolation ở cấp version, tạo secret riêng cho mỗi "phase".


Constraints và giới hạn thực tế

Giới hạn kích thước payload

Secret payload tối đa là 64 KiB (65,536 bytes). Đây không phải giới hạn arbitrary — nó đến từ fact rằng Secret Manager được thiết kế cho credentials, không phải cho storing arbitrary data. Nếu bạn cần store file lớn hơn (ví dụ: TLS certificate bundle phức tạp), xem xét break ra thành nhiều secrets hoặc dùng Cloud Storage.

Giới hạn số secrets và versions

  • Secrets per project: 10,000 (có thể tăng qua quota request)
  • Versions per secret: Không có hard limit (nhưng practice tốt là clean up versions DESTROYED/DISABLED định kỳ)
  • IAM bindings per secret: 1,500 (giới hạn IAM chung)

Propagation delay

Khi tạo hoặc cập nhật IAM binding cho một secret, có thể mất đến 60 giây để propagation hoàn tất. Trong deployment pipeline, cần account for delay này trước khi service mới được deploy.

Version access latency

Latency cho AccessSecretVersion():

  • Automatic replication: <10ms từ các Google Cloud regions lớn
  • User-managed replication: phụ thuộc vào khoảng cách từ caller đến gần nhất replica

Failure modes

Tham chiếu version bị DESTROYED

Nếu application hardcode version number và version đó bị destroy, application sẽ gặp NOT_FOUND error khi access. Đây là lý do cần có graceful handling cho secret access failures, không phải chỉ panic.

python
from google.cloud import secretmanager
from google.api_core import exceptions

client = secretmanager.SecretManagerServiceClient()
try:
    response = client.access_secret_version(
        request={"name": "projects/my-project/secrets/db-pass/versions/3"}
    )
except exceptions.NotFound:
    # version đã bị destroyed hoặc không tồn tại
    # fallback hoặc alert
    raise
except exceptions.PermissionDenied:
    # IAM không có quyền truy cập
    raise

Circular dependency khi startup

Một anti-pattern phổ biến: application cần đọc secret từ Secret Manager để khởi động, nhưng cần credentials để authenticate đến Secret Manager. Vòng lặp này bị phá vỡ bởi:

  • Workload Identity (GKE): không cần credentials để authenticate
  • Instance metadata (Compute Engine): tương tự
  • Application Default Credentials trong môi trường GCP

Vấn đề chỉ xuất hiện khi dùng Service Account key file để authenticate, tạo ra dependency: cần lưu trữ an toàn key file TRƯỚC KHI có thể truy cập Secret Manager.


Audit logging

Mọi operation trên Secret Manager đều được log vào Cloud Audit Logs:

Log TypeOperationDefault State
Admin ActivityCreate/Delete secret, Update IAMON (không thể tắt)
Data AccessAccessSecretVersion()OFF (phải bật thủ công)

Data Access logs cho Secret Manager là critical cho compliance — mỗi lần đọc secret value được record với: caller identity, source IP, timestamp, secret version accessed. Tuy nhiên, vì volume cao (mỗi application restart có thể generate hàng nghìn access), chi phí log có thể significant. Cần set retention policy hợp lý và xem xét log routing ra BigQuery cho analytics.

bash
# Bật Data Access logging cho Secret Manager
gcloud projects get-iam-policy my-project > policy.yaml
# Thêm vào auditConfigs:
# - auditLogConfigs:
#   - logType: DATA_READ
#   service: secretmanager.googleapis.com
gcloud projects set-iam-policy my-project policy.yaml

References