Lab 11 — Ansible Secrets & Vault
Mục tiêu: Sau Lab này, người học hiểu cách quản lý password, API key, database credential, token và các thông tin nhạy cảm trong Ansible một cách an toàn bằng Ansible Vault, đồng thời biết cách kết hợp Vault với
Variables,Templates,RolesvàApplication Deployment.
Đây là Lab rất quan trọng vì từ Lab 10 trở đi, chúng ta bắt đầu triển khai application thực tế. Một hệ thống production không thể để password và secret dạng plaintext trong Git.
1. Bối cảnh
Ở các Lab trước, chúng ta có thể đã viết:
postgres_user: todo
postgres_password: "password123"
hoặc:
github_token: "ghp_xxxxxxxxx"
Điều này không an toàn.
Ví dụ project:
ansible/
├── group_vars/
│ └── all.yml
│
├── roles/
│ └── application/
│
└── site.yml
Nếu all.yml được commit lên Git:
git add .
git commit -m "add production config"
git push
thì secret cũng bị push lên Git.
Git Repository
│
▼
┌─────────────────────┐
│ postgres_password │
│ api_token │
│ aws_secret_key │
│ ssh_private_key │
└─────────────────────┘
Đây là một security incident có thể xảy ra rất dễ dàng.
2. Secret là gì?
Trong thực tế, rất nhiều loại dữ liệu được xem là secret.
Database
DB_PASSWORD
DB_USERNAME
API
API_KEY
ACCESS_TOKEN
Cloud
AWS_ACCESS_KEY
AWS_SECRET_ACCESS_KEY
Third-party services
SLACK_WEBHOOK
SMTP_PASSWORD
Application
JWT_SECRET
ENCRYPTION_KEY
SESSION_SECRET
3. Secret khác Configuration như thế nào?
Đây là distinction rất quan trọng.
Configuration
Ví dụ:
application_port: 8080
application_name: todo
app_environment: production
Có thể commit vào Git.
Secret
Ví dụ:
postgres_password: "..."
jwt_secret: "..."
api_token: "..."
Không nên commit plaintext vào Git.
Có thể hình dung:
Configuration
│
└── Git
│
└── OK
Secret
│
└── Secret Management
│
└── Ansible Vault
4. Mục tiêu của Lab
Sau Lab này, người học có thể:
- Hiểu Secret Management.
- Hiểu tại sao không commit secret vào Git.
- Hiểu Ansible Vault.
- Tạo encrypted file.
- Encrypt variable.
- Decrypt variable.
- Chạy Playbook với Vault.
- Sử dụng Vault Password.
- Sử dụng
--ask-vault-pass. - Sử dụng
--vault-password-file. - Kết hợp Vault với
group_vars. - Kết hợp Vault với Templates.
- Kết hợp Vault với Roles.
- Sử dụng secret trong Docker Compose.
- Không expose secret trong logs.
- Hiểu
no_log. - Rotate secret.
- Hiểu giới hạn của Ansible Vault.
- Biết khi nào nên dùng external secret manager.
5. Ansible Vault là gì?
Ansible Vault là cơ chế của Ansible dùng để mã hóa dữ liệu nhạy cảm.
Thay vì:
postgres_password: "SuperSecret123"
chúng ta lưu encrypted data:
$ANSIBLE_VAULT;1.1;AES256
...
...
...
Người không có Vault password sẽ không đọc được plaintext.
6. Kiến trúc
Ví dụ:
Git
│
┌──────────┴──────────┐
│ │
▼ ▼
Normal Variables Encrypted Secrets
│ │
all.yml vault.yml
│
│ AES256
▼
Encrypted Content
Khi deployment:
Encrypted Vault
│
│ Vault Password
▼
Ansible
│
▼
Decrypted in memory
│
▼
Application
Điểm quan trọng:
Secret được mã hóa khi lưu trữ, nhưng Ansible phải giải mã nó khi sử dụng.
7. Chuẩn bị Project
Tiếp tục project từ Lab 10:
ansible-lab-11/
│
├── ansible.cfg
├── site.yml
│
├── inventory/
│ └── hosts.ini
│
├── group_vars/
│ └── all.yml
│
└── roles/
└── application/
├── defaults/
├── handlers/
├── tasks/
└── templates/
Chúng ta sẽ thêm:
group_vars/
├── all.yml
└── vault.yml
8. Không lưu Secret trong all.yml
Không nên:
application_name: todo
application_port: 8080
postgres_user: todo
postgres_password: "SuperSecret123"
Tách ra:
application_name: todo
application_port: 8080
postgres_user: todo
và:
vault.yml
chứa:
postgres_password: "SuperSecret123"
Nhưng vault.yml phải được encrypt.
9. Tạo Vault File
Chạy:
ansible-vault create group_vars/vault.yml
Ansible hỏi:
New Vault password:
Confirm New Vault password:
Nhập password.
Sau đó editor mở ra.
Nhập:
postgres_password: "SuperSecret123"
jwt_secret: "my-jwt-secret"
Lưu file.
10. Kiểm tra File
cat group_vars/vault.yml
Bạn sẽ không thấy:
postgres_password: "SuperSecret123"
mà thấy:
$ANSIBLE_VAULT;1.1;AES256
...
...
...
Đây là điều chúng ta muốn.
11. Đọc Vault File
Nếu chạy:
ansible-vault view group_vars/vault.yml
Ansible yêu cầu Vault password.
Sau khi nhập đúng:
postgres_password: "SuperSecret123"
jwt_secret: "my-jwt-secret"
12. Edit Vault
Không nên:
vim group_vars/vault.yml
Thay vào đó:
ansible-vault edit group_vars/vault.yml
Ansible sẽ:
Encrypted File
↓
Decrypt
↓
Open Editor
↓
Save
↓
Encrypt again
13. Encrypt một File có sẵn
Giả sử bạn có:
group_vars/vault.yml
plaintext:
postgres_password: "SuperSecret123"
Có thể chạy:
ansible-vault encrypt group_vars/vault.yml
Sau đó file trở thành encrypted.
14. Decrypt File
Khi cần decrypt:
ansible-vault decrypt group_vars/vault.yml
File trở về plaintext.
Nhưng trong project thực tế, không nên decrypt file rồi quên encrypt lại.
Tốt hơn:
ansible-vault edit
15. Encrypt String
Ansible cũng hỗ trợ:
ansible-vault encrypt_string
Ví dụ:
ansible-vault encrypt_string 'SuperSecret123'
Kết quả có dạng:
!vault |
$ANSIBLE_VAULT;1.1;AES256
...
Có thể copy vào YAML.
16. Cách 1 — Encrypt cả File
Cách đơn giản nhất cho người mới:
group_vars/
├── all.yml
└── vault.yml
vault.yml được encrypt toàn bộ.
Ví dụ plaintext:
postgres_password: "SuperSecret123"
jwt_secret: "..."
smtp_password: "..."
Sau khi encrypt:
$ANSIBLE_VAULT;1.1;AES256
...
Đây là cách chúng ta sẽ sử dụng trong Lab.
17. Cách 2 — Encrypt từng Variable
Có thể:
postgres_user: todo
postgres_password: !vault |
$ANSIBLE_VAULT;1.1;AES256
...
Ưu điểm:
all.yml
├── normal config
├── normal config
└── encrypted secret
Nhưng với người mới, cách này khó quản lý hơn.
18. Recommendation
Trong project lớn, có thể tổ chức:
group_vars/
├── all.yml
├── dev.yml
├── staging.yml
├── production.yml
└── vault.yml
Hoặc tốt hơn:
group_vars/
├── all/
│ ├── vars.yml
│ └── vault.yml
│
├── dev/
│ ├── vars.yml
│ └── vault.yml
│
└── production/
├── vars.yml
└── vault.yml
Chúng ta sẽ học sâu hơn ở Lab 13 — Multi-Environment.
19. Sử dụng Vault Variable
Giả sử:
group_vars/vault.yml
có:
postgres_password: "SuperSecret123"
Trong:
roles/application/templates/.env.j2
chúng ta có thể dùng:
POSTGRES_PASSWORD={{ postgres_password }}
Ansible tự động resolve variable.
Flow:
vault.yml
│
│ encrypted
▼
Ansible
│
│ decrypt
▼
postgres_password
│
▼
.env.j2
│
▼
.env
20. Docker Compose
Ví dụ:
services:
postgres:
image: postgres:16
environment:
POSTGRES_DB: "{{ postgres_db }}"
POSTGRES_USER: "{{ postgres_user }}"
POSTGRES_PASSWORD: "{{ postgres_password }}"
Ansible sẽ render:
postgres_password
↓
Jinja2
↓
Docker Compose
21. Một vấn đề quan trọng
Nếu chúng ta generate:
/opt/todo/compose.yml
và trong đó có:
POSTGRES_PASSWORD: "SuperSecret123"
thì secret đã xuất hiện plaintext trên server.
Điều này không nhất thiết sai, nhưng phải hiểu:
Ansible Vault chỉ bảo vệ secret khi lưu trữ trong Ansible project.
Nó không tự động biến mọi secret thành secret an toàn trên target server.
22. Secret trên Server
Ví dụ:
Ansible Controller
│
├── vault.yml
│ ↓ encrypted
│
└── deploy
↓
Server
│
└── /opt/todo/.env
↓
plaintext secret
Do đó phải bảo vệ:
/opt/todo/.env
Ví dụ:
mode: "0600"
và:
owner: "{{ deploy_user }}"
group: "{{ deploy_user }}"
23. .env Template
File:
roles/application/templates/.env.j2
APP_ENV={{ app_environment }}
DATABASE_HOST=postgres
DATABASE_PORT=5432
DATABASE_NAME={{ postgres_db }}
DATABASE_USER={{ postgres_user }}
DATABASE_PASSWORD={{ postgres_password }}
JWT_SECRET={{ jwt_secret }}
Task:
- name: Deploy application environment
ansible.builtin.template:
src: .env.j2
dest: "{{ application_dir }}/.env"
owner: "{{ deploy_user }}"
group: "{{ deploy_user }}"
mode: "0600"
24. no_log
Một vấn đề nguy hiểm:
- name: Create database
ansible.builtin.command:
cmd: >
create-db
--password {{ postgres_password }}
Nếu task fail hoặc log command:
password = SuperSecret123
có thể xuất hiện trong output.
Do đó:
- name: Create database
ansible.builtin.command:
cmd: >
create-db
--password {{ postgres_password }}
no_log: true
25. no_log là gì?
no_log: true
nói với Ansible:
Không hiển thị thông tin của task này trong output.
Ví dụ:
TASK [Create database]
changed: [server]
thay vì:
TASK [Create database]
cmd: create-db --password SuperSecret123
26. Cẩn thận với no_log
Không nên lạm dụng:
no_log: true
cho toàn bộ Playbook.
Nếu:
- hosts: all
no_log: true
thì debug cực kỳ khó.
Tốt hơn:
- name: Task containing secret
...
no_log: true
Chỉ che những task thực sự nhạy cảm.
27. Chạy Playbook với Vault
Chạy:
ansible-playbook site.yml --ask-vault-pass
Ansible hỏi:
Vault password:
Nhập password.
Sau đó:
Vault
↓
Decrypt
↓
Variables
↓
Templates
↓
Deployment
28. Vault Password File
Nếu không muốn nhập password mỗi lần:
echo 'my-vault-password' > .vault_pass
Sau đó:
chmod 600 .vault_pass
Chạy:
ansible-playbook site.yml \
--vault-password-file .vault_pass
29. Nhưng không commit .vault_pass
Đây là lỗi cực kỳ nguy hiểm.
Không được:
git add .vault_pass
Thêm:
.vault_pass
vào:
.gitignore
Ví dụ:
.vault_pass
30. Tốt hơn: Environment Variable
Có thể dùng:
export ANSIBLE_VAULT_PASSWORD_FILE=~/.ansible/vault_password
Sau đó:
ansible-playbook site.yml
Ansible sẽ sử dụng file password tương ứng.
Điểm quan trọng:
Vault encrypted file
+
Vault password
↓
Ansible
Cả hai đều cần được bảo vệ.
31. Tạo Vault Password File
Ví dụ:
mkdir -p ~/.ansible
echo 'my-vault-password' > ~/.ansible/vault_password
chmod 600 ~/.ansible/vault_password
Sau đó:
export ANSIBLE_VAULT_PASSWORD_FILE=~/.ansible/vault_password
Test:
ansible-vault view group_vars/vault.yml
32. Kiểm tra Deployment
Sau deployment:
ssh deploy@server
Kiểm tra:
ls -la /opt/todo/.env
Kỳ vọng:
-rw------- deploy deploy .env
Không nên:
-rw-r--r-- .env
vì user khác có thể đọc được.
33. Test Secret
Kiểm tra application:
docker compose ps
và:
curl http://localhost:8080/health
Application phải:
Running
+
Healthy
mà không cần expose password trong output.
34. Kiểm tra Vault
ansible-vault view group_vars/vault.yml
Nhập password.
Nếu đúng:
postgres_password
jwt_secret
được hiển thị.
Nếu sai:
Decryption failed
Đây là hành vi mong muốn.
35. Thực hành Rotate Password
Giả sử password hiện tại:
old-password
Thay bằng:
new-password
Chạy:
ansible-vault edit group_vars/vault.yml
Thay:
postgres_password: "old-password"
thành:
postgres_password: "new-password"
Sau đó:
ansible-playbook site.yml --ask-vault-pass
36. Secret Rotation
Trong production:
Old Secret
│
▼
Rotate
│
▼
New Secret
│
▼
Deploy
Secret rotation nên là một quy trình có chủ đích.
Không nên:
password thay đổi thủ công
↓
application chết
Mà:
Secret change
↓
Ansible
↓
Configuration
↓
Restart/Recreate
↓
Health Check
37. Tại sao không dùng Git để quản lý Secret?
Git có lịch sử:
Commit A
password = old
Commit B
password = new
Ngay cả khi bạn xóa password khỏi commit hiện tại:
git log
vẫn có thể tồn tại commit cũ.
Vì vậy:
Xóa secret khỏi file hiện tại không có nghĩa secret đã biến mất khỏi Git history.
Nếu secret đã bị commit và push:
hãy coi secret đó đã bị lộ và rotate nó.
38. Exercise 1 — Create Vault
Tạo:
group_vars/vault.yml
với:
postgres_password: "SuperSecret123"
jwt_secret: "my-super-jwt-secret"
Encrypt bằng:
ansible-vault create group_vars/vault.yml
Verify:
cat group_vars/vault.yml
Không được nhìn thấy plaintext.
39. Exercise 2 — Deploy PostgreSQL
Sử dụng:
postgres_password
trong:
compose.yml.j2
Deploy:
ansible-playbook site.yml --ask-vault-pass
Verify:
docker compose ps
40. Exercise 3 — Protect .env
Đảm bảo:
mode: "0600"
Kiểm tra:
ls -l /opt/todo/.env
Expected:
-rw------- ...
41. Exercise 4 — Hide Secret from Logs
Tạo một task có sử dụng:
postgres_password
Sau đó chạy Playbook.
Ban đầu có thể quan sát secret bị expose.
Sau đó thêm:
no_log: true
Chạy lại và so sánh.
Mục tiêu:
Hiểu tại sao Secret Management không chỉ là encryption.
42. Exercise 5 — Rotate Secret
Thay:
SuperSecret123
thành:
NewSecret456
Deploy lại.
Kiểm tra:
Application
↓
Database
↓
Connection
↓
Healthy
43. Exercise 6 — Wrong Vault Password
Chạy:
ansible-playbook site.yml --ask-vault-pass
nhập password sai.
Quan sát:
Decryption failed
Hiểu rằng:
Encrypted file
+
Wrong password
↓
Cannot decrypt
44. Exercise 7 — Vault Password File
Tạo:
~/.ansible/vault_password
với permission:
600
Sau đó:
export ANSIBLE_VAULT_PASSWORD_FILE=~/.ansible/vault_password
Chạy:
ansible-playbook site.yml
Không cần nhập password thủ công.
45. Exercise 8 — Secret Leak Challenge
Cố tình viết:
- name: Show password
ansible.builtin.debug:
msg: "{{ postgres_password }}"
Chạy Playbook.
Quan sát:
SuperSecret123
xuất hiện.
Sau đó xóa task.
Mục tiêu:
Hiểu rằng Vault không bảo vệ bạn khỏi chính Playbook của mình.
Vault chỉ encrypt dữ liệu lưu trữ.
46. Exercise 9 — Git Security
Chạy:
git status
Đảm bảo:
group_vars/vault.yml
được commit vì nó đã encrypted.
Nhưng:
.vault_pass
không được commit.
Kiểm tra:
git status
và:
git check-ignore .vault_pass
47. Exercise 10 — Production Secret Architecture
Thiết kế:
Git
│
├── application config
├── Ansible Roles
├── Playbooks
└── encrypted Vault
│
▼
Ansible
│
▼
Production
Yêu cầu:
- Không có plaintext password trong Git.
- Không có Vault password trong Git.
.envpermission0600.- Secret không xuất hiện trong logs.
- Application vẫn deploy được bằng một command.
48. Tư duy Senior #1 — Encryption ≠ Secret Management
Đây là một distinction rất quan trọng.
Ansible Vault giải quyết:
Secret at rest
Ví dụ:
Git
↓
vault.yml
↓
encrypted
Nhưng không tự giải quyết:
Secret rotation
Secret distribution
Secret auditing
Secret expiration
Access control
Secret injection
Đây là lý do production lớn thường sử dụng:
HashiCorp Vault
AWS Secrets Manager
AWS Parameter Store
Azure Key Vault
Google Secret Manager
Kubernetes Secrets + external secret systems
49. Tư duy Senior #2 — Vault Password cũng là Secret
Có:
vault.yml
được encrypt.
Nhưng nếu:
vault_password
nằm public:
GitHub
thì encryption gần như mất ý nghĩa.
Encrypted Vault
+
Public Password
↓
❌
Do đó:
Vault File
↓
Git
Vault Password
↓
Secure Environment / Secret Manager
50. Tư duy Senior #3 — Least Privilege
Không phải mọi server đều cần mọi secret.
Không nên:
all servers
↓
all secrets
Tốt hơn:
app-server
↓
application secrets
db-server
↓
database secrets
monitoring-server
↓
monitoring secrets
Đây là:
Principle of Least Privilege
51. Tư duy Senior #4 — Secret Rotation
Một secret tốt không phải là:
password rất phức tạp
mà còn phải:
Hard to guess
+
Limited access
+
Rotatable
+
Auditable
Ví dụ:
Secret A
↓
Rotate
↓
Secret B
↓
Rotate
↓
Secret C
52. Tư duy Senior #5 — Không log Secret
Các lệnh như:
echo $PASSWORD
hoặc:
debug:
var: password
không nên xuất hiện trong production automation.
Cần kiểm soát:
Ansible output
CI/CD logs
Docker logs
Application logs
Monitoring
Error messages
Secret có thể bị leak từ bất kỳ layer nào.
53. Tư duy Senior #6 — Secret và Artifact
Docker image:
todo-api:1.2.0
không nên chứa:
DATABASE_PASSWORD
API_KEY
JWT_SECRET
Không nên:
ENV DATABASE_PASSWORD=secret
và cũng không nên:
COPY .env .
Thay vào đó:
Docker Image
│
└── Application
Runtime
│
└── Secret
Tức là:
Build artifact không chứa environment-specific secrets.
54. Tư duy Senior #7 — Secret Injection
Kiến trúc tốt hơn:
Secret Manager
│
▼
Deployment System
│
▼
Application
Thay vì:
Git
│
└── plaintext secret
Ansible Vault là một bước rất tốt để người học hiểu nguyên lý này.
55. Ansible Vault có phù hợp Production không?
Có, đặc biệt với:
- Small team.
- Infrastructure automation.
- Ansible-centric environment.
- Secret số lượng vừa phải.
- Project không cần secret platform riêng.
Nhưng với hệ thống lớn:
Hundreds of servers
+
Many teams
+
Thousands of secrets
+
Rotation
+
Audit
+
Dynamic credentials
nên cân nhắc external secret manager.
56. Kiến trúc Secret Management
Mức cơ bản
Git
│
└── Ansible Vault
│
▼
Ansible
Mức production lớn
Secret Manager
│
┌───────────┴───────────┐
▼ ▼
CI/CD Ansible
│ │
└───────────┬───────────┘
▼
Application
57. Những lỗi người mới thường mắc
❌ Commit plaintext secret
password: "123456"
❌ Commit Vault password
.vault_pass
❌ Dùng debug với secret
debug:
var: postgres_password
❌ no_log toàn bộ Playbook
Debug cực kỳ khó.
❌ Secret nằm trong Docker image
ENV SECRET=...
❌ Permission .env quá rộng
644
❌ Không rotate secret sau khi bị leak
Nếu secret đã public:
Rotate ngay.
58. Production Checklist
Trước khi deploy:
Security
────────────────────────
✓ No plaintext secret in Git
✓ Vault file encrypted
✓ Vault password protected
✓ .vault_pass ignored
✓ .env permission 0600
✓ no_log where necessary
✓ No secret in Docker image
✓ No secret in debug output
Deployment:
✓ Vault decrypt successful
✓ Application starts
✓ Database connects
✓ Health check passes
✓ No secret appears in logs
59. Kết quả sau Lab 11
Sau Lab 10:
Application
↓
Ansible
↓
Production
Sau Lab 11:
┌───────────────┐
│ Git Repository│
└───────┬───────┘
│
┌───────┴───────┐
│ │
Configuration Vault Secret
│ │
│ Encrypted
│ │
└───────┬───────┘
▼
Ansible
│
▼
Production
│
┌──────┴──────┐
▼ ▼
Application Database
│
▼
Healthy
Người học lúc này đã đi từ:
Ansible Fundamentals
↓
Automation
↓
Server Provisioning
↓
Application Deployment
↓
Secret Management
và bắt đầu chạm tới tư duy Production-grade Infrastructure Automation.
60. Roadmap tiếp theo
Sau Lab 11, Lab 12 — Ansible Troubleshooting nên chuyển trọng tâm sang việc xử lý các tình huống deployment thực tế:
Lab 12
Ansible Troubleshooting
│
├── -vvv / -vvvv
├── Debug variables
├── Failed tasks
├── SSH problems
├── Permission problems
├── Template errors
├── Jinja2 errors
├── Docker errors
├── Network errors
├── Handler problems
├── Failed deployment
└── Recovery
Đây là một bước rất quan trọng nếu mục tiêu của cả series là đào tạo từ beginner lên Senior, vì ở level Senior, giá trị không nằm ở việc “viết được Playbook”, mà nằm ở khả năng đọc failure, tìm root cause, thiết kế automation có khả năng recover và đảm bảo hệ thống production không bị phá vỡ bởi chính automation.
All Rights Reserved