CFSSL trong production — Cloudflare's PKI toolkit cho internal CA

Tự host internal CA bằng CFSSL: cfssl init, intermediate CA, OCSP responder, multirootca, CI short-lived certs. So sánh AWS Private CA $400/tháng.

· 8 phút đọc

TL;DR

  • CFSSL là PKI toolkit viết bằng Go mà Cloudflare dùng nội bộ — cfssl, cfssljson, multirootca, mkbundle, ocspserve. Đủ thay AWS Private CA cho 90% workload internal.
  • AWS Private CA $400/tháng/CA flat — chỉ ROI khi bạn có ≥ 10 service và dùng short-lived cert (< 7 ngày). Dưới ngưỡng đó, CFSSL self-host trên 2 VM nhỏ + KMS thắng.
  • Hierarchy chuẩn: Root CA offline (RSA 4096, 10-20 năm) → Intermediate CA online (ECDSA P-256, 1-5 năm) → leaf cert short-lived (24h-7d). Đừng ký leaf trực tiếp từ root.
  • CFSSL không có database — config-file driven. Pair với HashiCorp Vault hoặc tự build state nếu cần revocation track ngoài CRL/OCSP.
  • multirootca cho phép một CA daemon serve nhiều profile (server cert, client cert, code signing) với policy khác nhau — không cần spin nhiều process.
  • CI/CD pattern: pipeline xin cert qua cfssl sign API, mount vào container, expire trong 24h. Không có cert nào sống đủ lâu để bị leak nguy hiểm.
  • Đừng dùng CFSSL làm public-facing CA. Nó là internal toolkit — không có CT log integration, không qua CA/Browser Forum audit.

Vì sao tôi không trả $400/tháng cho AWS Private CA

Câu chuyện bắt đầu khi team Platform có yêu cầu cấp cert cho 6 service nội bộ — service mesh mTLS, internal API gateway, vài admin dashboard. Đầu tiên reach cho AWS Private CA, mở console, thấy con số $400.00/month per CA (AWS PCA pricing). Đó là flat fee, không phụ thuộc số cert. Mỗi cert > 13 tháng còn thêm $0.75 — cert short-lived (< 7 ngày) miễn phí, nhưng vẫn đóng $400 sàn.

Với 6 service, đó là $66/service/tháng chỉ để có CA. Đem so với:

  • 2 VM t3.small chạy CFSSL ($30/tháng tổng)
  • Một KMS key cho root key ($1/tháng + API call)
  • Backup/IaC overhead

Tổng dưới $40/tháng. CFSSL không phải “phiên bản miễn phí” — nó là binary mà Cloudflare dùng nội bộ ký cert cho infrastructure của họ, được open-source từ 2014. Khi cùng một codebase đang ký cert cho data plane của Cloudflare thì khả năng nó production-ready không phải câu hỏi.

Quyết định: tự host CFSSL cho 6 service. Sau 8 tháng vận hành, viết bài này.

CFSSL có những gì — không phải một binary, là một toolkit

Khi git clone https://github.com/cloudflare/cfssl rồi make, bạn nhận về một thư mục bin/ với 7 binary:

BinaryMục đích
cfsslCLI chính: init CA, sign cert, gencert, info
cfssljsonParse JSON output của cfssl thành file .pem/.csr riêng
multirootcaLong-running daemon serve nhiều CA profile qua HTTP API
mkbundleBuild intermediate cert bundle cho client verify
cfssl-bundleTạo cert chain hoàn chỉnh từ leaf cert
cfssl-certinfoInspect cert (alternative cho openssl x509 -text)
cfssl-newkeyGenerate key + CSR theo profile

Cái mà người ta hay nhầm: cfssl là CLI một-shot, còn multirootca mới là daemon production. Tôi từng thấy team chạy cfssl serve trong production rồi than về thiếu HA — cfssl serve chỉ là dev server, không có multi-profile, không thay key on-the-fly, không có HSM integration.

Hierarchy: root offline, intermediate online, leaf ephemeral

