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

Bài 8: PKI Secrets Engine - Certificate Authority

PKI deep dive, Root CA, Intermediate CA, Certificate roles, Issue/Sign, CRL, OCSP, Auto-rotation, ACME, PKI certificate counter (1.21), cert-manager integration, mTLS.

🔒 DevSecOps — Bài 8 Bài 8: PKI Secrets Engine - Certificate Authority

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

Phần 2: Secrets Engines - Quản lý Bí mật

xdev.asia

Giới thiệu

PKI (Public Key Infrastructure) Secrets Engine biến Vault thành một Certificate Authority (CA) hoàn chỉnh, có khả năng tạo, ký, và quản lý X.509 certificates một cách tự động. Thay vì mua certificates từ bên thứ ba hoặc quản lý CA server riêng, Vault cung cấp PKI-as-a-Service với API đơn giản.

Tại sao cần PKI Secrets Engine?

  • Tự động hóa hoàn toàn: Issue certificates qua API/CLI, tích hợp CI/CD
  • Short-lived certificates: Giảm rủi ro khi certificate bị compromise
  • mTLS: Bảo mật service-to-service communication
  • Centralized management: Quản lý tất cả certificates từ một nơi
  • ACME support: Tương thích với Let's Encrypt protocol

1. Kiến trúc PKI — Root CA và Intermediate CA

1.1. Mô hình 2 tầng (khuyến nghị)

┌─────────────────────────────────────────┐
│            Root CA (Offline)             │ ← Vault PKI mount: pki/
│         Validity: 10-20 years           │
│    Chỉ dùng để ký Intermediate CA       │
└────────────────┬────────────────────────┘
                 │ Signs
                 ▼
┌─────────────────────────────────────────┐
│        Intermediate CA (Online)          │ ← Vault PKI mount: pki_int/
│         Validity: 3-5 years             │
│    Dùng để issue certificates           │
└────────────────┬────────────────────────┘
                 │ Issues
                 ▼
┌─────────────────────────────────────────┐
│         Leaf Certificates               │
│    Validity: 30 days - 1 year           │
│    TLS, mTLS, code signing, etc.        │
└─────────────────────────────────────────┘

Tại sao dùng 2 tầng?

  • Root CA private key được bảo vệ tốt hơn (ít khi sử dụng)
  • Nếu Intermediate CA bị compromise, chỉ cần revoke và tạo mới
  • Không ảnh hưởng đến Root CA trust chain

2. Thiết lập Root CA

2.1. Enable PKI cho Root CA

# Enable PKI engine cho Root CA
vault secrets enable -path=pki pki

# Cấu hình max lease TTL cho Root CA (20 năm)
vault secrets tune -max-lease-ttl=175200h pki

2.2. Generate Root Certificate

# Generate Root CA certificate
vault write -format=json pki/root/generate/internal \
  common_name="XDev Root Certificate Authority" \
  organization="XDev Asia" \
  ou="Infrastructure Security" \
  country="VN" \
  locality="Ho Chi Minh City" \
  issuer_name="root-2024" \
  ttl=175200h \
  key_type="rsa" \
  key_bits=4096 | tee /tmp/root-ca.json

# Extract Root CA certificate
jq -r '.data.certificate' /tmp/root-ca.json > /tmp/root-ca.pem

# Xem thông tin certificate
openssl x509 -in /tmp/root-ca.pem -text -noout | head -20

# Output:
# Certificate:
#     Data:
#         Version: 3 (0x2)
#         Serial Number: ...
#         Signature Algorithm: sha256WithRSAEncryption
#         Issuer: C = VN, L = Ho Chi Minh City, OU = Infrastructure Security, O = XDev Asia, CN = XDev Root Certificate Authority
#         Validity
#             Not Before: Jan 15 10:00:00 2024 GMT
#             Not After : Jan 15 10:00:00 2044 GMT

2.3. Cấu hình URLs cho Root CA

# Cấu hình CRL và Issuing URLs
vault write pki/config/urls \
  issuing_certificates="https://vault.xdev.asia/v1/pki/ca" \
  crl_distribution_points="https://vault.xdev.asia/v1/pki/crl" \
  ocsp_servers="https://vault.xdev.asia/v1/pki/ocsp"

3. Thiết lập Intermediate CA

3.1. Enable PKI cho Intermediate CA

