Chuyển đến nội dung chính

BÀI 10: CONFIGMAPS VÀ SECRETS

Quản lý configuration với ConfigMaps và sensitive data với Secrets. Immutable ConfigMaps/Secrets, secrets encryption at rest, External Secrets Operator để đồng bộ từ AWS Secrets Manager, GCP Secret Manager, HashiCorp Vault.

ConfigMaps và Secrets trong Kubernetes

Trong môi trường production, ứng dụng cần đọc cấu hình từ môi trường bên ngoài thay vì hardcode vào container image. Kubernetes cung cấp hai cơ chế chuyên biệt: ConfigMap cho dữ liệu cấu hình thông thường và Secret cho dữ liệu nhạy cảm. Bài học này đi sâu vào cả hai, bao gồm mã hóa at-rest và tích hợp với external secret management systems.

ConfigMaps & Secrets trong Kubernetes

ConfigMaps: Quản lý Configuration Data

ConfigMap là gì?

ConfigMap là một Kubernetes object lưu trữ dữ liệu cấu hình dạng key-value. Dữ liệu này được tách biệt hoàn toàn khỏi container image, cho phép bạn thay đổi cấu hình mà không cần build lại image. ConfigMap có thể chứa các chuỗi đơn giản, file cấu hình nhiều dòng, hoặc thậm chí toàn bộ nội dung file.

ConfigMap phù hợp với:

  • Biến môi trường ứng dụng (database host, port, feature flags)
  • Nội dung file cấu hình (nginx.conf, application.properties)
  • Command-line arguments cho container
  • Cấu hình không nhạy cảm khác

Tạo ConfigMap từ Literal Values

# Tạo ConfigMap từ literal values
kubectl create configmap app-config \
  --from-literal=APP_ENV=production \
  --from-literal=APP_PORT=8080 \
  --from-literal=DB_HOST=postgres.default.svc.cluster.local \
  --from-literal=DB_PORT=5432

# Xem ConfigMap
kubectl get configmap app-config -o yaml

Tạo ConfigMap từ File

# Tạo file cấu hình
cat > app.properties << 'EOF'
app.name=my-application
app.version=2.1.0
app.max-connections=100
app.timeout=30s
log.level=INFO
log.format=json
EOF

# Tạo ConfigMap từ file
kubectl create configmap app-properties --from-file=app.properties

# Tạo ConfigMap với custom key name
kubectl create configmap app-properties --from-file=config=app.properties

ConfigMap YAML Definition

apiVersion: v1
kind: ConfigMap
metadata:
  name: app-config
  namespace: default
  labels:
    app: my-app
    version: "2.1.0"
data:
  # Single-line values
  APP_ENV: "production"
  APP_PORT: "8080"
  DB_HOST: "postgres.default.svc.cluster.local"
  DB_PORT: "5432"
  CACHE_TTL: "300"

  # Multi-line file content
  nginx.conf: |
    server {
        listen 80;
        server_name example.com;

        location / {
            proxy_pass http://backend:8080;
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
        }
    }

  application.yaml: |
    spring:
      application:
        name: my-service
      datasource:
        url: jdbc:postgresql://postgres:5432/mydb
    server:
      port: 8080
    logging:
      level:
        root: INFO

Cách Sử Dụng ConfigMap trong Pod

1. Environment Variables từ ConfigMap

apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-app
spec:
  replicas: 3
  selector:
    matchLabels:
      app: my-app
  template:
    metadata:
      labels:
        app: my-app
    spec:
      containers:
      - name: app
        image: my-app:2.1.0
        # Inject từng key từ ConfigMap
        env:
        - name: APP_ENV
          valueFrom:
            configMapKeyRef:
              name: app-config
              key: APP_ENV
        - name: APP_PORT
          valueFrom:
            configMapKeyRef:
              name: app-config
              key: APP_PORT
        # Inject tất cả keys từ ConfigMap
        envFrom:
        - configMapRef:
            name: app-config

2. Volume Mount từ ConfigMap

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-app
spec:
  replicas: 2
  selector:
    matchLabels:
      app: nginx-app
  template:
    metadata:
      labels:
        app: nginx-app
    spec:
      containers:
      - name: nginx
        image: nginx:1.27
        ports:
        - containerPort: 80
        volumeMounts:
        - name: nginx-config
          mountPath: /etc/nginx/conf.d
          readOnly: true
        - name: app-properties
          mountPath: /app/config
          readOnly: true
      volumes:
      - name: nginx-config
        configMap:
          name: app-config
          items:
          - key: nginx.conf
            path: default.conf
      - name: app-properties
        configMap:
          name: app-config
          items:
          - key: application.yaml
            path: application.yaml
          defaultMode: 0444