Bài học từ RFC 5280 và mọi CA architecture từng tồn tại: không ký leaf trực tiếp từ root. Lý do đơn giản — nếu root key compromise, bạn phải re-issue mọi cert từng được issued. Với intermediate, bạn revoke intermediate, root vẫn an toàn vì offline.

Setup tôi dùng:

// root-csr.json
{
  "CN": "ExampleCorp Root CA",
  "key": { "algo": "rsa", "size": 4096 },
  "names": [
    { "C": "VN", "O": "ExampleCorp", "OU": "PKI" }
  ],
  "ca": { "expiry": "175200h" }   // 20 năm
}
# Khởi tạo root CA — chạy MỘT LẦN trên airgapped machine
cfssl gencert -initca root-csr.json | cfssljson -bare root-ca

# Output: root-ca.pem, root-ca-key.pem, root-ca.csr
# root-ca-key.pem → đưa vào HSM hoặc YubiHSM, KHÔNG để trên disk

Sau khi có root, sinh intermediate CSR rồi ký bằng root (offline):

// intermediate-csr.json
{
  "CN": "ExampleCorp Issuing CA G1",
  "key": { "algo": "ecdsa", "curve": "P-256" },
  "names": [
    { "C": "VN", "O": "ExampleCorp", "OU": "PKI Issuing" }
  ],
  "ca": { "expiry": "43800h" }    // 5 năm
}
# Trên máy có root key (offline, một lần)
cfssl gencert -initca intermediate-csr.json | cfssljson -bare intermediate

# Ký CSR bằng root
cfssl sign \
  -ca root-ca.pem -ca-key root-ca-key.pem \
  -config ca-config.json -profile intermediate \
  intermediate.csr | cfssljson -bare intermediate

ca-config.json là nơi định nghĩa policy cho từng loại cert:

{
  "signing": {
    "default": { "expiry": "8760h" },
    "profiles": {
      "intermediate": {
        "usages": ["cert sign", "crl sign"],
        "expiry": "43800h",
        "ca_constraint": { "is_ca": true, "max_path_len": 0 }
      },
      "server": {
        "usages": ["signing", "key encipherment", "server auth"],
        "expiry": "168h"
      },
      "client": {
        "usages": ["signing", "key encipherment", "client auth"],
        "expiry": "24h"
      },
      "peer": {
        "usages": ["signing", "key encipherment", "server auth", "client auth"],
        "expiry": "168h"
      }
    }
  }
}

max_path_len: 0 là quan trọng — nó nói intermediate này không được ký intermediate khác. Một path length attack đơn giản: nếu intermediate compromise, attacker không thể tạo sub-CA để fan-out cert.

ECDSA P-256 cho intermediate, không phải RSA

Mọi tutorial CFSSL trên internet dùng RSA 2048 cho mọi tầng. Đừng làm vậy. ECDSA P-256 nhanh hơn RSA 2048 khoảng 10× cho signing operation (intermediate sẽ ký cert mỗi vài giây trong CI pipeline busy), key size 32 bytes vs 256 bytes, và security level tương đương RSA 3072. RSA chỉ giữ ở root vì tương thích lâu năm — root sống 20 năm, ECDSA cũng được hỗ trợ rộng rãi nhưng RSA an toàn hơn nếu có ngày nào curve P-256 bị attack mới.

multirootca — daemon thực sự cho production

Khi đã có intermediate, đưa nó vào multirootca daemon. Đây là binary mà thường bị bỏ qua trong tutorial:

// multiroot.conf
[ servers ]
production-server = ./profiles/server.json
production-client = ./profiles/client.json
ci-ephemeral     = ./profiles/ci.json
# profiles/server.json
{
  "private": "file://./intermediate-key.pem",
  "certificate": "./intermediate.pem",
  "config": {
    "signing": {
      "default": {
        "usages": ["signing", "key encipherment", "server auth"],
        "expiry": "168h"
      }
    }
  },
  "nets": ["10.0.0.0/8"],
  "auth_keys": {
    "platform-service": {
      "type": "standard",
      "key": "env:CFSSL_AUTH_KEY_PLATFORM"
    }
  }
}

Chạy:

multirootca \
  -roots multiroot.conf \
  -tls-cert /etc/cfssl/server.pem \
  -tls-key /etc/cfssl/server-key.pem \
  -a :8888