# Enable PKI engine cho Intermediate CA
vault secrets enable -path=pki_int pki

# Cấu hình max lease TTL (5 năm)
vault secrets tune -max-lease-ttl=43800h pki_int

3.2. Generate CSR cho Intermediate CA

# Generate CSR (Certificate Signing Request)
vault write -format=json pki_int/intermediate/generate/internal \
  common_name="XDev Intermediate Certificate Authority" \
  organization="XDev Asia" \
  ou="Infrastructure Security" \
  country="VN" \
  issuer_name="intermediate-2024" \
  key_type="rsa" \
  key_bits=4096 | tee /tmp/intermediate-csr.json

# Extract CSR
jq -r '.data.csr' /tmp/intermediate-csr.json > /tmp/intermediate.csr

3.3. Sign Intermediate CSR bằng Root CA

# Root CA ký CSR của Intermediate CA
vault write -format=json pki/root/sign-intermediate \
  csr=@/tmp/intermediate.csr \
  format=pem_bundle \
  ttl=43800h | tee /tmp/intermediate-cert.json

# Extract signed certificate
jq -r '.data.certificate' /tmp/intermediate-cert.json > /tmp/intermediate.pem

3.4. Import signed certificate vào Intermediate CA

# Set signed certificate cho Intermediate CA
vault write pki_int/intermediate/set-signed \
  certificate=@/tmp/intermediate.pem

3.5. Cấu hình URLs cho Intermediate CA

vault write pki_int/config/urls \
  issuing_certificates="https://vault.xdev.asia/v1/pki_int/ca" \
  crl_distribution_points="https://vault.xdev.asia/v1/pki_int/crl" \
  ocsp_servers="https://vault.xdev.asia/v1/pki_int/ocsp"

4. Certificate Roles

Roles định nghĩa template cho certificates được issue. Mỗi role quy định domain patterns, TTL, key types, và các extensions cho phép.

4.1. Role cho Web Server TLS

vault write pki_int/roles/web-server \
  allowed_domains="xdev.asia,internal.xdev.asia" \
  allow_subdomains=true \
  allow_bare_domains=false \
  allow_wildcard_certificates=true \
  max_ttl="2160h" \
  ttl="720h" \
  key_type="rsa" \
  key_bits=2048 \
  key_usage="DigitalSignature,KeyEncipherment" \
  ext_key_usage="ServerAuth" \
  organization="XDev Asia" \
  country="VN" \
  require_cn=true \
  server_flag=true \
  client_flag=false

4.2. Role cho mTLS Client

vault write pki_int/roles/mtls-client \
  allowed_domains="services.internal" \
  allow_subdomains=true \
  max_ttl="720h" \
  ttl="168h" \
  key_type="ec" \
  key_bits=256 \
  key_usage="DigitalSignature" \
  ext_key_usage="ClientAuth" \
  require_cn=true \
  server_flag=false \
  client_flag=true \
  no_store=true

4.3. Role cho mTLS (cả Server và Client)

vault write pki_int/roles/mtls-service \
  allowed_domains="services.internal" \
  allow_subdomains=true \
  allow_ip_sans=true \
  allowed_other_sans="" \
  max_ttl="720h" \
  ttl="168h" \
  key_type="ec" \
  key_bits=256 \
  key_usage="DigitalSignature,KeyEncipherment" \
  ext_key_usage="ServerAuth,ClientAuth" \
  require_cn=true \
  server_flag=true \
  client_flag=true

4.4. Role cho Internal Services (short-lived)

vault write pki_int/roles/internal-service \
  allowed_domains="svc.cluster.local,internal.xdev.asia" \
  allow_subdomains=true \
  allow_ip_sans=true \
  max_ttl="72h" \
  ttl="24h" \
  key_type="ec" \
  key_bits=256 \
  generate_lease=true \
  no_store=false

5. Issue Certificates

5.1. Issue certificate cho web server

# Issue certificate
vault write -format=json pki_int/issue/web-server \
  common_name="api.xdev.asia" \
  alt_names="api-v2.xdev.asia,api-internal.xdev.asia" \
  ip_sans="10.0.1.100" \
  ttl="720h" | tee /tmp/api-cert.json

