Vấn đề: Mối nguy từ việc phân tán Secret
Trong những năm đầu làm sysadmin, việc quản lý cấu hình với tôi dường như rất đơn giản. Tôi chỉ việc tạo một file .env trong thư mục dự án, dán mật khẩu database và API key của Stripe vào, rồi tiếp tục công việc. Quy trình đó nhanh chóng bộc lộ vấn đề. Khi hạ tầng mở rộng lên 18 máy ảo trên cả môi trường staging và production, thông tin xác thực (credential) bị rò rỉ khắp nơi—từ file lịch sử bash, cache build của Docker, lịch sử commit Git cho đến các kênh Slack nội bộ.
Mọi thứ chỉ thực sự thay đổi sau khi cụm staging của chúng tôi hứng chịu một cuộc tấn công credential-stuffing tự động. Sự cố đó chứng minh rằng quy tắc firewall và SSH key chỉ bảo vệ được lớp ngoài cùng. Khi kẻ tấn công chiếm được quyền truy cập shell vào một container ứng dụng web đơn lẻ, các file cấu hình dạng plaintext sẽ làm lộ toàn bộ hệ thống backend. Việc vận hành bảo trì cũng phiền toái không kém. Chỉ một việc xoay vòng mật khẩu PostgreSQL bị lộ đã đòi hỏi cập nhật 24 file cấu hình riêng biệt, khởi động lại sáu microservice và phải lên lịch bảo trì hệ thống.
Để giải quyết triệt để tình trạng phân tán secret, hệ thống cần ba năng lực cốt lõi: tập trung hóa lưu trữ với chuẩn mã hóa mạnh mẽ, thực thi kiểm soát truy cập nghiêm ngặt và tự động tạo credential động có thời hạn tự hủy.
Kiến trúc cốt lõi: Cách thức vận hành của Vault
HashiCorp Vault quản lý secret và bảo vệ dữ liệu nhạy cảm thông qua cơ chế kiểm soát truy cập dựa trên danh tính (identity-based access). Dưới đây là mô hình hoạt động mà bạn sẽ làm việc trên máy chủ Linux:
- Storage Backend: Lớp lưu trữ bền vững đã được mã hóa. Vault hỗ trợ filesystem cục bộ, Consul, bộ lưu trữ Raft tích hợp và các cơ sở dữ liệu. Vault mã hóa toàn bộ dữ liệu ngay trong bộ nhớ trước khi ghi bất kỳ byte nào xuống đĩa.
- Unsealing và Cơ chế chia sẻ bí mật Shamir: Khi khởi động, Vault luôn ở trạng thái khóa (sealed). Nó không thể giải mã các secret đã lưu cho đến khi quản trị viên mở khóa (unseal) bằng master key đã được phân mảnh (thường là 3 trong số 5 mảnh key) hoặc qua khóa Cloud KMS tự động.
- Secrets Engines: Các plugin module hóa dành cho nhiều loại dữ liệu khác nhau. Engine
kv-v2quản lý các secret tĩnh có phiên bản (versioned), trong khi các dynamic engine tạo credential tạm thời theo yêu cầu cho PostgreSQL, MySQL hoặc AWS. - Policies: Các quy tắc truy cập theo đường dẫn (path-based) viết bằng ngôn ngữ HCL (HashiCorp Configuration Language). Chúng cấp quyền rõ ràng như
read,createhoặcupdate. - Leases và TTLs: Mọi secret động và access token đều gắn liền với một khoảng thời gian sống (Time-to-Live). Khi thời hạn thuê (lease) hết hạn, Vault sẽ tự động thu hồi và xóa credential khỏi cơ sở dữ liệu hoặc dịch vụ thượng nguồn.
Thực hành: Triển khai Vault trên Ubuntu Linux
Hãy cùng thực hiện cài đặt Vault trên Ubuntu 24.04/22.04 LTS, cấu hình systemd daemon, khởi tạo storage và cấp phát credential PostgreSQL động.
Bước 1: Cài đặt Vault từ HashiCorp Repository
Cập nhật danh mục package của hệ thống, cài đặt các dependency cần thiết và đăng ký GPG signing key chính thức của HashiCorp:
# Cài đặt các gói phụ thuộc
sudo apt-get update -y
sudo apt-get install -y gpg coreutils curl jq
# Import GPG key của HashiCorp
wget -O- https://apt.releases.hashicorp.com/gpg | sudo gpg --dearmor -o /usr/share/keyrings/hashicorp-archive-keyring.gpg
# Thêm repository chính thức
echo "deb [signed-by=/usr/share/keyrings/hashicorp-archive-keyring.gpg] https://apt.releases.hashicorp.com $(lsb_release -cs) main" | sudo tee /etc/apt/sources.list.d/hashicorp.list
# Cài đặt binary của Vault
sudo apt-get update -y
sudo apt-get install -y vault
Bước 2: Cấu hình Vault Daemon
Vault đọc cấu hình chính từ /etc/vault.d/vault.hcl. Chúng ta sẽ tạo một thư mục dữ liệu riêng, gán quyền sở hữu cho system user vault và cấu hình listener cục bộ.
# Chuẩn bị thư mục dữ liệu với quyền hạn giới hạn
sudo mkdir -p /opt/vault/data
sudo chown -R vault:vault /opt/vault
sudo chmod 750 /opt/vault/data
Ghi các thiết lập daemon vào /etc/vault.d/vault.hcl:
sudo tee /etc/vault.d/vault.hcl << 'EOF'
storage "file" {
path = "/opt/vault/data"
}
listener "tcp" {
address = "127.0.0.1:8200"
tls_disable = 1
}
api_addr = "http://127.0.0.1:8200"
cluster_addr = "http://127.0.0.1:8201"
ui = true
disable_mlock = true
EOF
Kích hoạt systemd unit và xác minh daemon đang hoạt động:
sudo systemctl enable vault
sudo systemctl start vault
sudo systemctl status vault --no-pager
Bước 3: Khởi tạo và Unseal Vault Instance
Thiết lập biến môi trường client để CLI trỏ tới listener cục bộ:
export VAULT_ADDR='http://127.0.0.1:8200'
echo "export VAULT_ADDR='http://127.0.0.1:8200'" >> ~/.bashrc
Khởi tạo storage backend của Vault. Lệnh chạy một lần này sẽ chia master key thành 5 mảnh và yêu cầu ngưỡng 3 mảnh để tái tạo khóa:
vault operator init -key-shares=5 -key-threshold=3 > ~/vault-init-keys.txt
chmod 600 ~/vault-init-keys.txt
Mở file ~/vault-init-keys.txt. Hãy sao lưu ngay 5 unseal key này và root token vào một trình quản lý mật khẩu được mã hóa an toàn.
Nhập 3 unseal key khác nhau để đưa Vault về trạng thái hoạt động:
vault operator unseal <UNSEAL_KEY_1>
vault operator unseal <UNSEAL_KEY_2>
vault operator unseal <UNSEAL_KEY_3>
Kiểm tra trạng thái seal. Hãy đảm bảo output CLI hiển thị Sealed: false:
vault status
Đăng nhập bằng root token để bắt đầu cấu hình các engine:
vault login <INITIAL_ROOT_TOKEN>
Bước 4: Lưu trữ và Truy xuất Static Secret (KV-v2)
Mount engine Key-Value phiên bản 2 tại đường dẫn secret/:
vault secrets enable -path=secret kv-v2
Lưu cấu hình môi trường production cho một payment microservice:
vault kv put secret/myapp/config \
api_key="sk_live_9948571029384756" \
payment_gateway_url="https://api.payment.internal" \
max_retries="5"
Kiểm tra tính hợp lệ của các payload phức tạp hoặc file policy trước khi ghi vào API giúp ngăn ngừa lỗi cú pháp khi vận hành. Khi cần định dạng các output JSON thô, công cụ JSON Formatter & Validator trên ToolCraft là một trợ thủ đắc lực. Công cụ này xử lý hoàn toàn trên trình duyệt mà không gửi bất kỳ token hay cấu hình nào về máy chủ bên ngoài.
Truy xuất dữ liệu đã lưu bằng CLI và lọc các trường dữ liệu bằng jq:
vault kv get -format=json secret/myapp/config | jq '.data.data'
Bước 5: Tự động hóa xoay vòng Credential Database động
Secret tĩnh vẫn tiềm ẩn rủi ro cần thu hồi thủ công nếu bị lộ. Secret động giải quyết vấn đề này bằng cách tạo ra các role database tạm thời, duy nhất theo từng yêu cầu.
Kích hoạt database secrets engine:
vault secrets enable database
Kết nối Vault với instance PostgreSQL đang chạy tại 127.0.0.1:5432:
vault write database/config/my-postgres-db \
plugin_name=postgresql-database-plugin \
allowed_roles="app-readwrite" \
connection_url="postgresql://{{username}}:{{password}}@127.0.0.1:5432/appdb?sslmode=disable" \
username="vault_admin" \
password="VaultAdminSecurePassword123!"
Định nghĩa một application role với TTL mặc định 1 giờ và giới hạn lease tối đa 24 giờ:
vault write database/roles/app-readwrite \
db_name=my-postgres-db \
creation_statements="CREATE ROLE \"{{name}}\" WITH LOGIN PASSWORD '{{password}}' VALID UNTIL '{{expiration}}'; \
GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA public TO \"{{name}}\";" \
default_ttl="1h" \
max_ttl="24h"
Tạo một cặp credential mới bằng cách truy vấn endpoint của role:
vault read database/creds/app-readwrite
Vault sẽ tự động cấp một user riêng (ví dụ: v-token-app-read-0a1b2c3d), gán mật khẩu ngẫu nhiên và đăng ký lease có hiệu lực 3600 giây. Khi lease hết hạn, Vault sẽ tự động xóa role database đó.
Bước 6: Tích hợp với ứng dụng Python
Ứng dụng có thể tự yêu cầu và quản lý credential ngắn hạn của mình khi khởi động bằng package Python hvac chính thức.
import hvac
import psycopg2
# Khởi tạo Vault client
client = hvac.Client(url='http://127.0.0.1:8200', token='s.yourAppAuthTokenHere')
if not client.is_authenticated():
raise PermissionError("Không thể xác thực với Vault daemon.")
# Yêu cầu credential PostgreSQL ngắn hạn (lease mặc định 1 giờ)
creds = client.secrets.database.generate_credentials(name='app-readwrite')
db_user = creds['data']['username']
db_pass = creds['data']['password']
lease_id = creds['lease_id']
print(f"[+] Đã tạo dynamic user: {db_user}")
print(f"[+] Lease ID đang hoạt động: {lease_id} ({creds['lease_duration']}s)")
# Kết nối đến PostgreSQL bằng credential tạm thời
conn = psycopg2.connect(
host="127.0.0.1",
port=5432,
dbname="appdb",
user=db_user,
password=db_pass
)
with conn.cursor() as cur:
cur.execute("SELECT current_user, session_user, now();")
user_info = cur.fetchone()
print(f"[+] Phiên làm việc đang hoạt động: {user_info}")
conn.close()
# Cơ chế gia hạn: background worker có thể gia hạn lease trước khi hết hạn TTL
# client.sys.renew_lease(lease_id=lease_id, increment=3600)
Các bước tiếp theo
Chuyển đổi từ file .env dạng plaintext sang HashiCorp Vault giúp thay thế các credential dùng dài hạn bằng các token ngắn hạn có ghi log kiểm toán đầy đủ. Mật khẩu tĩnh sẽ biến mất hoàn toàn khỏi mã nguồn và ổ cứng máy chủ, trong khi secret động giúp việc xoay vòng credential diễn ra hoàn toàn tự động.
Hãy bắt đầu bằng việc di chuyển các kết nối cơ sở dữ liệu và API key của bên thứ ba sang Vault. Khi các dịch vụ của bạn đã xử lý việc gia hạn token một cách trơn tru, bạn có thể tích hợp tiếp Vault với pipeline CI/CD, cụm Kubernetes và các nhà cung cấp định danh đám mây (cloud identity providers).

