3 giờ sáng. Mình vừa push xong đống Ansible playbooks lên GitHub để sync với server mới. Năm phút sau, điện thoại rung — email từ GitHub Security Alert: “We found a potential secret exposed in your repository.” Database password, WordPress auth key, API key của monitoring tool — tất cả nằm thẳng trong vars/main.yml, plain text, public repo. Đó là lần đầu tiên mình học được bài học về Ansible Vault theo cách đắt nhất có thể.
Nếu bạn đang dùng Ansible để quản lý infrastructure và lưu playbook trên Git — dù là private repo — đây là vấn đề bạn cần giải quyết trước khi nó thành sự cố thật.
Bài toán: Secrets trong Ansible Playbook và Git
Ansible playbook thường cần rất nhiều thông tin nhạy cảm: database password, SSH private key, API token của cloud provider, Telegram bot token, SMTP password… Những thứ này phải tồn tại trong codebase để Ansible dùng được. Commit lên Git thì lộ secrets. Không commit thì team không share được, server mới không có vars để chạy.
Đây là conflict kinh điển: infrastructure-as-code muốn mọi thứ trong Git, nhưng secrets không được phép vào Git.
So sánh 4 Cách Quản Lý Secrets trong Ansible
Cách 1: Hardcode thẳng vào vars file
# vars/main.yml — ĐỪNG LÀM THẾ NÀY
db_password: "MyP@ssw0rd123"
api_key: "sk-ant-api03-xxxxxxxxxxxxx"
wp_secret_key: "abcdefghijklmnop"
Tiện ngay lúc đó, nhưng một khi đã commit lên Git — dù bạn xóa đi sau — nó vẫn còn trong git history. git log -p sẽ tìm thấy. GitHub Secret Scanner sẽ tìm thấy. Bot scan public repo sẽ tìm thấy trong vài phút sau khi push.
Cách 2: Environment variables
# vars/main.yml
db_password: "{{ lookup('env', 'DB_PASSWORD') }}"
api_key: "{{ lookup('env', 'API_KEY') }}"
Tốt hơn về bảo mật — không có gì nhạy cảm trong git. Nhưng khi deploy lên 10 server, bạn phải set env vars trên từng server. Khi onboard member mới, phải share qua Slack DM hay email — cũng không an toàn. Khi rotate password, phải update từng chỗ thủ công.
Cách 3: External Secret Manager (HashiCorp Vault, AWS Secrets Manager)
# vars/main.yml với HashiCorp Vault lookup
db_password: "{{ lookup('hashi_vault', 'secret=secret/db/password:value') }}"
Enterprise solution — audit log đầy đủ, access control chi tiết, auto-rotation. Nhưng để chạy HashiCorp Vault bạn cần thêm một cụm server riêng, cần cấu hình authentication, cần maintain. Với team nhỏ hoặc side project, đây là overkill. AWS Secrets Manager thì tốt nhưng bị lock-in vào AWS và tốn thêm tiền mỗi tháng.
Cách 4: Ansible Vault (built-in, miễn phí)
Ansible Vault là tính năng có sẵn trong Ansible — không cần cài thêm gì. Nó encrypt file vars bằng AES-256, bạn commit file đã encrypt lên Git hoàn toàn an toàn. Chỉ người có vault password mới decrypt được.
Phân Tích Ưu Nhược Điểm
| Approach | Bảo mật | Độ tiện | Team share | Chi phí |
|---|---|---|---|---|
| Hardcode | Rất tệ | Rất dễ | Dễ | Miễn phí |
| Env vars | Tốt | Phức tạp | Khó | Miễn phí |
| External Vault | Tốt nhất | Rất phức tạp | Tốt | Tốn tiền / infra |
| Ansible Vault | Tốt | Vừa phải | Tốt | Miễn phí |
Ansible Vault nằm ở điểm sweet spot: đủ an toàn, đủ tiện, team share được qua git, không tốn tiền. Cho 80% use case — VPS cá nhân, team nhỏ đến vừa, startup — đây là lựa chọn hợp lý nhất.
Khi Nào Nên Chọn Ansible Vault?
- Đang dùng Ansible để deploy hoặc configure server
- Team size từ 1–20 người, chưa cần enterprise secret management
- Muốn lưu toàn bộ infrastructure config trên Git, bao gồm cả secrets
- Không muốn setup thêm infrastructure chỉ để quản lý secrets
- Compliance không yêu cầu audit log chi tiết cho từng lần truy cập secret
Nếu bạn cần fine-grained access control theo role, audit trail đầy đủ, hoặc đang ở quy mô enterprise — lúc đó mới nghĩ đến HashiCorp Vault.
Hướng Dẫn Triển Khai Ansible Vault
Bước 1: Tạo vault password mạnh
Vault password là chìa khóa decrypt toàn bộ secrets — nó cần đủ mạnh và phải được lưu trữ an toàn (password manager của team, không phải sticky note hay Slack message).
Mình dùng password generator tại toolcraft.app để tạo vault password — tool chạy 100% trên trình duyệt nên không lo password bị gửi lên server nào cả. Tạo xong thì lưu ngay vào Bitwarden hoặc 1Password của team.
# Tạo file vault password trong project (nhớ thêm vào .gitignore)
echo "your-very-strong-vault-password-here" > .vault_pass
chmod 600 .vault_pass
Bước 2: Tạo và encrypt file secrets
Tách secrets ra file riêng — convention phổ biến là vars/vault.yml hoặc group_vars/all/vault.yml. Đặt prefix vault_ cho tên biến để dễ phân biệt:
# vars/vault.yml — nội dung TRƯỚC khi encrypt
vault_db_password: "MySecretDbPass123!"
vault_api_key: "sk-ant-api03-xxxxxxxxxxxxxxxxx"
vault_wp_secret_key: "random-64-char-string-here"
vault_telegram_bot_token: "1234567890:ABCDEFxxxxx"
vault_smtp_password: "smtp-password-here"
# Encrypt file (nếu file chưa tồn tại, dùng 'create' thay vì 'encrypt')
ansible-vault encrypt vars/vault.yml --vault-password-file .vault_pass
# Tạo file mới và mở editor để nhập nội dung ngay
ansible-vault create vars/vault.yml --vault-password-file .vault_pass
Sau khi encrypt, file trông như này — hoàn toàn safe để commit lên git:
$ANSIBLE_VAULT;1.1;AES256
38663266623431313530356635663438656665383066346433383836326438633165303832666562
3566356334363931313630353962373061386564326266310a623539353531313135356235363033
...
Bước 3: Tham chiếu vault vars trong playbook
File vars/main.yml (không encrypt, commit bình thường) tham chiếu đến vault vars:
# vars/main.yml — không có secrets, chỉ có tham chiếu
db_password: "{{ vault_db_password }}"
api_key: "{{ vault_api_key }}"
wp_secret_key: "{{ vault_wp_secret_key }}"
# deploy.yml
---
- hosts: webservers
vars_files:
- vars/main.yml
- vars/vault.yml
tasks:
- name: Configure database
template:
src: templates/db.conf.j2
dest: /etc/myapp/db.conf
# Template dùng {{ db_password }} như bình thường
Bước 4: Chạy playbook với vault
# Chạy với vault password file
ansible-playbook deploy.yml --vault-password-file .vault_pass
# Set biến môi trường (tiện cho CI/CD)
export ANSIBLE_VAULT_PASSWORD_FILE=.vault_pass
ansible-playbook deploy.yml
# Nhập password thủ công (cho môi trường dev)
ansible-playbook deploy.yml --ask-vault-pass
Bước 5: Cấu hình .gitignore
# .gitignore
.vault_pass
*.vault_pass
.ansible_vault_password
# KHÔNG thêm vars/vault.yml — file đã encrypt, an toàn để commit
Bước 6: Xem và chỉnh sửa vault file
# Xem nội dung đã decrypt (không lưu ra file plain text)
ansible-vault view vars/vault.yml --vault-password-file .vault_pass
# Chỉnh sửa trực tiếp (mở editor, tự encrypt lại khi save)
ansible-vault edit vars/vault.yml --vault-password-file .vault_pass
Bước 7: Rotate vault password khi cần
Khi team member nghỉ việc hoặc vault password bị nghi ngờ lộ — rotate ngay:
# Rekey với password mới
ansible-vault rekey vars/vault.yml \
--vault-password-file .vault_pass \
--new-vault-password-file .vault_pass_new
# Sau đó thay file password cũ bằng file mới
mv .vault_pass_new .vault_pass
Tích Hợp với CI/CD (GitHub Actions)
Lưu vault password vào GitHub repository secret, sau đó dùng trong workflow:
# .github/workflows/deploy.yml
- name: Deploy with Ansible
env:
ANSIBLE_VAULT_PASSWORD: ${{ secrets.ANSIBLE_VAULT_PASSWORD }}
run: |
echo "$ANSIBLE_VAULT_PASSWORD" > /tmp/.vault_pass
chmod 600 /tmp/.vault_pass
ansible-playbook deploy.yml --vault-password-file /tmp/.vault_pass
rm /tmp/.vault_pass
Những Lỗi Hay Gặp
Lỗi: “Decryption failed (no vault secrets would decrypt)” — Sai vault password hoặc file vault bị corrupt. Kiểm tra lại file .vault_pass, đặc biệt là trailing space hay Windows line ending:
cat -A .vault_pass
# Cuối dòng phải là $ (newline Unix)
# Nếu thấy ^M$ là đang có Windows line ending — cần fix:
sed -i 's/\r//' .vault_pass
Lỡ commit file chưa encrypt: Dùng BFG Repo Cleaner để xóa khỏi git history, sau đó rotate tất cả secrets đã lộ ngay lập tức — không trì hoãn.
Chỉ cần encrypt một giá trị đơn lẻ: Ansible Vault hỗ trợ inline encryption, paste kết quả thẳng vào vars file:
ansible-vault encrypt_string 'my-secret-value' \
--name 'vault_db_password' \
--vault-password-file .vault_pass
Sau Incident Đó
Sau cái đêm 3 giờ sáng đó, mình migrate toàn bộ Ansible playbooks sang dùng Vault trong khoảng 2 tiếng. Pattern rất đơn giản: tách secrets ra vars/vault.yml, encrypt, commit lên git, team share vault password qua password manager. Vault password thì tạo từ toolcraft.app — chạy client-side hoàn toàn nên không có gì được gửi lên server bên thứ ba.
Đây không phải giải pháp hoàn hảo — nếu vault password bị lộ thì mọi secrets đều lộ theo. Nhưng so với hardcode plain text trong git hoặc share secrets qua Slack, Ansible Vault là bước tiến bộ đáng kể với effort triển khai tối thiểu. Với phần lớn team nhỏ và vừa đang dùng Ansible, đây là đủ — và tốt hơn nhiều so với không làm gì.