# Extract certificate components
jq -r '.data.certificate' /tmp/api-cert.json > /tmp/api.crt
jq -r '.data.private_key' /tmp/api-cert.json > /tmp/api.key
jq -r '.data.ca_chain[]' /tmp/api-cert.json > /tmp/ca-chain.pem
jq -r '.data.issuing_ca' /tmp/api-cert.json > /tmp/issuing-ca.pem

# Xem thông tin certificate
openssl x509 -in /tmp/api.crt -text -noout

# Verify certificate chain
openssl verify -CAfile /tmp/ca-chain.pem /tmp/api.crt
# /tmp/api.crt: OK

5.2. Issue wildcard certificate

vault write -format=json pki_int/issue/web-server \
  common_name="*.xdev.asia" \
  ttl="720h" | tee /tmp/wildcard-cert.json

5.3. Issue certificate cho mTLS service

vault write -format=json pki_int/issue/mtls-service \
  common_name="payment-service.services.internal" \
  ip_sans="10.0.2.50" \
  ttl="168h" | tee /tmp/payment-svc-cert.json

# Extract cho service sử dụng
jq -r '.data.certificate' /tmp/payment-svc-cert.json > /etc/tls/tls.crt
jq -r '.data.private_key' /tmp/payment-svc-cert.json > /etc/tls/tls.key
jq -r '.data.ca_chain[]' /tmp/payment-svc-cert.json > /etc/tls/ca.crt

6. Sign CSR — Ký Certificate Request bên ngoài

Khi application tự generate private key và gửi CSR để ký:

# Application tạo private key và CSR
openssl req -new -newkey ec -pkeyopt ec_paramgen_curve:prime256v1 \
  -keyout /tmp/app.key -out /tmp/app.csr -nodes \
  -subj "/CN=myapp.services.internal/O=XDev Asia"

# Vault ký CSR
vault write -format=json pki_int/sign/internal-service \
  csr=@/tmp/app.csr \
  common_name="myapp.services.internal" \
  ttl="24h" | tee /tmp/signed-cert.json

# Extract signed certificate
jq -r '.data.certificate' /tmp/signed-cert.json > /tmp/app.crt

7. CRL (Certificate Revocation List)

7.1. Cấu hình CRL

# Cấu hình CRL settings
vault write pki_int/config/crl \
  expiry="72h" \
  disable=false \
  auto_rebuild=true \
  auto_rebuild_grace_period="12h" \
  enable_delta=true \
  delta_rebuild_interval="15m"

7.2. Revoke Certificate

# Revoke bằng serial number
vault write pki_int/revoke \
  serial_number="39:dd:2e:90:b7:23:1f:8d:d3:7d:31:c5:1b:da:84:d0:5b:65:31:58"

# Revoke bằng certificate PEM
vault write pki_int/revoke \
  certificate=@/tmp/api.crt

# Xem CRL hiện tại
curl -s "$VAULT_ADDR/v1/pki_int/crl" | openssl crl -inform DER -text -noout

7.3. Tidy — Cleanup expired certificates

# Tidy certificates database
vault write pki_int/tidy \
  tidy_cert_store=true \
  tidy_revoked_certs=true \
  tidy_revoked_cert_issuer_associations=true \
  tidy_expired_issuers=true \
  safety_buffer="72h" \
  issuer_safety_buffer="720h"

# Kiểm tra trạng thái tidy
vault read pki_int/tidy-status

8. OCSP (Online Certificate Status Protocol)

OCSP cho phép clients kiểm tra trạng thái revocation của certificate theo real-time.

8.1. Enable OCSP

# OCSP đã được enable mặc định khi cấu hình ocsp_servers URL
# Kiểm tra OCSP response
openssl ocsp \
  -issuer /tmp/issuing-ca.pem \
  -cert /tmp/api.crt \
  -url "$VAULT_ADDR/v1/pki_int/ocsp" \
  -resp_text

# Output:
# Response Status: successful (0x0)
# ...
# Cert Status: good
# This Update: Jan 15 12:00:00 2024 GMT
# Next Update: Jan 16 12:00:00 2024 GMT

9. ACME Protocol Support

Từ Vault 1.14+, PKI engine hỗ trợ ACME protocol — cùng protocol mà Let's Encrypt sử dụng. Điều này cho phép tích hợp với các ACME clients như certbot, cert-manager, etc.

9.1. Enable ACME

# Cấu hình cluster path (bắt buộc cho ACME)
vault write pki_int/config/cluster \
  path="https://vault.xdev.asia/v1/pki_int" \
  aia_path="https://vault.xdev.asia/v1/pki_int"

