1. Cấu hình Tekton Task để fetch secret từ Vault
1.1. Tạo Vault Token cho Tekton Pipeline
Trước khi Tekton có thể truy cập Vault, bạn cần tạo một token với quyền đọc (read) vào các secret cụ thể mà pipeline cần sử dụng.
Thao tác này giúp phân quyền chính xác, đảm bảo pipeline chỉ lấy được dữ liệu cần thiết, không bị tràn quyền.
Kết quả mong đợi: Một token chuỗi dài được in ra màn hình, có thể dùng trong bước tiếp theo.
vault token create -capability=read -ttl=1h -period=1h -path=secret/data/k8s-prod/database
1.2. Viết Tekton Task để gọi Vault API
Tạo file Task YAML để định nghĩa bước fetch secret. Task này sẽ dùng hình thức HTTP GET đến endpoint của Vault với token đã tạo.
Việc này chuyển đổi việc lấy secret từ Vault thành một bước chuẩn trong pipeline Tekton, giúp trace log dễ dàng.
Kết quả mong đợi: Task được áp dụng vào cluster, sẵn sàng để Pipeline gọi.
Đường dẫn file: /opt/tekton/tasks/vault-fetch-task.yaml
apiVersion: tekton.dev/v1beta1
kind: Task
metadata:
name: vault-fetch-secret
spec:
params:
- name: VAULT_ADDR
default: "http://vault-server:8200"
- name: VAULT_TOKEN
type: string
- name: SECRET_PATH
default: "secret/data/k8s-prod/database"
- name: OUTPUT_DIR
default: "/workspace/secrets"
steps:
- name: fetch-secret
image: curlimages/curl:latest
script:
|
#!/bin/bash
set -e
mkdir -p $(params.OUTPUT_DIR)
# Fetch raw secret data
RESPONSE=$(curl -s --fail \
-H "X-Vault-Token: $(params.VAULT_TOKEN)" \
"$(params.VAULT_ADDR)/v1/$(params.SECRET_PATH)")
# Extract values and write to files
# Assuming secret structure: { "data": { "username": "...", "password": "..." } }
echo "$RESPONSE" | jq -r '.data.username' > $(params.OUTPUT_DIR)/DB_USERNAME
echo "$RESPONSE" | jq -r '.data.password' > $(params.OUTPUT_DIR)/DB_PASSWORD
echo "Secrets fetched and stored in $(params.OUTPUT_DIR)"
workspaces:
- name: output
mountPath: /workspace
workspaces:
- name: output
description: Workspace to store fetched secrets
1.3. Verify kết quả Task
Chạy một Pipeline đơn giản sử dụng Task trên để kiểm tra.
Kiểm tra xem file secret có được tạo ra trong workspace không.
cat /workspace/secrets/DB_USERNAME
2. Đổ secret vào file môi trường hoặc ConfigMap trong quá trình build
2.1. Tạo Pipeline Tekton tích hợp Task fetch
Tạo Pipeline YAML gọi Task đã tạo ở bước 1, sau đó truyền file secret vào bước build (build image hoặc build artifact).
Điều này đảm bảo secret được đưa vào môi trường build (Docker build context hoặc runtime) an toàn mà không hardcode trong code nguồn.
Kết quả mong đợi: Pipeline chạy thành công, secret xuất hiện trong workspace của bước build.
Đường dẫn file: /opt/tekton/pipelines/build-with-vault-pipeline.yaml
apiVersion: tekton.dev/v1beta1
kind: Pipeline
metadata:
name: build-app-with-secrets
spec:
workspaces:
- name: shared-secrets
description: Shared workspace for secrets
tasks:
- name: fetch-secrets
taskRef:
name: vault-fetch-secret
params:
- name: VAULT_TOKEN
value: $(params.VAULT_TOKEN) # Pass token from PipelineRun
- name: SECRET_PATH
value: secret/data/k8s-prod/database
workspaces:
- name: output
workspace: shared-secrets
- name: build-image
taskRef:
name: buildah-push # Assuming you have a buildah task
runAfter:
- fetch-secrets
params:
- name: IMAGE
value: $(params.IMAGE)
- name: CONTEXT
value: $(workspaces.source.path)
workspaces:
- name: source
workspace: source
- name: output
workspace: shared-secrets
env:
- name: DB_USERNAME
valueFrom:
configMapKeyRef:
name: secret-configmap
key: DB_USERNAME
- name: DB_PASSWORD
value: $(workspaces.shared-secrets.path)/DB_PASSWORD
2.2. Tạo ConfigMap từ Secret file trong bước Deploy
Trong bước deploy (sau khi build), tạo một Task mới để đọc file secret từ workspace và tạo ConfigMap (hoặc Secret object) trong cluster Kubernetes đích.
Việc này giúp ứng dụng nhận secret qua mechanism chuẩn của K8s (EnvVar hoặc Volume Mount) thay vì hardcoded trong image.
Kết quả mong đợi: ConfigMap chứa secret được tạo trong cluster đích.
Đường dẫn file: /opt/tekton/tasks/create-configmap-task.yaml
apiVersion: tekton.dev/v1beta1
kind: Task
metadata:
name: create-configmap-from-secrets
spec:
params:
- name: CONFIGMAP_NAME
default: app-secrets
- name: NAMESPACE
default: production
steps:
- name: create-cm
image: kubectl/kubectl:latest
script:
|
#!/bin/bash
set -e
# Read from workspace
DB_USER=$(cat /workspace/secrets/DB_USERNAME)
DB_PASS=$(cat /workspace/secrets/DB_PASSWORD)
# Create ConfigMap (Note: Use Secret object for production, this is for demo)
kubectl create configmap $(params.CONFIGMAP_NAME) \
--from-literal=DATABASE_USERNAME=$(DB_USER) \
--from-literal=DATABASE_PASSWORD=$(DB_PASS) \
--namespace=$(params.NAMESPACE) \
--dry-run=client -o yaml | kubectl apply -f -
workspaces:
- name: secrets
mountPath: /workspace/secrets
workspaces:
- name: secrets
2.3. Verify ConfigMap
Chạy lệnh kiểm tra nội dung ConfigMap vừa tạo.
kubectl get configmap app-secrets -n production -o yaml
3. Triển khai Vault Agent Sidecar Injector vào ArgoCD
3.1. Cài đặt Vault Agent Sidecar Injector
Triển khai Vault Agent Sidecar Injector vào cluster Kubernetes. Component này lắng nghe các Pod được tạo và tự động thêm container Vault Agent vào nếu Pod có annotation yêu cầu.
Đây là bước quan trọng để ArgoCD (và các app khác) có thể tự động inject secret mà không cần sửa code source.
Kết quả mong đợi: Pod của injector chạy trong namespace `kube-system` hoặc namespace đã chỉ định.
kubectl apply -f https://github.com/hashicorp/vault-k8s/releases/download/v0.21.0/vault-agent-sidecar-injector.yaml
3.2. Cấu hình ArgoCD Application để sử dụng Injector
Chỉnh sửa manifest của ArgoCD Application (hoặc Resource trong Git repo) để thêm annotation kích hoạt injection.
Annotation này báo cho Vault Agent Sidecar Injector biết Pod cần được inject với cấu hình cụ thể (vault address, auth method, secret path).
Kết quả mong đợi: Khi ArgoCD sync, Pod mới được tạo sẽ có thêm container Vault Agent chạy bên cạnh container ứng dụng.
Đường dẫn file trong Git repo: manifests/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-app
namespace: production
spec:
replicas: 1
selector:
matchLabels:
app: my-app
template:
metadata:
annotations:
# Enable injection
vault.hashicorp.com/agent-inject: "true"
vault.hashicorp.com/agent-inject-secret: "secret/data/k8s-prod/database:app-secrets"
vault.hashicorp.com/agent-inject-template: |
{{- with secret "secret/data/k8s-prod/database" -}}
export DB_USERNAME="{{ .Data.data.username }}"
export DB_PASSWORD="{{ .Data.data.password }}"
{{- end }}
vault.hashicorp.com/role: "argocd-role"
vault.hashicorp.com/auth-method: "kubernetes"
spec:
serviceAccountName: argocd-sa
containers:
- name: my-app
image: my-registry/my-app:latest
env:
- name: DB_USERNAME
valueFrom:
secretKeyRef:
name: app-secrets
key: DB_USERNAME
- name: DB_PASSWORD
valueFrom:
secretKeyRef:
name: app-secrets
key: DB_PASSWORD
3.3. Verify Injector hoạt động
Khởi tạo lại Pod để kích hoạt sync của ArgoCD và kiểm tra container bên trong Pod.
Chạy lệnh xem danh sách container trong Pod, phải thấy thêm container `vault-agent`.
kubectl get pod -n production -l app=my-app -o jsonpath='{.spec.containers[*].name}'
4. Quản lý vòng đời secret: Tự động luân chuyển (Rotation)
4.1. Cấu hình Policy Rotation trong Vault
Cấu hình Vault để tự động luân chuyển secret khi có yêu cầu hoặc theo định kỳ. Sử dụng engine `kv-v2` hoặc `secret-backend` hỗ trợ versioning.
Việc này đảm bảo khi deploy mới, ArgoCD và Tekton sẽ lấy phiên bản mới nhất của secret mà không cần can thiệp thủ công.
Kết quả mong đợi: Vault tự động tạo version mới cho secret khi giá trị được update.
vault secrets enable -path=k8s-prod/ kv-v2
vault kv put k8s-prod/database username="admin_v2" password="new_secure_password_v2"
4.2. Cấu hình ArgoCD để Trigger Sync khi Secret thay đổi
Trong trường hợp secret được lưu trong Git (không khuyến khích) hoặc khi secret được fetch từ Vault trong runtime (như bước 3), ArgoCD cần biết khi nào cần refresh.
Tuy nhiên, với mô hình Sidecar Injector, việc "rotation" xảy ra khi Pod được restart. Để tự động hóa, ta dùng `ArgoCD App of Apps` hoặc webhook để trigger `kubectl rollout restart` khi secret version thay đổi.
Ở đây ta cấu hình `Prune` và `Sync Policy` để đảm bảo state của K8s luôn khớp với manifest mới nhất (nếu secret được quản lý qua Vault Agent).
Đường dẫn file: manifests/argocd-app.yaml
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: my-app
namespace: argocd
spec:
project: default
source:
repoURL: https://github.com/your-repo/infra
targetRevision: HEAD
path: manifests
destination:
server: https://kubernetes.default.svc
namespace: production
syncPolicy:
automated:
prune: true
selfHeal: true
syncOptions:
- CreateNamespace=true
- PrunePropagationPolicy=foreground
- PruneLast=true
4.3. Tự động hóa Rotation qua Tekton Pipeline
Tạo một Tekton Pipeline riêng biệt (Rotation Pipeline) được trigger khi có sự kiện trong Vault (qua webhook) hoặc theo schedule (Cron).
Pipeline này sẽ: (1) Update secret trong Vault, (2) Trigger ArgoCD sync hoặc restart deployment.
Kết quả mong đợi: Secret được update, Pod tự động restart và nhận secret mới.
Đường dẫn file: /opt/tekton/pipelines/secret-rotation-pipeline.yaml
apiVersion: tekton.dev/v1beta1
kind: PipelineRun
metadata:
name: rotate-secrets-trigger
spec:
pipelineRef:
name: rotation-pipeline
params:
- name: SECRET_PATH
value: "secret/data/k8s-prod/database"
- name: NEW_VALUE
value: "rotated_password_$(date +%s)"
workspaces:
- name: shared
volumeClaimName: rotation-pvc
4.4. Verify Rotation
Kiểm tra log của Pod sau khi rotation để xác nhận ứng dụng đã nhận secret mới.
Chạy lệnh xem log của container ứng dụng, tìm keyword "DB_PASSWORD" hoặc "Connection".
kubectl logs -n production -l app=my-app --tail=50 | grep -i "password\|connect"
Đ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 4: Triển khai và cấu hình ArgoCD cho GitOps
Phần 6: Áp dụng chính sách bảo mật và kiểm soát truy cập »