Ba thứ đáng chú ý ở config trên:

  1. auth_keys — HMAC pre-shared key. Caller phải HMAC request body bằng key này; CA verify trước khi ký. Không có auth = ai cũng ký được cert.
  2. nets — IP allowlist tại CA level. Defense-in-depth nếu auth key leak.
  3. private: "file://..." — CFSSL hỗ trợ pkcs11: URL cho HSM. Nếu nghiêm túc thì intermediate key phải ở HSM (YubiHSM2 ~$650, hoặc AWS CloudHSM nếu trong AWS).

CI/CD pipeline: short-lived cert mỗi build

Đây là pattern thay đổi cách suy nghĩ về cert management. Thay vì rotate cert thủ công, mỗi pipeline run xin cert mới, sống đúng đủ thời gian build + deploy + chạy:

# .github/workflows/deploy.yml (snippet)
- name: Request mTLS client cert from internal CA
  run: |
    cfssl gencert \
      -tls-remote-ca /etc/ca/root.pem \
      -hostname "ci-${{ github.run_id }}.internal" \
      -profile ci-ephemeral \
      -config ca-client.json \
      csr.json | cfssljson -bare client

    # Upload to ephemeral secret, expire in 24h
    aws secretsmanager create-secret \
      --name "ci-cert-${{ github.run_id }}" \
      --secret-string "$(cat client.pem client-key.pem)" \
      --description "Auto-expire" \
      --tags Key=AutoDelete,Value=24h

Lợi ích:

  • Cert leak qua log → 24h sau hết tác dụng. Không phải đi rotate.
  • Không có CRL/OCSP cho cert này — nó tự revoke vì expire.
  • Audit trail: mỗi build có cert riêng, traceable tới github.run_id.

Đây là pattern Cloudflare dùng cho internal infrastructure — họ ký cert có lifetime tính bằng giờ.

Revocation: CRL hay OCSP, hay không cần?

Câu hỏi triết học PKI: cert revocation luôn pain. CRL list trễ, OCSP load lên CA. CFSSL hỗ trợ cả hai qua ocspserve binary, nhưng tôi không enable.

Reasoning: nếu mọi cert leaf < 7 ngày, revocation là vấn đề window 7 ngày. Khi compromise, rotate intermediate signing key, mọi cert đang issued bằng key cũ vẫn valid tới khi expire — đó là lý do leaf phải ngắn. Nếu cần hard cutoff trong vài giờ, rotate CA cert. Không cần OCSP.

Nếu vẫn muốn OCSP (compliance, audit requirement):

# Generate OCSP responder cert (signed by intermediate)
cfssl gencert \
  -ca intermediate.pem -ca-key intermediate-key.pem \
  -config ca-config.json -profile ocsp \
  ocsp-csr.json | cfssljson -bare ocsp

# Run responder
ocspserve -port=8889 -db-config=ocspdb.json

ocspdb.json point tới SQLite/MySQL — đó là chỗ CFSSL không tự lo được. Bạn phải có database track revoked cert. Nếu không sẵn sàng vận hành DB, đừng dùng OCSP.

So sánh với AWS Private CA — bảng quyết định

Yếu tốCFSSL self-hostAWS Private CA
Cost flat~$30-50/tháng (2 VM + KMS)$400/CA/tháng
Cost per cert$0 (CPU/network only)Free (short-lived < 7d) hoặc $0.75/cert
Setup time1-2 ngày30 phút
HSM-backed rootYubiHSM2 (~$650 one-time) hoặc CloudHSM ($1.40/h)Bao gồm
HA / multi-regionBạn tự lo (Active-passive với KMS)Built-in
ComplianceBạn lo auditSOC 2, PCI, HIPAA, FIPS 140-2
Integration EKS/RDSCần codeNative (PCA Connector)
CRL/OCSPCó nhưng tự hostManaged
Audit logBạn build (CloudTrail equivalent)CloudTrail tự động