3. Command-Line Arguments từ ConfigMap

apiVersion: v1
kind: Pod
metadata:
  name: app-with-args
spec:
  containers:
  - name: app
    image: my-app:2.1.0
    command: ["/app/server"]
    args:
    - "--port=$(APP_PORT)"
    - "--env=$(APP_ENV)"
    env:
    - name: APP_PORT
      valueFrom:
        configMapKeyRef:
          name: app-config
          key: APP_PORT
    - name: APP_ENV
      valueFrom:
        configMapKeyRef:
          name: app-config
          key: APP_ENV

Secrets: Quản lý Dữ Liệu Nhạy Cảm

Secrets là gì và Tại sao Base64 KHÔNG Phải Encryption?

Secret trong Kubernetes lưu trữ dữ liệu nhạy cảm như mật khẩu, token, và TLS certificates. Một điều quan trọng cần hiểu: Secrets mặc định chỉ được base64 encode, KHÔNG được mã hóa. Base64 chỉ là một cách encoding để truyền dữ liệu nhị phân qua text channel — bất kỳ ai cũng có thể decode dễ dàng.

# Base64 encode
echo -n "my-password" | base64
# Output: bXktcGFzc3dvcmQ=

# Base64 decode - rất dễ dàng!
echo "bXktcGFzc3dvcmQ=" | base64 -d
# Output: my-password

Điều này có nghĩa là ai có quyền đọc Secret trong etcd hoặc qua Kubernetes API đều có thể xem được giá trị thực. Do đó cần thêm các lớp bảo mật mà chúng ta sẽ thảo luận sau.

Các Loại Secret

1. Opaque (Generic) Secret

# Tạo Opaque secret từ literal
kubectl create secret generic db-credentials \
  --from-literal=username=postgres \
  --from-literal=password=S3cur3P@ssw0rd \
  --from-literal=host=postgres.default.svc.cluster.local

# Tạo từ file
kubectl create secret generic tls-cert \
  --from-file=tls.crt=./server.crt \
  --from-file=tls.key=./server.key
apiVersion: v1
kind: Secret
metadata:
  name: db-credentials
  namespace: default
type: Opaque
data:
  # Values phải được base64 encoded
  username: cG9zdGdyZXM=
  password: UzNjdXIzUEBzc3cwcmQ=
  host: cG9zdGdyZXMuZGVmYXVsdC5zdmMuY2x1c3Rlci5sb2NhbA==
stringData:
  # stringData tự động encode - dễ đọc hơn khi viết YAML
  connection-string: "postgresql://postgres:S3cur3P@ssw0rd@postgres:5432/mydb"

2. TLS Secret

# Tạo TLS secret từ certificate files
kubectl create secret tls my-tls-secret \
  --cert=path/to/tls.crt \
  --key=path/to/tls.key
apiVersion: v1
kind: Secret
metadata:
  name: my-tls-secret
type: kubernetes.io/tls
data:
  tls.crt: LS0tLS1CRUdJTi... # base64 encoded certificate
  tls.key: LS0tLS1CRUdJTi... # base64 encoded private key

3. Docker Registry Secret

# Tạo Docker registry secret
kubectl create secret docker-registry regcred \
  --docker-server=registry.example.com \
  --docker-username=my-user \
  --docker-password=my-password \
  [email protected]
apiVersion: v1
kind: Secret
metadata:
  name: regcred
type: kubernetes.io/dockerconfigjson
data:
  .dockerconfigjson: eyJhdXRocyI6eyJyZWdpc3RyeS5leGFtcGxlLmNvbSI6eyJ1c2VybmFtZSI6Im15LXVzZXIiLCJwYXNzd29yZCI6Im15LXBhc3N3b3JkIn19fQ==

Sử Dụng Secret trong Pod

Mounting Secret như Environment Variables

apiVersion: apps/v1
kind: Deployment
metadata:
  name: backend-app