# Enable ACME
vault write pki_int/config/acme \
  enabled=true \
  allowed_roles="web-server,internal-service" \
  allow_role_ext_key_usage=true \
  default_directory_policy="role:web-server"

9.2. Sử dụng certbot với Vault ACME

# Sử dụng certbot để request certificate từ Vault
certbot certonly \
  --server "https://vault.xdev.asia/v1/pki_int/acme/directory" \
  --standalone \
  --non-interactive \
  --agree-tos \
  --email [email protected] \
  -d "api.xdev.asia"

10. PKI Certificate Counter (Vault 1.21+)

Vault 1.21 thêm tính năng đếm certificates đã issue, giúp monitoring và compliance.

# Đọc certificate count
vault read pki_int/certificates/count

# Output:
# Key      Value
# ---      -----
# count    1547

# List certificates (serial numbers)
vault list pki_int/certs

# Đọc thông tin certificate cụ thể
vault read pki_int/cert/<serial-number>

11. Tích hợp cert-manager (Kubernetes)

11.1. Cài đặt cert-manager Vault Issuer

# vault-issuer.yaml
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
  name: vault-issuer
spec:
  vault:
    server: https://vault.xdev.asia
    path: pki_int/sign/internal-service
    auth:
      kubernetes:
        role: cert-manager
        mountPath: /v1/auth/kubernetes
        serviceAccountRef:
          name: cert-manager-vault

11.2. Request certificate qua cert-manager

# certificate.yaml
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
  name: api-tls
  namespace: default
spec:
  secretName: api-tls-secret
  duration: 720h
  renewBefore: 168h
  issuerRef:
    name: vault-issuer
    kind: ClusterIssuer
  commonName: api.services.internal
  dnsNames:
    - api.services.internal
    - api.default.svc.cluster.local
  ipAddresses:
    - 10.0.2.100
  privateKey:
    algorithm: ECDSA
    size: 256

11.3. Sử dụng trong Pod

# deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: api-server
spec:
  template:
    spec:
      containers:
        - name: api
          image: myapp/api:latest
          volumeMounts:
            - name: tls
              mountPath: /etc/tls
              readOnly: true
          env:
            - name: TLS_CERT
              value: /etc/tls/tls.crt
            - name: TLS_KEY
              value: /etc/tls/tls.key
            - name: CA_CERT
              value: /etc/tls/ca.crt
      volumes:
        - name: tls
          secret:
            secretName: api-tls-secret

12. Thiết lập mTLS hoàn chỉnh

12.1. Issue Server Certificate

# Server certificate
vault write -format=json pki_int/issue/mtls-service \
  common_name="server.services.internal" \
  ttl="168h" > /tmp/server-cert.json

jq -r '.data.certificate' /tmp/server-cert.json > /etc/tls/server.crt
jq -r '.data.private_key' /tmp/server-cert.json > /etc/tls/server.key
jq -r '.data.ca_chain[]' /tmp/server-cert.json > /etc/tls/ca-bundle.crt

12.2. Issue Client Certificate

# Client certificate
vault write -format=json pki_int/issue/mtls-client \
  common_name="client-app.services.internal" \
  ttl="168h" > /tmp/client-cert.json

jq -r '.data.certificate' /tmp/client-cert.json > /etc/tls/client.crt
jq -r '.data.private_key' /tmp/client-cert.json > /etc/tls/client.key

12.3. Test mTLS với curl

# Server cần verify client certificate
# Client gửi request với certificate
curl --cacert /etc/tls/ca-bundle.crt \
  --cert /etc/tls/client.crt \
  --key /etc/tls/client.key \
  https://server.services.internal:8443/api/health

12.4. Nginx mTLS Configuration

server {
    listen 8443 ssl;
    server_name server.services.internal;

    # Server certificate
    ssl_certificate     /etc/tls/server.crt;
    ssl_certificate_key /etc/tls/server.key;

    # mTLS — require client certificate
    ssl_client_certificate /etc/tls/ca-bundle.crt;
    ssl_verify_client on;
    ssl_verify_depth 2;

    # TLS settings
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ciphers HIGH:!aNULL:!MD5;
    ssl_prefer_server_ciphers on;

    location / {
        proxy_pass http://backend:8080;
        proxy_set_header X-Client-CN $ssl_client_s_dn_cn;
    }
}

