1. Cấu hình Vault Policy để phân quyền cấp phát chứng chỉ
Để đảm bảo an toàn, chúng ta cần giới hạn quyền của Kubernetes ServiceAccount chỉ được cấp chứng chỉ cho các domain cụ thể mà ứng dụng đó sở hữu, không cho phép cấp phát tùy tiện.
Tạo file policy mới để định nghĩa quyền hạn cho ứng dụng sample-app.
cat
Kết quả mong đợi: File policy được tạo thành công trên local hoặc server, sẵn sàng để load vào Vault.
Load policy này vào Vault sử dụng token admin (hoặc token có quyền write policy).
export VAULT_TOKEN="your-admin-token-here"
vault policy write sample-app-policy /tmp/sample-app-policy.hcl
Kết quả mong đợi: Vault trả về thông báo "Success!" và policy "sample-app-policy" đã được lưu trữ.
Tiếp theo, tạo một Role trong Vault PKI mount để áp dụng policy này, giới hạn domain và TTL.
export VAULT_ADDR="http://vault-server:8200"
vault write pki/roles/sample-app \
allowed_domains="sample-app.local" \
allow_subdomains=true \
max_ttl="24h" \
ttl="24h" \
key_type="ec" \
key_bits=256 \
key_usage="signcrl,key_encipherment" \
ext_key_usage="server_auth"
Kết quả mong đợi: Role "sample-app" được tạo với các tham số bảo mật ECDSA, chỉ cho phép cấp phát cho sample-app.local và các subdomain.
Tạo một Kubernetes Auth Method role để liên kết ServiceAccount với Vault Policy đã tạo.
vault write auth/kubernetes/role/sample-app-role \
bound_service_account_names="sample-app-sa" \
bound_service_account_namespaces="default" \
policies="sample-app-policy" \
ttl="1h"
Kết quả mong đợi: Role "sample-app-role" được tạo, khi ServiceAccount "sample-app-sa" trong namespace "default" login vào Vault sẽ nhận được policy "sample-app-policy".
2. Triển khai ứng dụng mẫu với Service và Ingress
Chuẩn bị môi trường Kubernetes để chạy ứng dụng mẫu, tạo ServiceAccount và cấu hình Ingress để kích hoạt luồng yêu cầu chứng chỉ.
Tạo file Kubernetes manifest chứa Deployment, Service, ServiceAccount và Ingress.
cat
Kết quả mong đợi: File YAML được tạo, chứa đầy đủ cấu hình để Kubernetes nhận diện và quản lý chứng chỉ qua Ingress.
Áp dụng cấu hình vào cluster Kubernetes.
kubectl apply -f /tmp/sample-app-deployment.yaml
Kết quả mong đợi: Kubernetes báo "deployment.apps/sample-app created", "service/sample-app created", "serviceaccount/sample-app-sa created", và "ingress.networking.k8s.io/sample-app-ingress created".
Kiểm tra trạng thái Ingress và Pod.
kubectl get ingress sample-app-ingress
kubectl get pods -l app=sample-app
Kết quả mong đợi: Ingress hiện trạng thái "Active" hoặc "Pending" (tùy thuộc vào khi nào Caddy cấp phát), Pod ở trạng thái "Running".
3. Cấu hình Caddy để tự động phát hiện Ingress và cấp chứng chỉ
Cấu hình Caddy như một Ingress Controller trong Kubernetes, tích hợp với Vault để tự động xin và làm mới chứng chỉ khi phát hiện Ingress mới.
Tạo file ConfigMap chứa cấu hình Caddy, định nghĩa listener và tích hợp Vault.
cat
Kết quả mong đợi: ConfigMap "caddy-config" được tạo trong namespace default.
Triển khai Caddy như một Deployment trong Kubernetes (giả định đã có Docker image Caddy với plugin Vault).
cat
Kết quả mong đợi: File deployment Caddy được tạo, cấu hình để mount ConfigMap và lấy token từ Secret.
Tạo ServiceAccount và Secret chứa Vault Token cho Caddy (trong thực tế nên dùng K8s auth flow, nhưng ở đây đơn giản hóa để test).
kubectl create serviceaccount caddy-sa
# Giả định bạn đã có token, hoặc dùng vault write auth/kubernetes/login để lấy token cho service-account này
# Tạo secret chứa token
echo "your-caddy-token-here" | kubectl create secret generic vault-token-secret --from-literal=token=- -n default --dry-run=client -o yaml | kubectl apply -f -
Kết quả mong đợi: ServiceAccount và Secret được tạo thành công.
Áp dụng deployment Caddy.
kubectl apply -f /tmp/caddy-deployment.yaml
Kết quả mong đợi: Pod Caddy được khởi tạo, log hiển thị "Serving on :80, :443" và "Provisioning certificate for sample-app.local".
Kiểm tra log của Caddy để xác nhận quá trình giao tiếp với Vault.
kubectl logs -l app=caddy --tail=50
Kết quả mong đợi: Log hiển thị thông báo thành công khi xin chứng chỉ từ Vault, ví dụ: "Obtained certificate from Vault for sample-app.local".
4. Kiểm tra luồng làm việc: Yêu cầu -> Cấp phát -> Làm mới chứng chỉ
Thực hiện kiểm tra toàn bộ luồng từ việc phát hiện Ingress, Caddy yêu cầu Vault, Vault cấp phát, và chứng chỉ được mount vào container ứng dụng.
Truy cập vào ứng dụng qua trình duyệt hoặc curl để kiểm tra SSL.
curl -k https://sample-app.local
openssl s_client -connect sample-app.local:443 -servername sample-app.local | openssl x509 -noout -text
Kết quả mong đợi:
- `curl` trả về nội dung trang mặc định của Nginx.
- `openssl` hiển thị chi tiết chứng chỉ, trường "Issuer" là Vault PKI CA, trường "Subject" là "CN = sample-app.local".
Kiểm tra chứng chỉ trong Kubernetes Secret để xác nhận Caddy đã lưu trữ chứng chỉ.
kubectl get secret sample-app-tls -o yaml
Kết quả mong đợi: Secret hiển thị hai key: `tls.crt` (certificate) và `tls.key` (private key), nội dung được base64 encoded.
Simulate quá trình làm mới chứng chỉ (Renewal) bằng cách xóa secret chứng chỉ cũ, buộc Caddy phải xin mới.
kubectl delete secret sample-app-tls
Kết quả mong đợi: Secret bị xóa, Ingress vẫn hoạt động nhưng chứng chỉ cũ mất.
Kiểm tra log Caddy ngay sau khi xóa secret.
kubectl logs -l app=caddy --tail=20
Kết quả mong đợi: Log Caddy hiển thị "Certificate not found, requesting new certificate from Vault" và sau đó là "Certificate obtained successfully".
Verify lại chứng chỉ mới được cấp phát có ngày hết hạn mới.
openssl s_client -connect sample-app.local:443 -servername sample-app.local 2>/dev/null | openssl x509 -noout -dates
Kết quả mong đợi:
- `notAfter` hiển thị ngày hết hạn mới (thường là +24h so với thời điểm hiện tại theo cấu hình Role).
- Chứng chỉ vẫn hợp lệ và kết nối HTTPS thành công.
Kiểm tra trong Vault xem có chứng chỉ mới được cấp phát trong log hoặc audit.
vault read pki/issue/sample-app -format=json | jq '.data.serial_number'
Kết quả mong đợi: Vault trả về serial number của chứng chỉ vừa cấp, xác nhận luồng làm việc từ K8s -> Caddy -> Vault đã hoạt động khép kín.
Điều hướng series:
Mục lục: Series: Series: Xây dựng hệ thống quản lý mật mã và chứng chỉ số tự động (Automated PKI & Certificate Management) với HashiCorp Vault, Caddy và Kubernetes
« Phần 3: Cấu hình Caddy như một Certificate Authority (CA) tự động
Phần 5: Tự động hóa gia hạn chứng chỉ và quản lý vòng đời chứng chỉ số »