Chuyển đến nội dung chính

Bài 22: Vault với Terraform, Ansible và CI/CD Pipelines

Terraform Vault Provider, Ansible Vault lookup, CI/CD integration, GitHub Actions OIDC, GitLab CI JWT, Jenkins, ArgoCD Vault Plugin, External Secrets Operator.

🔒 DevSecOps — Bài 22 Bài 22: Vault với Terraform, Ansible và CI/CD Pipelines

HashiCorp Vault từ Cơ bản đến Nâng cao

Phần 6: Tích hợp ứng dụng thực tế

xdev.asia

1. Terraform Vault Provider

Terraform Vault Provider cho phép quản lý cấu hình Vault as Code — secrets engines, auth methods, policies, entities, groups, tất cả đều có thể version-controlled trong Git.

Cấu hình Provider

# providers.tf
terraform {
  required_providers {
    vault = {
      source  = "hashicorp/vault"
      version = "~> 4.0"
    }
  }
}

provider "vault" {
  address = "https://vault.company.com:8200"
  # Auth qua VAULT_TOKEN env var hoặc:
  # auth_login {
  #   path = "auth/approle/login"
  #   parameters = {
  #     role_id   = var.vault_role_id
  #     secret_id = var.vault_secret_id
  #   }
  # }
}

Quản lý Secrets Engines

# secrets-engines.tf

# KV v2
resource "vault_mount" "kv" {
  path        = "secret"
  type        = "kv-v2"
  description = "KV v2 secrets engine"
  options = {
    version = "2"
    max_versions = 10
  }
}

# Database secrets engine
resource "vault_mount" "database" {
  path = "database"
  type = "database"
}

resource "vault_database_secret_backend_connection" "postgres" {
  backend       = vault_mount.database.path
  name          = "production-postgres"
  allowed_roles = ["app-role", "readonly-role"]

  postgresql {
    connection_url = "postgresql://{{username}}:{{password}}@db.company.com:5432/production"
    username       = var.db_admin_user
    password       = var.db_admin_password
  }
}

resource "vault_database_secret_backend_role" "app_role" {
  backend             = vault_mount.database.path
  name                = "app-role"
  db_name             = vault_database_secret_backend_connection.postgres.name
  default_ttl         = 3600
  max_ttl             = 14400
  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}}\";",
  ]
  revocation_statements = [
    "REVOKE ALL PRIVILEGES ON ALL TABLES IN SCHEMA public FROM \"{{name}}\";",
    "DROP ROLE IF EXISTS \"{{name}}\";",
  ]
}

# PKI
resource "vault_mount" "pki" {
  path                  = "pki"
  type                  = "pki"
  max_lease_ttl_seconds = 315360000  # 10 years
}

resource "vault_pki_secret_backend_root_cert" "root" {
  backend     = vault_mount.pki.path
  type        = "internal"
  common_name = "Company Root CA"
  ttl         = "315360000"
}

Quản lý Auth Methods

# auth.tf

# Kubernetes auth
resource "vault_auth_backend" "kubernetes" {
  type = "kubernetes"
  path = "kubernetes"
}

resource "vault_kubernetes_auth_backend_config" "config" {
  backend            = vault_auth_backend.kubernetes.path
  kubernetes_host    = "https://kubernetes.default.svc:443"
}

resource "vault_kubernetes_auth_backend_role" "webapp" {
  backend                          = vault_auth_backend.kubernetes.path
  role_name                        = "webapp"
  bound_service_account_names      = ["webapp-sa"]
  bound_service_account_namespaces = ["production"]
  token_policies                   = ["webapp-policy"]
  token_ttl                        = 3600
}

# AppRole
resource "vault_auth_backend" "approle" {
  type = "approle"
}

resource "vault_approle_auth_backend_role" "cicd" {
  backend        = vault_auth_backend.approle.path
  role_name      = "cicd-pipeline"
  token_policies = ["cicd-deploy"]
  token_ttl      = 1800
  secret_id_ttl  = 600
  secret_id_num_uses = 1
}

Quản lý Policies

# policies.tf

resource "vault_policy" "webapp" {
  name   = "webapp-policy"
  policy = file("policies/webapp.hcl")
}

resource "vault_policy" "cicd" {
  name = "cicd-deploy"
  policy = <<EOT
path "secret/data/production/*" {
  capabilities = ["read"]
}
path "database/creds/app-role" {
  capabilities = ["read"]
}
EOT
}

Đọc secrets từ Vault trong Terraform

# Đọc secret để dùng trong Terraform resources
data "vault_kv_secret_v2" "db_creds" {
  mount = "secret"
  name  = "production/db"
}

resource "aws_db_instance" "production" {
  engine   = "postgres"
  username = data.vault_kv_secret_v2.db_creds.data["username"]
  password = data.vault_kv_secret_v2.db_creds.data["password"]
  # ... other config
}

2. Ansible với Vault

Lookup Plugin

