Giới thiệu vấn đề: Ác mộng hardcode credential trong dự án thực tế
Mở file .env dự án ra và thấy nguyên chuỗi password PostgreSQL root, AWS secret key hay private key TLS nằm tênh hênh? Cảnh tượng này chắc chắn không xa lạ gì với anh em sysadmin hay DevOps.
Hệ thống chỉ có 2-3 server thì SSH vào sửa file thủ công vẫn xong. Nhưng khi scale lên 40 microservices với hơn 150 container trên Kubernetes, cách làm này phá sản ngay. Chỉ cần một lập trình viên vô tình git push nhầm file config lên GitHub public, bot quét tự động sẽ vét sạch credential chỉ sau 30 giây.
Sau đợt chuyển đổi hạ tầng từ CentOS 7 sang AlmaLinux 9, team mình quyết định đóng hẳn lỗ hổng này. Bọn mình dựng một cụm quản lý secret tập trung trên CentOS Stream 9. Giải pháp chuẩn chỉ và phổ biến nhất hiện nay chính là HashiCorp Vault.
Cơ chế hoạt động cốt lõi của HashiCorp Vault
Nắm vững 4 khái niệm dưới đây trước khi gõ lệnh sẽ giúp anh em đỡ rối khi debug:
- Storage Backend: Nơi lưu trữ dữ liệu đã mã hóa. Với cụm dưới 5 node, Integrated Storage (Raft) có sẵn trong Vault là lựa chọn gọn nhất. Anh em không cần dựng thêm cụm Consul rời phức tạp bên ngoài.
- Unseal Keys & Shamir’s Secret Sharing: Khởi động xong, Vault luôn ở trạng thái sealed (khóa chặt). Dữ liệu lúc này hoàn toàn không thể đọc. Thuật toán Shamir mặc định bẻ khóa master thành 5 mảnh và yêu cầu ghép tối thiểu 3 mảnh để mở. Nhờ vậy, không một cá nhân nào có thể tự ý mở hòm secret một mình.
- Secrets Engines: Plugin xử lý dữ liệu. Thông dụng nhất là
kv(Key-Value) để cất API key tĩnh. Nếu làm hệ thống lớn, anh em có thể dùng Dynamic Secrets để tự sinh user database tạm thời kèm thời gian sống (TTL). - Policies & Tokens: Vault kiểm soát quyền theo nguyên tắc phân quyền tối thiểu (Least Privilege). Mọi request gửi tới Vault bắt buộc phải đính kèm Token hợp lệ.
Thực hành: Dựng Vault Server trên CentOS Stream 9
Các bước dưới đây được chạy thực tế trên một VPS CentOS Stream 9 sạch (cấu hình 2 vCPU, 4GB RAM).
Bước 1: Cài đặt Vault từ repository HashiCorp
CentOS Stream 9 quản lý gói qua DNF. Đầu tiên, thêm repo chính hãng để tải bản cập nhật mới nhất:
# Cài công cụ quản lý repository
sudo dnf install -y dnf-plugins-core
# Thêm repository HashiCorp chính thức
sudo dnf config-manager --add-repo https://rpm.releases.hashicorp.com/RHEL/hashicorp.repo
# Cài đặt Vault
sudo dnf install -y vault
Kiểm tra binary sau khi cài:
vault --version
Bước 2: Cấu hình Raft Storage và Web UI
Tạo thư mục chứa dữ liệu Raft và phân quyền chuẩn cho user vault:
sudo mkdir -p /opt/vault/data
sudo chown -R vault:vault /opt/vault/data
sudo chmod 750 /opt/vault/data
Ghi đè nội dung file /etc/vault.d/vault.hcl:
storage "raft" {
path = "/opt/vault/data"
node_id = "node1"
}
listener "tcp" {
address = "0.0.0.0:8200"
tls_disable = 1 # Lưu ý: Khi lên production, bắt buộc trỏ tới cert/key TLS xịn
}
api_addr = "http://127.0.0.1:8200"
cluster_addr = "http://127.0.0.1:8201"
ui = true
disable_mlock = true
Bật và chạy service cùng hệ thống:
sudo systemctl enable --now vault
sudo systemctl status vault
Bước 3: Mở cổng Firewall
Nếu server đang bật firewalld, mở port 8200 để truy cập API và Dashboard:
sudo firewall-cmd --permanent --add-port=8200/tcp
sudo firewall-cmd --reload
Bước 4: Khởi tạo cụm và Unseal Vault
Khai báo biến môi trường cho Vault CLI:
export VAULT_ADDR='http://127.0.0.1:8200'
Chạy lệnh init để sinh khóa:
vault operator init
Màn hình sẽ hiển thị 5 Unseal Keys và 1 Initial Root Token:
Unseal Key 1: dE8jFn3q0...
Unseal Key 2: 9kLm2xP1a...
Unseal Key 3: xYz10uVb8...
Unseal Key 4: aB7cdE3f4...
Unseal Key 5: pQ4rtL9s2...
Initial Root Token: hvs.CAESIJ...
Cảnh báo quan trọng: Lưu 5 key này vào password manager an toàn ngay lập tức. Mất các key này đồng nghĩa với việc mất toàn bộ dữ liệu vĩnh viễn.
Mở khóa bằng cách chạy lệnh unseal 3 lần với 3 key bất kỳ:
vault operator unseal <Unseal_Key_1>
vault operator unseal <Unseal_Key_2>
vault operator unseal <Unseal_Key_3>
Kiểm tra thấy dòng Sealed: false là Vault đã sẵn sàng nhận dữ liệu.
Bước 5: Thao tác lưu trữ Secret và mẹo xử lý JSON
Đăng nhập bằng Root Token:
vault login <Initial_Root_Token>
Bật engine Key-Value phiên bản 2 (hỗ trợ versioning và rollback secret):
vault secrets enable -version=2 -path=secret kv
Thử ghi thông tin database của ứng dụng:
vault kv put secret/my-app/database username="db_admin" password="K8#mP$9vL2@xQ!7z" port=5432
Lấy dữ liệu ra kiểm tra:
vault kv get secret/my-app/database
Khi tạo tài khoản hệ thống, mật khẩu cần độ phức tạp cao từ 24 đến 32 ký tự. Thay vì tự gõ bừa, mình hay dùng Password Generator trên ToolCraft. Công cụ này cho phép tùy biến độ dài, chọn ký tự đặc biệt và kiểm tra entropy rất nhanh.
Ngoài ra, khi ứng dụng gọi Vault qua REST API, dữ liệu trả về thường là JSON lồng nhau nhiều cấp. Để soi nhanh cấu trúc payload khi debug curl, mình ném dữ liệu vào JSON Formatter & Validator. Điểm tiện nhất của ToolCraft là toàn bộ code chạy 100% bằng JavaScript client-side trên browser. Không có byte dữ liệu nào gửi về server backend nên rất yên tâm khi xử lý secret nhạy cảm.
Bước 6: Tạo Policy và cấp phát Token an toàn
Tuyệt đối không cấp Root Token cho ứng dụng hay CI/CD pipeline. Hãy tạo một policy chỉ có quyền đọc secret tương ứng. Viết file app-policy.hcl:
cat <<EOF > app-policy.hcl
path "secret/data/my-app/*" {
capabilities = ["read"]
}
EOF
Nạp policy vào hệ thống và tạo Token có thời hạn 24 giờ:
vault policy write my-app-read app-policy.hcl
vault token create -policy="my-app-read" -ttl="24h"
Token này chỉ đọc được dữ liệu trong nhánh secret/my-app/. Sau 24 tiếng, nó tự thu hồi hiệu lực mà không ảnh hưởng tới các dịch vụ khác.
Tổng kết
Triển khai HashiCorp Vault giúp team loại bỏ triệt để thói quen lưu password rải rác trong file text hay git repo. Khi đã quen với vòng đời Unseal, KV Engine và Policy, anh em có thể đào sâu thêm Dynamic Secrets cho MySQL/PostgreSQL hoặc dùng Vault Agent để tự động inject secret thẳng vào Kubernetes Pod.