spec:
  replicas: 3
  selector:
    matchLabels:
      app: backend
  template:
    metadata:
      labels:
        app: backend
    spec:
      containers:
      - name: backend
        image: backend:1.0.0
        env:
        - name: DB_USERNAME
          valueFrom:
            secretKeyRef:
              name: db-credentials
              key: username
        - name: DB_PASSWORD
          valueFrom:
            secretKeyRef:
              name: db-credentials
              key: password
        - name: DB_HOST
          valueFrom:
            secretKeyRef:
              name: db-credentials
              key: host
      imagePullSecrets:
      - name: regcred

Mounting Secret như Volume

apiVersion: apps/v1
kind: Deployment
metadata:
  name: secure-app
spec:
  replicas: 2
  selector:
    matchLabels:
      app: secure-app
  template:
    metadata:
      labels:
        app: secure-app
    spec:
      containers:
      - name: app
        image: secure-app:1.0.0
        volumeMounts:
        - name: secret-volume
          mountPath: /etc/secrets
          readOnly: true
        - name: tls-certs
          mountPath: /etc/tls
          readOnly: true
      volumes:
      - name: secret-volume
        secret:
          secretName: db-credentials
          defaultMode: 0400  # Chỉ owner có thể đọc
      - name: tls-certs
        secret:
          secretName: my-tls-secret
          items:
          - key: tls.crt
            path: server.crt
          - key: tls.key
            path: server.key
            mode: 0400

Immutable ConfigMaps và Secrets

Tại Sao Cần Immutable?

Trong large clusters với hàng nghìn Pod, mỗi ConfigMap/Secret change đều trigger một watch event đến tất cả kubelet. Điều này tạo ra significant load lên kube-apiserver. Kubernetes 1.21+ hỗ trợ immutable ConfigMaps và Secrets — một tối ưu hóa quan trọng cho production clusters.

Lợi ích của immutable:

  • Giảm tải kube-apiserver: kubelet không cần watch changes
  • Tăng tính ổn định: ngăn chặn accidental updates có thể break applications
  • Hiệu suất tốt hơn đáng kể với số lượng Pod lớn
apiVersion: v1
kind: ConfigMap
metadata:
  name: app-config-v2
  namespace: default
  labels:
    app: my-app
    version: "2.0.0"
data:
  APP_ENV: "production"
  APP_VERSION: "2.0.0"
  DB_POOL_SIZE: "20"
immutable: true  # Không thể thay đổi sau khi tạo
apiVersion: v1
kind: Secret
metadata:
  name: api-keys-v1
  namespace: default
type: Opaque
data:
  stripe-key: c2tfdGVzdF8xMjM0NTY3ODkw
  sendgrid-key: U0cuYWJjZGVmZ2hpams=
immutable: true  # Secret không thể bị modify
# Cố gắng update immutable ConfigMap sẽ fail
kubectl patch configmap app-config-v2 \
  --patch '{"data":{"APP_ENV":"staging"}}'
# Error: configmap "app-config-v2" is immutable

# Để "update" immutable resource, tạo version mới
kubectl create configmap app-config-v3 \
  --from-literal=APP_ENV=staging \
  --from-literal=APP_VERSION=2.1.0

# Xóa version cũ sau khi migrate
kubectl delete configmap app-config-v2

Secrets Encryption At Rest

Vấn Đề với Default Storage

Mặc định, Secrets được lưu trong etcd dưới dạng base64 plain text. Bất kỳ ai có quyền đọc etcd hoặc backup của etcd đều có thể xem toàn bộ secret values. Đây là một security risk nghiêm trọng trong production.

EncryptionConfiguration

Kubernetes hỗ trợ encryption at rest thông qua EncryptionConfiguration — một file cấu hình cho kube-apiserver chỉ định cách mã hóa resources trước khi lưu vào etcd.

# /etc/kubernetes/encryption-config.yaml
apiVersion: apiserver.config.k8s.io/v1
kind: EncryptionConfiguration
resources:
  - resources:
    - secrets
    - configmaps  # Có thể mã hóa cả ConfigMaps
    providers:
    # AES-GCM: Khuyến nghị nhất - authenticated encryption
    - aescbc:
        keys:
        - name: key1
          secret: 
    # AES-CBC: Legacy, vẫn supported
    - aesgcm:
        keys:
        - name: key1
          secret: 
    # identity: không mã hóa - dùng để migrate hoặc decrypt
    - identity: {}
# Tạo encryption key
head -c 32 /dev/urandom | base64