Quy tắc tôi dùng:

  • < 10 service, không compliance bắt buộc → CFSSL.
  • ≥ 10 service hoặc PCI/HIPAA hoặc cần SOC 2 in scope → AWS Private CA. Audit cost saving > $400/tháng.
  • Multi-account AWS org → AWS Private CA + Resource Access Manager share. CFSSL multi-account khó.
  • EKS với cert-manager → AWS Private CA Issuer plugin chính chủ; CFSSL có cfssl-issuer cho cert-manager nhưng kém maintained.

HSM integration — đừng skip nếu nghiêm túc

Intermediate key trên disk = nếu VM compromise thì game over. CFSSL hỗ trợ PKCS#11 từ phiên bản 1.4:

{
  "private": "pkcs11:slot-id=0;token=YubiHSM;id=%01;pin-source=file:///etc/cfssl/pin",
  "certificate": "./intermediate.pem"
}

YubiHSM2 đủ tốt cho throughput ~50 sign/sec. Nếu cần > 500 sign/sec (Cloudflare scale) thì cần SoftHSM hoặc AWS CloudHSM cluster. Cho 6 service, một YubiHSM2 thừa.

Backup root key — nơi đa số bị fail

Root key sống 20 năm. Backup chiến lược của tôi:

  1. Generate root trên airgapped Linux laptop (Tails OS).
  2. cfssljson xuất root-ca-key.pem.
  3. Encrypt bằng age với 5 recipient key (CTO, 2 SRE lead, CISO, security officer).
  4. Split key bằng ssss-split (Shamir Secret Sharing) — 3-of-5 threshold.
  5. Print từng share lên giấy, lưu 5 location vật lý khác nhau.
  6. Wipe laptop.

Khôi phục root key cần 3 người gặp mặt + decrypt age recipients. Quá phức tạp cho daily ops — đó là điểm. Root key chỉ dùng khi rotate intermediate (1-5 năm/lần).

Vận hành — checklist sau 8 tháng

  • Root CA generated airgapped, key encrypted + Shamir split, 5 location backup
  • Intermediate CA ECDSA P-256, lifetime 5 năm
  • Intermediate key trong HSM (YubiHSM2 hoặc CloudHSM)
  • multirootca chạy 2 instance behind NLB, health check /api/v1/cfssl/info
  • Auth key HMAC per service, rotate quý
  • IP allowlist (nets) tại CA level
  • Profile cho mỗi loại cert: server, client, peer, ocsp
  • Leaf cert max 7 ngày (24h cho CI)
  • Root cert trong trust store của mọi internal service (Ansible/Salt push)
  • Monitoring: prom metric cfssl_sign_requests_total qua exporter
  • Alert nếu sign rate > 2× baseline (có thể CI loop infinite)
  • DR plan: rotate intermediate trong 4h (test mỗi quý)
  • Document escalation cho lost auth key

Cạm bẫy thường gặp

1. Quên ca_constraint.is_ca: true cho intermediate. Cert sinh ra nhưng không ký được cert khác. Browser/client throw unable to get local issuer.

2. Dùng cfssl serve thay vì multirootca. cfssl serve chỉ là dev tool — single profile, không reload, không HSM.

3. Profile expiry không match thực tế. Set 8760h (1 năm) cho leaf rồi quên — 12 tháng sau outage hàng loạt.

4. CN với wildcard không hoạt động cho mTLS client. Client cert dùng subject để identify; wildcard *.svc.cluster.local trong CN làm verification fail vì client cert không match SNI logic.

5. multirootca không reload config khi SIGHUP. Phải restart. Plan rolling restart.

Bottom line

CFSSL không phải DIY hobby project — nó là toolkit production-grade Cloudflare đang chạy. Nếu bạn dưới ngưỡng AWS Private CA ROI ($400/tháng cho < 10 service) và sẵn sàng vận hành 2 VM + HSM + IaC, CFSSL self-host tiết kiệm 80-90% cost. Trade-off là bạn lo HA, audit log, integration. Đối với 6-service internal mesh của tôi, đó là trade-off đáng giá. Khi scale lên 30 service hoặc cần PCI compliance, di chuyển sang Private CA là một-tuần effort vì hierarchy đã đúng từ đầu.

Tham chiếu