Chiến lược Backup và Recovery cho Vault PKI và Caddy
Việc mất dữ liệu Vault đồng nghĩa với mất toàn bộ chuỗi chứng chỉ (Certificate Chain) và khóa riêng tư (Private Key) của CA. Chúng ta cần thực hiện backup định kỳ cho storage backend của Vault và trạng thái của Caddy.
Bước 1: Cấu hình Backup tự động cho Vault (File Storage Backend)
Nếu bạn đang chạy Vault với file backend (phổ biến trong môi trường dev/test hoặc single-node), hãy sử dụng command snapshot. Trong môi trường production với Raft hoặc Consul, command này vẫn áp dụng tương tự nhưng cần chạy trên node leader.
Tại sao: Để lưu trữ snapshot của toàn bộ trạng thái dữ liệu Vault, bao gồm cả secrets engine PKI, vào một file nhị phân an toàn.
Kết quả mong đợi: File backup được tạo ra và có thể restore lại khi cần.
Trước tiên, đảm bảo bạn đã set biến môi trường Vault, sau đó chạy command backup:
export VAULT_ADDR='https://127.0.0.1:8200'
export VAULT_TOKEN='s.HASH_TOKEN_TUAU'
vault operator raft snapshot save /vault/backup/vault-snapshot-$(date +%F).bin
Để tự động hóa, thêm vào crontab của node chạy Vault (ví dụ backup mỗi ngày lúc 2h sáng):
echo "0 2 * * * root VAULT_ADDR='https://127.0.0.1:8200' VAULT_TOKEN='s.HASH_TOKEN_TUAU' vault operator raft snapshot save /vault/backup/vault-snapshot-$(date +%F).bin" >> /etc/crontab
Bước 2: Backup dữ liệu Caddy
Caddy lưu trữ trạng thái trong file `caddy.json` hoặc `caddy.db` (SQLite) và chứng chỉ trong thư mục `/.local/share/caddy` hoặc `pki/` tùy cấu hình.
Tại sao: Nếu Caddy bị lỗi hoặc mất dữ liệu, bạn cần khôi phục cấu hình và các chứng chỉ đã cấp phát (nếu lưu trữ cục bộ) để tránh gián đoạn dịch vụ.
Kết quả mong đợi: Thư mục chứa dữ liệu Caddy được nén và lưu vào vị trí an toàn.
Script backup Caddy (giả sử chạy trong container hoặc host Linux):
mkdir -p /backup/caddy
tar -czf /backup/caddy/caddy-backup-$(date +%F).tar.gz \
/etc/caddy/Caddyfile \
/var/lib/caddy/pki \
/var/lib/caddy/caddy.db 2>/dev/null || echo "Backup completed (some files may not exist)"
Bước 3: Quy trình Recovery (Restore) Vault
Khôi phục Vault đòi hỏi phải tắt service, xóa dữ liệu hiện tại (nếu bị hỏng), và load lại snapshot.
Tại sao: Vault không cho phép ghi đè dữ liệu trực tiếp khi đang chạy để đảm bảo tính nhất quán. Cần khởi động lại với snapshot mới.
Kết quả mong đợi: Vault khởi động lại và có đầy đủ dữ liệu PKI cũ (root CA, intermediate CA, certs đã cấp).
Lưu ý: Đảm bảo bạn đang ở trên node leader (nếu dùng Raft). Thực hiện restore như sau:
# Tạm dừng Vault
systemctl stop vault
# Backup dữ liệu hiện tại (nếu có) để phòng ngừa
mv /vault/data /vault/data.old
# Restore từ snapshot (chọn file mới nhất)
vault operator raft snapshot restore /vault/backup/vault-snapshot-2023-10-27.bin
# Khởi động lại Vault
systemctl start vault
Verify kết quả: Kiểm tra xem Root CA và Intermediate CA có còn hoạt động không.
VAULT_ADDR='https://127.0.0.1:8200' vault read pki/intermediate/cert
Bước 4: Quy trình Recovery Caddy
Khôi phục Caddy bằng cách giải nén file backup vào đúng vị trí cấu hình và khởi động lại service.
Tại sao: Caddy cần file cấu hình và database để biết trạng thái chứng chỉ đang chạy.
Kết quả mong đợi: Caddy khởi động thành công và các service nội bộ vẫn có chứng chỉ hợp lệ.
cd /var/lib/caddy
tar -xzf /backup/caddy/caddy-backup-$(date +%F).tar.gz --overwrite
systemctl restart caddy
Verify kết quả: Kiểm tra log khởi động của Caddy.
systemctl status caddy | grep -i "listening"
Triển khai mTLS giữa các dịch vụ nội bộ trong Kubernetes
Trong môi trường microservices, giao tiếp nội bộ qua HTTP là rủi ro lớn. Chúng ta sẽ dùng Vault PKI để cấp phát chứng chỉ cho từng Pod và cấu hình mTLS (Mutual TLS) giữa Client và Server.
Bước 1: Tạo Role và Policy cho mTLS trong Vault
Cần tạo một role trong PKI engine của Vault cho phép cấp phát chứng chỉ ngắn hạn (short-lived) cho các workload trong cluster.
Tại sao: Giới hạn thời gian sống (TTL) của chứng chỉ giúp giảm thiểu rủi ro nếu khóa bị lộ. Role này sẽ được dùng để cấp cert cho cả client và server.
Kết quả mong đợi: Role `k8s-mtls` được tạo với TTL 24h và khả năng tạo cả server và client cert.
export VAULT_ADDR='https://127.0.0.1:8200'
export VAULT_TOKEN='s.HASH_TOKEN_TUAU'
vault write pki/roles/k8s-mtls \
allowed_domains="svc.cluster.local" \
max_ttl="24h" \
key_usage="DigitalSignature,KeyEncipherment" \
ext_key_usage="ServerAuth,ClientAuth" \
allow_subdomains=true \
allow_any_name=false
Bước 2: Cấu hình Kubernetes Service Account và RBAC
Để Vault nhận diện Pod, ta cần gắn nhãn (label) hoặc Service Account vào Pod và cấu hình RBAC để Vault có thể xác thực JWT của Pod đó.
Tại sao: Vault cần biết Pod nào được phép yêu cầu chứng chỉ. Đây là cơ chế Zero Trust: không ai được cấp cert trừ khi được Kubernetes xác thực.
Kết quả mong đợi: Service Account có quyền tạo JWT token để tương tác với Vault.
Tạo Service Account và RoleBinding (giả sử namespace `default`):
cat
Bước 3: Cấu hình Vault Agent Sidecar để cấp phát và gia hạn chứng chỉ
Sử dụng Vault Agent như một container sidecar trong Pod để tự động lấy chứng chỉ từ Vault và lưu vào volume mount.
Tại sao: Tách biệt việc quản lý chứng chỉ khỏi ứng dụng chính. Agent sẽ tự động renew (gia hạn) chứng chỉ trước khi hết hạn mà không cần restart Pod.
Kết quả mong đợi: Pod có file `tls.crt` và `tls.key` tự động được tạo và cập nhật.
Tạo manifest cho Pod với Vault Agent Sidecar (đường dẫn config agent bên dưới):
cat
Tạo ConfigMap cho Vault Agent (`vault-agent-config`):
cat
Và tạo ConfigMap cho template (để sinh file cert/key từ Vault):
cat
Lưu ý: Trong thực tế, bạn cần tạo Role `k8s-mtls-role` trong Vault trước để agent có thể auth. Command tạo role (chạy trên terminal Vault):
vault write auth/kubernetes/role/k8s-mtls-role \
bound_service_account_names=mtls-client \
bound_service_account_namespaces=default \
ttl=24h \
type=serviceaccount
Bước 4: Cấu hình Caddy để yêu cầu mTLS
Cấu hình Caddy (đóng vai trò API Gateway hoặc LoadBalancer nội bộ) để yêu cầu client phải gửi chứng chỉ hợp lệ.
Tại sao: Server sẽ từ chối kết nối nếu client không có chứng chỉ được ký bởi CA của Vault.
Kết quả mong đợi: Client không có chứng chỉ sẽ bị từ chối (400/500), Client có chứng chỉ đúng sẽ được phục vụ.
File cấu hình Caddy (`/etc/caddy/Caddyfile`):
cat
Chú ý: Để Caddy có thể verify client cert, nó cần Root CA của Vault. Đảm bảo Root CA đã được mount vào container Caddy và cấu hình `client_auth verify`.
Verify kết quả: Sử dụng curl để test mTLS từ một Pod khác.
curl --cert /etc/ssl/certs/tls.crt --key /etc/ssl/private/tls.key \
--cacert /etc/ssl/certs/root-ca.crt \
https://frontend.default.svc.cluster.local:8443
Phân tích log Vault và Caddy để debug lỗi cấp phát chứng chỉ
Khi hệ thống PKI gặp sự cố (cert hết hạn, từ chối kết nối, lỗi ký), log là nguồn thông tin duy nhất. Chúng ta cần biết cách đọc và lọc log hiệu quả.
Bước 1: Kích hoạt chế độ Debug cho Vault
Thay đổi cấu hình `log_level` trong file `vault.hcl` hoặc biến môi trường `VAULT_LOG_LEVEL`.
Tại sao: Mặc định Vault chỉ log `info` hoặc `warn`. Lỗi chi tiết về quá trình ký chứng chỉ thường nằm ở `debug`.
Kết quả mong đợi: Vault in ra dòng log chi tiết cho mỗi request API.
Chỉnh sửa file cấu hình Vault (ví dụ `/etc/vault/config.hcl`):
cat
Khởi động lại Vault: `systemctl restart vault`
Để xem log thời gian thực khi có lỗi cấp phát:
journalctl -u vault -f | grep -i "pki\|sign\|error\|denied"
Bước 2: Phân tích log Caddy khi lỗi handshake
Caddy log rất giàu thông tin về TLS handshake. Nếu client không thể kết nối, log sẽ chỉ rõ lý do (cert expired, wrong CA, missing client cert).
Tại sao: Xác định nhanh xem lỗi nằm ở phía client (không gửi cert) hay server (không nhận diện cert).
Kết quả mong đợi: Log hiển thị lỗi cụ thể như "TLS handshake error" kèm lý do.
Chạy Caddy ở chế độ debug (thêm flag `-debug` hoặc `-adapter caddyfile -config /etc/caddy/Caddyfile -debug`):
systemctl stop caddy
caddy run --config /etc/caddy/Caddyfile --adapter caddyfile -debug 2>&1 | tee /var/log/caddy-debug.log
Lọc log tìm lỗi TLS:
grep -i "tls\|handshake\|certificate" /var/log/caddy-debug.log
Bước 3: Debug lỗi "Cert Expired" hoặc "Revoked"
Khi chứng chỉ hết hạn hoặc bị thu hồi, client sẽ nhận lỗi ngay lập tức.
Tại sao: Cần kiểm tra lại TTL và trạng thái trong Vault để biết chứng chỉ đã được gia hạn chưa.
Kết quả mong đợi: Xác định được ID của chứng chỉ bị lỗi và trạng thái hiện tại.
Trong Vault, liệt kê các chứng chỉ đã cấp (cần policy đủ quyền):
vault read pki/intermediate/cert | grep -i "serial_number"
vault list pki/intermediate/revoked
Để kiểm tra một chứng chỉ cụ thể (dùng openssl với file cert trên disk):
openssl x509 -in /etc/ssl/certs/tls.crt -noout -dates -text | grep -i "not after\|not before\|issuer"
Verify kết quả: So sánh ngày hết hạn trong log Vault và ngày hết hạn trong file cert.
Tối ưu hiệu năng khi xử lý hàng ngàn chứng chỉ đồng thời
Khi hệ thống mở rộng lên hàng ngàn microservices, việc cấp phát và gia hạn chứng chỉ đồng thời có thể làm quá tải Vault và Caddy.
Bước 1: Cấu hình HashiCorp Vault với Raft Storage Backend
Chuyển đổi từ File Storage sang Raft (Raft Storage) để có khả năng mở rộng ngang (horizontal scaling) và chịu tải cao hơn.
Tại sao: File storage bị giới hạn bởi I/O của một node. Raft cho phép phân tán dữ liệu và cân bằng tải giữa các node.
Kết quả mong đợi: Vault cluster có thể xử lý hàng ngàn request/giây mà không bị nghẽn cổ chai I/O.
File cấu hình `raft.hcl` (áp dụng cho tất cả node):
cat
Bước 2: Tối ưu Caddy với Connection Limits và Keep-Alive
Giới số lượng kết nối đồng thời và bật HTTP/2 để giảm overhead handshake TLS.
Tại sao: Caddy có thể bị quá tải nếu có quá nhiều kết nối TLS mới đồng thời. HTTP/2 cho phép multiplexing trên một kết nối TCP duy nhất, giảm chi phí handshake.
Kết quả mong đợi: Server xử lý nhiều client hơn với ít tài nguyên CPU hơn.
Cấu hình Caddy (`/etc/caddy/Caddyfile`):
cat
Bước 3: Sử dụng Cache cho chứng chỉ Root CA
Cấu hình Caddy và các client để cache Root CA, tránh việc phải tải lại CA cho mỗi kết nối mới.
Tại sao: Việc tải và verify Root CA tốn CPU. Nếu CA không đổi, cache giúp giảm tải đáng kể.
Kết quả mong đợi: Giảm thời gian handshake TLS xuống mức thấp nhất.
Trong Vault Agent hoặc script gia hạn, đảm bảo Root CA chỉ được tải một lần và lưu vào file tĩnh, không tải lại mỗi lần renew:
mkdir -p /vault/ca-cache
curl -s https://vault.example.com/v1/pki/ca/pem --output /vault/ca-cache/root-ca.pem
chmod 644 /vault/ca-cache/root-ca.pem
Trong Caddy, chỉ định file CA cache này:
# Trong Caddyfile
tls {
client_auth verify
client_crl /vault/ca-cache/root-ca.pem
}
Bước 4: Monitor và Alert cho hiệu năng
Sử dụng Prometheus để thu thập metrics từ Vault và Caddy để phát hiện nghẽn cổ chai.
Tại sao: Cần biết khi nào hệ thống đạt ngưỡng 80% CPU hoặc latency tăng cao để scale kịp thời.
Kết quả mong đợi: Dashboard hiển thị số lượng request/s, latency P99, và số lượng chứng chỉ đang hoạt động.
Enable metrics trong Vault (thêm vào config `config.hcl`):
cat
Enable metrics trong Caddy (cần plugin hoặc cấu hình Prometheus trong Caddy 2):
# Trong Caddyfile
metrics {
listen :9101
}
EOF
Verify kết quả: Truy cập endpoint metrics để kiểm tra.
curl http://localhost:9100/metrics | grep vault_core_requests_total
curl http://localhost:9101/metrics | grep caddy_http_requests_total
Đ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 5: Tự động hóa gia hạn chứng chỉ và quản lý vòng đời chứng chỉ số