# playbook.yml
- name: Deploy application
  hosts: webservers
  vars:
    db_password: "{{ lookup('hashi_vault', 'secret/data/production/db:password') }}"
    api_key: "{{ lookup('hashi_vault', 'secret/data/production/api:key') }}"

  tasks:
    - name: Configure application
      template:
        src: app.conf.j2
        dest: /etc/app/app.conf
        mode: '0640'
      vars:
        db_host: "{{ lookup('hashi_vault', 'secret/data/production/db:host') }}"

    - name: Get dynamic database credentials
      set_fact:
        db_creds: "{{ lookup('hashi_vault', 'database/creds/deploy-role') }}"

    - name: Run database migration
      command: ./migrate.sh
      environment:
        DB_USER: "{{ db_creds.username }}"
        DB_PASS: "{{ db_creds.password }}"

Ansible Collection

# Install collection
ansible-galaxy collection install community.hashi_vault
- name: Read KV secret
  community.hashi_vault.vault_kv2_get:
    url: https://vault.company.com:8200
    engine_mount_point: secret
    path: production/db
    auth_method: approle
    role_id: "{{ vault_role_id }}"
    secret_id: "{{ vault_secret_id }}"
  register: db_secret

- name: Write KV secret
  community.hashi_vault.vault_kv2_write:
    url: https://vault.company.com:8200
    engine_mount_point: secret
    path: production/app/deploy-info
    data:
      version: "{{ app_version }}"
      deployed_by: "{{ ansible_user_id }}"
      deployed_at: "{{ ansible_date_time.iso8601 }}"

3. CI/CD Integration

GitHub Actions (OIDC — Recommended)

# .github/workflows/deploy.yml
name: Deploy
on:
  push:
    branches: [main]

permissions:
  id-token: write
  contents: read

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Get secrets from Vault
        uses: hashicorp/vault-action@v3
        with:
          url: https://vault.company.com:8200
          method: jwt
          path: github-actions
          role: my-app
          jwtGithubAudience: https://github.com/my-org
          exportEnv: true
          secrets: |
            secret/data/production/db username | DB_USER ;
            secret/data/production/db password | DB_PASS ;
            secret/data/production/registry token | REGISTRY_TOKEN

      - name: Build and push
        run: |
          echo "$REGISTRY_TOKEN" | docker login -u _token --password-stdin registry.company.com
          docker build -t registry.company.com/myapp:${{ github.sha }} .
          docker push registry.company.com/myapp:${{ github.sha }}

ArgoCD Vault Plugin

# ArgoCD Application manifest
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: webapp
spec:
  source:
    repoURL: https://github.com/company/webapp
    path: k8s
    plugin:
      name: argocd-vault-plugin
      env:
        - name: AVP_TYPE
          value: vault
        - name: AVP_AUTH_TYPE
          value: k8s
        - name: VAULT_ADDR
          value: https://vault.company.com:8200
        - name: AVP_K8S_ROLE
          value: argocd
# k8s/secret.yaml (template)
apiVersion: v1
kind: Secret
metadata:
  name: app-secrets
  annotations:
    avp.kubernetes.io/path: "secret/data/production/webapp"
type: Opaque
stringData:
  DB_PASSWORD: <password>
  API_KEY: <api_key>

External Secrets Operator

# ClusterSecretStore
apiVersion: external-secrets.io/v1beta1
kind: ClusterSecretStore
metadata:
  name: vault-store
spec:
  provider:
    vault:
      server: "https://vault.company.com:8200"
      path: "secret"
      version: "v2"
      auth:
        kubernetes:
          mountPath: "kubernetes"
          role: "external-secrets"
          serviceAccountRef:
            name: external-secrets-sa

---
# ExternalSecret
apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
  name: app-db-secret
  namespace: production
spec:
  refreshInterval: 1m
  secretStoreRef:
    kind: ClusterSecretStore
    name: vault-store
  target:
    name: app-db-credentials
    creationPolicy: Owner
  data:
    - secretKey: username
      remoteRef:
        key: production/db
        property: username
    - secretKey: password
      remoteRef:
        key: production/db
        property: password

4. Vault trong GitOps Workflow

┌─────────────┐   Push config    ┌──────────────┐
│  Developer  │ ───────────────▶ │     Git      │
│             │                  │  (manifests  │
└─────────────┘                  │   + secret   │
                                 │   placeholders│
                                 └──────┬───────┘
                                        │
                                   ArgoCD / Flux
                                        │
                                        ▼
                                 ┌──────────────┐
                                 │  AVP / ESO   │
                                 │  (resolve    │
                                 │   secrets)   │
                                 └──────┬───────┘
                                        │
                           Fetch secrets │
                                        ▼
                                 ┌──────────────┐
                                 │    Vault     │
                                 │              │
                                 └──────────────┘

5. Tổng kết

  • Terraform Vault Provider — Vault configuration as code, quản lý toàn bộ Vault setup

  • Ansible — lookup plugin và collection cho operational workflows

  • GitHub Actions — OIDC-based (no static secrets), vault-action

  • ArgoCD Vault Plugin — resolve secret placeholders trong GitOps

  • External Secrets Operator — Kubernetes-native sync từ Vault

Phần cuối cùng sẽ đi vào Production & Enterprise — HA, Integrated Storage, Replication, Namespaces, Monitoring và Troubleshooting.