0

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, RolesApplication 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.
  • .env permission 0600.
  • 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 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

Viblo
Let's register a Viblo Account to get more interesting posts.