13. Auto-Rotation Script

#!/bin/bash
# cert-renew.sh — Tự động renew certificate trước khi hết hạn

set -euo pipefail

CERT_PATH="/etc/tls/server.crt"
KEY_PATH="/etc/tls/server.key"
CA_PATH="/etc/tls/ca-bundle.crt"
ROLE="web-server"
CN="api.xdev.asia"
RENEW_BEFORE_DAYS=7

# Kiểm tra certificate còn bao nhiêu ngày
if [ -f "$CERT_PATH" ]; then
  EXPIRY=$(openssl x509 -in "$CERT_PATH" -enddate -noout | cut -d= -f2)
  EXPIRY_EPOCH=$(date -d "$EXPIRY" +%s 2>/dev/null || date -j -f "%b %d %T %Y %Z" "$EXPIRY" +%s)
  NOW_EPOCH=$(date +%s)
  DAYS_LEFT=$(( (EXPIRY_EPOCH - NOW_EPOCH) / 86400 ))
  
  echo "Certificate expires in $DAYS_LEFT days"
  
  if [ "$DAYS_LEFT" -gt "$RENEW_BEFORE_DAYS" ]; then
    echo "Certificate still valid. No renewal needed."
    exit 0
  fi
fi

echo "Renewing certificate..."

# Issue new certificate
RESULT=$(vault write -format=json "pki_int/issue/${ROLE}" \
  common_name="$CN" \
  ttl="720h")

echo "$RESULT" | jq -r '.data.certificate' > "$CERT_PATH"
echo "$RESULT" | jq -r '.data.private_key' > "$KEY_PATH"
echo "$RESULT" | jq -r '.data.ca_chain[]' > "$CA_PATH"

chmod 644 "$CERT_PATH" "$CA_PATH"
chmod 600 "$KEY_PATH"

echo "Certificate renewed successfully!"

# Reload service (nginx example)
nginx -s reload 2>/dev/null || true

14. Policies cho PKI

# Policy cho application: chỉ issue certificates
path "pki_int/issue/web-server" {
  capabilities = ["create", "update"]
}

path "pki_int/sign/web-server" {
  capabilities = ["create", "update"]
}

# Policy cho cert-manager
path "pki_int/sign/internal-service" {
  capabilities = ["create", "update"]
}

# Policy cho PKI admin
path "pki_int/*" {
  capabilities = ["create", "read", "update", "delete", "list"]
}

path "pki/*" {
  capabilities = ["create", "read", "update", "delete", "list"]
}

# Read CA certificates (public)
path "pki/ca/pem" {
  capabilities = ["read"]
}

path "pki_int/ca/pem" {
  capabilities = ["read"]
}

15. Best Practices

15.1. Certificate Lifecycle

  • ✅ Root CA TTL: 10-20 năm, offline storage
  • ✅ Intermediate CA TTL: 3-5 năm
  • ✅ Leaf certificates TTL: 30 ngày - 1 năm (càng ngắn càng tốt)
  • ✅ Renew certificates khi còn 1/3 thời gian sống
  • ✅ Sử dụng ECDSA P-256 cho leaf certs (nhanh hơn RSA)

15.2. Security

  • ✅ Không export Root CA private key
  • ✅ Separate PKI mounts cho Root và Intermediate
  • ✅ Enable CRL và OCSP
  • ✅ Regular tidy operations
  • ✅ Monitor certificate count và expiration
  • ✅ Restrict role permissions theo team/service
  • ✅ Sử dụng no_store=true cho high-volume issuance

Tổng kết

Trong bài học này, bạn đã thiết lập một PKI infrastructure hoàn chỉnh với Vault:

  1. Root CA + Intermediate CA — kiến trúc 2 tầng chuẩn
  2. Certificate Roles — templates cho web server, mTLS, internal services
  3. Issue và Sign certificates — tự động qua CLI/API
  4. CRL và OCSP — certificate revocation
  5. ACME support — tương thích Let's Encrypt protocol
  6. cert-manager integration — tự động PKI trong Kubernetes
  7. mTLS — bảo mật service-to-service
  8. Auto-rotation — tự động renew certificates

Bài tiếp theo sẽ khám phá Transit Secrets Engine — Encryption as a Service, nơi Vault giúp bạn mã hóa dữ liệu mà không cần quản lý encryption keys.