# Output (ví dụ):
# 4MhFHOTCNF/i9C2BpZCUlFxH3MBXdFYn2JeUNuT4EQs=

# Cấu hình kube-apiserver sử dụng encryption config
# Thêm flag vào kube-apiserver manifest:
# --encryption-provider-config=/etc/kubernetes/encryption-config.yaml

# Verify encryption đang hoạt động
kubectl create secret generic test-secret \
  --from-literal=key=my-secret-value

# Đọc trực tiếp từ etcd (không qua API)
ETCDCTL_API=3 etcdctl get \
  --endpoints=https://127.0.0.1:2379 \
  --cacert=/etc/kubernetes/pki/etcd/ca.crt \
  --cert=/etc/kubernetes/pki/etcd/server.crt \
  --key=/etc/kubernetes/pki/etcd/server.key \
  /registry/secrets/default/test-secret | hexdump -C

# Nếu encrypted, output sẽ bắt đầu bằng "k8s:enc:aescbc:v1:key1:..."
# Thay vì plaintext base64

# Encrypt tất cả existing secrets
kubectl get secrets --all-namespaces -o json | kubectl replace -f -

External Secrets Operator

Tại Sao Cần External Secrets Operator?

Encryption at rest bảo vệ secrets trong etcd, nhưng vẫn còn một vấn đề: secrets vẫn được quản lý trong Kubernetes. Trong enterprise environments, secrets thường được quản lý tập trung tại AWS Secrets Manager, HashiCorp Vault, GCP Secret Manager, Azure Key Vault. External Secrets Operator (ESO) giải quyết vấn đề này bằng cách tự động đồng bộ secrets từ các external systems vào Kubernetes Secrets.

Cài Đặt External Secrets Operator

# Cài ESO qua Helm
helm repo add external-secrets https://charts.external-secrets.io
helm repo update

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

# Verify installation
kubectl get pods -n external-secrets
kubectl get crd | grep external-secrets

SecretStore và ClusterSecretStore CRDs

ESO sử dụng hai loại CRD chính: SecretStore (namespace-scoped) và ClusterSecretStore (cluster-wide). Đây là nơi bạn cấu hình kết nối đến external secret backend.

ClusterSecretStore cho AWS Secrets Manager

apiVersion: external-secrets.io/v1beta1
kind: ClusterSecretStore
metadata:
  name: aws-secrets-manager
spec:
  provider:
    aws:
      service: SecretsManager
      region: ap-southeast-1
      auth:
        # Sử dụng IRSA (IAM Roles for Service Accounts)
        jwt:
          serviceAccountRef:
            name: external-secrets-sa
            namespace: external-secrets
# IAM Role với permission đọc secrets
# Attach policy: SecretsManagerReadWrite hoặc custom policy

# Service Account với IRSA annotation
apiVersion: v1
kind: ServiceAccount
metadata:
  name: external-secrets-sa
  namespace: external-secrets
  annotations:
    eks.amazonaws.com/role-arn: arn:aws:iam::123456789012:role/external-secrets-role

ExternalSecret để Sync từ AWS Secrets Manager

apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
  name: db-credentials
  namespace: production
spec:
  refreshInterval: 1h  # Sync mỗi 1 giờ
  secretStoreRef:
    name: aws-secrets-manager
    kind: ClusterSecretStore
  target:
    name: db-credentials  # Tên Kubernetes Secret sẽ được tạo
    creationPolicy: Owner
    template:
      type: Opaque
      data:
        # Có thể transform và combine nhiều external secrets
        connection-string: "postgresql://{{ .username }}:{{ .password }}@{{ .host }}:5432/mydb"
  data:
  - secretKey: username
    remoteRef:
      key: production/myapp/db
      property: username
  - secretKey: password
    remoteRef:
      key: production/myapp/db
      property: password
  - secretKey: host
    remoteRef:
      key: production/myapp/db
      property: host

Sync Toàn Bộ AWS Secret

apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
  name: app-secrets-full
  namespace: production
spec:
  refreshInterval: 30m
  secretStoreRef:
    name: aws-secrets-manager
    kind: ClusterSecretStore
  target:
    name: app-secrets
    creationPolicy: Owner
  # Sync toàn bộ secret object
  dataFrom:
  - extract:
      key: production/myapp/all-secrets

SecretStore cho HashiCorp Vault

apiVersion: external-secrets.io/v1beta1
kind: ClusterSecretStore
metadata:
  name: vault-backend
