Triển khai Vault Operator và khởi tạo instance Vault
Bước đầu tiên là cài đặt Vault Operator bằng Helm để quản lý vòng đời của Vault trên Kubernetes. Operator sẽ tự động tạo các resource cần thiết như StatefulSet, Service và ConfigMap.
Tại sao: Sử dụng Helm chart chính thức của HashiCorp giúp chuẩn hóa quy trình triển khai, hỗ trợ tự động scaling và xử lý lỗi, giảm thiểu việc cấu hình thủ công YAML phức tạp.
Kết quả mong đợi: Namespace vault được tạo, pod Vault chạy ở trạng thái Running hoặc Pending (chờ Unseal).
Đầu tiên, thêm repository Helm chính thức của HashiCorp vào local client.
helm repo add hashicorp https://helm.releases.hashicorp.com
Cập nhật repository để lấy phiên bản mới nhất của chart.
helm repo update
Cài đặt Vault Operator vào namespace vault. Flag --create-namespace tự động tạo namespace nếu chưa tồn tại.
helm install vault hashicorp/vault-operator --namespace vault --create-namespace
Verify: Kiểm tra trạng thái của các pod trong namespace vault.
kubectl get pods -n vault
Bạn sẽ thấy pod vault-operator-xxx đang chạy. Nếu chưa thấy, đợi khoảng 30-60 giây.
Tạo Custom Resource (CR) cho Vault
Tiếp theo, tạo file cấu hình vault.yaml để định nghĩa instance Vault. Đây là tài nguyên custom (CRD) mà Operator sẽ đọc để deploy.
Tại sao: Vault cần một định nghĩa rõ ràng về phiên bản, số lượng replica, loại lưu trữ và cấu hình mạng để Operator có thể tự động hóa việc deploy.
Kết quả mong đợi: Pod Vault được tạo ra, trạng thái ban đầu là Unsealed sau khi khởi động (nếu cấu hình auto-unseal) hoặc Sealed.
Tạo file manifests/vault.yaml với nội dung hoàn chỉnh dưới đây. Cấu hình này sử dụng 3 replica cho HA và lưu trữ local (Raft) để đơn giản hóa bước đầu.
apiVersion: vault.hashicorp.com/v1alpha1
kind: Vault
metadata:
name: vault
namespace: vault
spec:
server:
image: vault:1.17.0
replicas: 3
listener:
tcp:
address: "0.0.0.0:8200"
tls_disable: true
storage:
type: raft
raft:
node_id: "vault"
data_dir: "/vault/data"
retry_join:
attempt: 3
interval: "10s"
dns_servers: ["127.0.0.1:53"]
auto_unseal:
auto_unseal_key_type: "transit"
transit:
url: "https://vault.vault.svc.cluster.local:8200"
token: "root-token-placeholder"
extra_volume_mounts:
- name: "vault-data"
mount_path: "/vault/data"
- name: "vault-secrets"
mount_path: "/vault/secrets"
extra_volumes:
- name: "vault-data"
emptyDir: {}
- name: "vault-secrets"
emptyDir: {}
inject_sidecar: true
sidecar_image: vault:1.17.0
sidecar_template:
metadata:
annotations:
vault.hashicorp.com/agent-inject: "true"
vault.hashicorp.com/agent-inject-status: "true"
vault.hashicorp.com/agent-inject-secret-db-creds: "secret/data/app/db-creds"
vault.hashicorp.com/agent-inject-template-db-creds: "true"
vault.hashicorp.com/agent-reload: "true"
vault.hashicorp.com/agent-reload-path: "vault/secrets/*"
vault.hashicorp.com/agent-run-init: "true"
vault.hashicorp.com/agent-run-clone: "true"
resources:
requests:
cpu: "500m"
memory: "1Gi"
limits:
cpu: "1000m"
memory: "2Gi"
Áp dụng file cấu hình vừa tạo vào cluster.
kubectl apply -f manifests/vault.yaml
Verify: Theo dõi log của pod Vault để đảm bảo quá trình khởi tạo thành công.
kubectl logs -f -l app=vault -n vault
Bạn sẽ thấy thông báo raft: configured as server và unsealed nếu auto-unseal hoạt động. Nếu thấy sealed, bạn cần unseal thủ công (bỏ qua ở bước này vì đang dùng auto-unseal).
Cấu hình lưu trữ backend (Raft Storage)
Vault cần một backend storage để lưu trữ secrets và trạng thái cluster. Trong Kubernetes, ta thường dùng Raft storage với persistent volume hoặc S3.
Tại sao: Raft là protocol consensus giúp đảm bảo tính nhất quán (consistency) giữa các node trong cluster. Nếu không cấu hình storage bền vững (PersistentVolume), dữ liệu sẽ mất khi pod bị restart.
Kết quả mong đợi: Vault lưu dữ liệu vào PersistentVolumeClaim (PVC) thay vì ephemeral storage, đảm bảo dữ liệu không mất khi pod restart.
Cập nhật lại file manifests/vault.yaml để sử dụng StorageClass của cluster. Giả sử cluster của bạn có storage class standard (thường là default trên GKE/EKS/AKS).
apiVersion: vault.hashicorp.com/v1alpha1
kind: Vault
metadata:
name: vault
namespace: vault
spec:
server:
image: vault:1.17.0
replicas: 3
listener:
tcp:
address: "0.0.0.0:8200"
tls_disable: true
storage:
type: raft
raft:
node_id: "vault"
data_dir: "/vault/data"
retry_join:
attempt: 3
interval: "10s"
storage_config:
path: "/vault/data"
storage_volume_claim:
storage_class: "standard"
access_modes:
- ReadWriteOnce
resources:
requests:
storage: 10Gi
auto_unseal:
auto_unseal_key_type: "transit"
transit:
url: "https://vault.vault.svc.cluster.local:8200"
token: "root-token-placeholder"
resources:
requests:
cpu: "500m"
memory: "1Gi"
limits:
cpu: "1000m"
memory: "2Gi"
Áp dụng lại cấu hình đã cập nhật.
kubectl apply -f manifests/vault.yaml
Verify: Kiểm tra xem PVC đã được tạo và binding thành công.
kubectl get pvc -n vault
Bạn sẽ thấy 3 PVC với trạng thái Bound, tương ứng với 3 replica của Vault.
Cấu hình S3 Backend (Tùy chọn cho Production)
Nếu muốn dùng S3 (AWS S3, MinIO, GCS) làm backend thay vì PVC, cần cấu hình storage type là consul hoặc s3 (tùy phiên bản Vault) và cung cấp credentials.
Tại sao: S3 cung cấp độ bền cao hơn, dễ dàng backup và restore, phù hợp cho môi trường production đa vùng (multi-region).
Kết quả mong đợi: Vault lưu state vào bucket S3, không phụ thuộc vào PVC của Kubernetes.
File cấu hình cho S3 backend (ví dụ với MinIO hoặc AWS S3).
apiVersion: vault.hashicorp.com/v1alpha1
kind: Vault
metadata:
name: vault-s3
namespace: vault
spec:
server:
image: vault:1.17.0
replicas: 3
listener:
tcp:
address: "0.0.0.0:8200"
tls_disable: true
storage:
type: s3
s3:
region: "us-east-1"
bucket: "vault-storage-bucket"
access_key: "YOUR_AWS_ACCESS_KEY"
secret_key: "YOUR_AWS_SECRET_KEY"
endpoint: "https://s3.amazonaws.com"
iam_role: "true"
auto_unseal:
auto_unseal_key_type: "transit"
transit:
url: "https://vault.vault.svc.cluster.local:8200"
token: "root-token-placeholder"
resources:
requests:
cpu: "500m"
memory: "1Gi"
Verify: Chạy command vault status từ trong pod để kiểm tra backend.
kubectl exec -it vault-0 -n vault -- vault status
Output sẽ hiển thị Storage Type: s3.
Thiết lập phương thức xác thực Kubernetes (Kubernetes Auth Method)
Sau khi Vault chạy, cần cấu hình Auth Method để các ứng dụng trong cluster có thể xác thực với Vault mà không cần hardcode secret token.
Tại sao: Kubernetes Auth Method sử dụng ServiceAccount Token của Pod để xác thực. Điều này an toàn hơn, token tự động hết hạn và không cần lưu trữ token dài hạn.
Kết quả mong đợi: Vault chấp nhận token từ ServiceAccount Kubernetes, trả về token Vault ngắn hạn (JWT) cho ứng dụng.
Đầu tiên, cần tạo một Secret Engine để lưu trữ thông tin xác thực Kubernetes. Chạy lệnh này từ terminal (cần có token root hoặc quyền admin).
kubectl exec -it vault-0 -n vault -- vault secrets enable kubernetes
Verify: Kiểm tra xem engine đã được enable chưa.
kubectl exec -it vault-0 -n vault -- vault secrets list
Bạn sẽ thấy kubernetes/ trong danh sách.
Cấu hình Kubernetes Auth Method
Bây giờ cấu hình Auth Method để Vault biết cách liên lạc với Kubernetes API Server.
Tại sao: Vault cần biết địa chỉ API Server, CA certificate và role mapping để xác minh token của ServiceAccount.
kubectl exec -it vault-0 -n vault -- vault auth enable kubernetes
Cấu hình thông tin kết nối đến Kubernetes API Server. Lưu ý kubernetes_host là internal DNS của kube-apiserver.
kubectl exec -it vault-0 -n vault -- vault write auth/kubernetes/config \
kubernetes_host=https://kubernetes.default.svc \
kubernetes_ca_cert=@/var/run/secrets/kubernetes.io/serviceaccount/ca.crt
Verify: Kiểm tra cấu hình đã được ghi nhận.
kubectl exec -it vault-0 -n vault -- vault read auth/kubernetes/config
Bạn sẽ thấy kubernetes_host và kubernetes_ca_cert đã được điền đầy đủ.
Tạo Role Mapping
Tạo role để ánh xạ ServiceAccount Kubernetes vào policy của Vault. Role này xác định ServiceAccount nào được phép lấy token và policy nào được áp dụng.
Tại sao: Đảm bảo chỉ ServiceAccount cụ thể (ví dụ app-deployer) mới được truy cập vào secret cụ thể, tuân thủ nguyên tắc least privilege.
Kết quả mong đợi: ServiceAccount app-deployer trong namespace default có thể xác thực thành công với Vault.
Tạo role app-role ánh xạ đến ServiceAccount app-deployer.
kubectl exec -it vault-0 -n vault -- vault write auth/kubernetes/role/app-role \
bound_service_account_names=app-deployer \
bound_service_account_namespaces=default \
ttl=1h \
policies=default
Verify: Thử xác thực bằng cách giả lập token của ServiceAccount (nếu bạn đã tạo SA). Hoặc đơn giản là kiểm tra role đã tồn tại.
kubectl exec -it vault-0 -n vault -- vault read auth/kubernetes/role/app-role
Output sẽ hiển thị bound_service_account_names: [app-deployer].
Tạo Secret Engine và cấu hình secret mẫu
Bước cuối cùng là thiết lập nơi lưu trữ secret (Secret Engine) và tạo dữ liệu mẫu để ứng dụng sử dụng.
Tại sao: Vault hỗ trợ nhiều loại secret engine (KV v2, Database, Transit, PKI). Ở đây ta dùng KV v2 (Key-Value) để lưu cấu hình, password, connection string.
Kết quả mong đợi: Secret engine app-secrets được tạo, và dữ liệu mẫu db-creds được lưu trữ thành công.
Enable Secret Engine KV v2 với tên app-secrets.
kubectl exec -it vault-0 -n vault -- vault secrets enable -version=2 app-secrets
Verify: Kiểm tra engine đã được enable.
kubectl exec -it vault-0 -n vault -- vault secrets list
Bạn sẽ thấy app-secrets/ trong danh sách.
Lưu trữ Secret mẫu
Ghi một secret mẫu vào engine app-secrets để ứng dụng có thể lấy về.
Tại sao: Cần có dữ liệu thực tế để test quy trình lấy secret trong các bước sau (CI/CD).
kubectl exec -it vault-0 -n vault -- vault kv put app-secrets/db-creds \
username="app_user" \
password="SuperSecretPassword123!" \
connection_string="postgresql://app_user:SuperSecretPassword123!@db-host:5432/mydb"
Verify: Đọc lại secret để đảm bảo dữ liệu đã được lưu.
kubectl exec -it vault-0 -n vault -- vault kv get app-secrets/db-creds
Bạn sẽ thấy các trường username, password và connection_string được hiển thị rõ ràng.
Cấu hình Policy cho ứng dụng
Tạo policy để hạn chế quyền truy cập vào secret này chỉ dành cho role đã tạo ở phần trước.
Tại sao: Mặc định role app-role có policy default, nhưng ta cần tạo policy riêng để kiểm soát chặt chẽ.
Kết quả mong đợi: Policy app-policy được tạo, cho phép đọc secret app-secrets/data/db-creds.
Tạo file policy policies/app-policy.hcl.
path "app-secrets/data/db-creds" {
capabilities = ["read"]
}
path "app-secrets/metadata/db-creds" {
capabilities = ["read"]
}
Áp dụng policy vào Vault.
kubectl exec -it vault-0 -n vault -- vault policy write app-policy - < policies/app-policy.hcl
Cập nhật role app-role để sử dụng policy mới thay vì default.
kubectl exec -it vault-0 -n vault -- vault write auth/kubernetes/role/app-role \
bound_service_account_names=app-deployer \
bound_service_account_namespaces=default \
ttl=1h \
policies=app-policy
Verify: Kiểm tra policy đã được áp dụng cho role.
kubectl exec -it vault-0 -n vault -- vault read auth/kubernetes/role/app-role
Output sẽ hiển thị policies: [app-policy].
Điều hướng series:
Mục lục: Series: Series: Xây dựng hệ thống CI/CD an toàn và tự động hóa cho Kubernetes với ArgoCD, Tekton và HashiCorp Vault
« Phần 1: Chuẩn bị môi trường và yêu cầu hệ thống
Phần 3: Xây dựng pipeline CI với Tekton »