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: 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