spec:
  provider:
    vault:
      server: "https://vault.example.com:8200"
      path: "secret"
      version: "v2"  # KV v2
      auth:
        # Kubernetes Auth Method
        kubernetes:
          mountPath: "kubernetes"
          role: "my-app-role"
          serviceAccountRef:
            name: external-secrets-sa
            namespace: external-secrets
apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
  name: vault-secrets
  namespace: production
spec:
  refreshInterval: 15m
  secretStoreRef:
    name: vault-backend
    kind: ClusterSecretStore
  target:
    name: vault-app-secrets
    creationPolicy: Owner
  data:
  - secretKey: api-key
    remoteRef:
      key: secret/data/production/myapp
      property: api-key
  - secretKey: jwt-secret
    remoteRef:
      key: secret/data/production/myapp
      property: jwt-secret
  dataFrom:
  - extract:
      key: secret/data/production/database
# Kiểm tra trạng thái sync
kubectl get externalsecret -n production
kubectl describe externalsecret db-credentials -n production

# Output mong đợi:
# Status:
#   Binding:
#     Name: db-credentials
#   Conditions:
#     Last Transition Time: 2026-03-30T10:00:00Z
#     Message: Secret was synced
#     Reason: SecretSynced
#     Status: True
#     Type: Ready

Best Practices cho ConfigMaps và Secrets

1. Không Commit Secrets Vào Git

# .gitignore
*-secret.yaml
secrets/
*.env
.env.*

# Sử dụng git-secrets để prevent accidental commits
brew install git-secrets
git secrets --install
git secrets --register-aws

2. RBAC Cho Secrets

# Giới hạn quyền đọc secrets
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: secret-reader
  namespace: production
rules:
- apiGroups: [""]
  resources: ["secrets"]
  verbs: ["get"]
  resourceNames: ["db-credentials", "api-keys"]  # Chỉ cho phép đọc specific secrets
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: app-secret-binding
  namespace: production
subjects:
- kind: ServiceAccount
  name: my-app-sa
  namespace: production
roleRef:
  kind: Role
  name: secret-reader
  apiGroup: rbac.authorization.k8s.io

3. Secret Rotation

# Với External Secrets Operator, rotation tự động:
# 1. Update secret trong AWS Secrets Manager/Vault
# 2. ESO tự động sync vào Kubernetes Secret sau refreshInterval
# 3. Pods cần được restart để nhận secret mới (nếu dùng env vars)

# Với volume mount, secret updates được reflect tự động (sau sync period)
# Với env vars, cần restart pod

# Trigger restart
kubectl rollout restart deployment/my-app -n production

4. Secret Namespace Isolation

# Secrets chỉ accessible trong cùng namespace
# Để share secrets giữa namespaces, dùng ESO với ClusterSecretStore
# hoặc sync secret sang multiple namespaces

apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
  name: shared-secret
  namespace: staging  # Deploy vào namespace staging
spec:
  refreshInterval: 1h
  secretStoreRef:
    name: vault-backend
    kind: ClusterSecretStore
  target:
    name: shared-api-keys
  dataFrom:
  - extract:
      key: secret/data/shared/api-keys

5. Audit Logging Cho Secret Access

# audit-policy.yaml
apiVersion: audit.k8s.io/v1
kind: Policy
rules:
# Log tất cả secret operations ở level Request
- level: Request
  verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]
  resources:
  - group: ""
    resources: ["secrets"]
# Log metadata cho các operations khác
- level: Metadata
  resources:
  - group: ""
    resources: ["pods", "services"]

Tổng Kết

ConfigMaps và Secrets là nền tảng của configuration management trong Kubernetes. Những điểm quan trọng cần nhớ:

  • ConfigMap cho non-sensitive configuration, hỗ trợ env vars, volume mount, và command args
  • Secret mặc định chỉ base64 encode — KHÔNG phải encryption, cần thêm lớp bảo mật
  • Immutable ConfigMaps/Secrets cải thiện performance đáng kể trong large clusters
  • EncryptionConfiguration với AES-GCM/AES-CBC mã hóa secrets at rest trong etcd
  • External Secrets Operator là giải pháp tốt nhất cho production: sync từ Vault, AWS Secrets Manager, giảm attack surface
  • Luôn áp dụng RBAC chặt chẽ, không commit secrets vào Git, và có kế hoạch rotation