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.
multirootcacho 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 signAPI, 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.smallchạ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:
| Binary | Mục đích |
|---|---|
cfssl | CLI chính: init CA, sign cert, gencert, info |
cfssljson | Parse JSON output của cfssl thành file .pem/.csr riêng |
multirootca | Long-running daemon serve nhiều CA profile qua HTTP API |
mkbundle | Build intermediate cert bundle cho client verify |
cfssl-bundle | Tạo cert chain hoàn chỉnh từ leaf cert |
cfssl-certinfo | Inspect cert (alternative cho openssl x509 -text) |
cfssl-newkey | Generate 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:
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.nets— IP allowlist tại CA level. Defense-in-depth nếu auth key leak.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-host | AWS 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 time | 1-2 ngày | 30 phút |
| HSM-backed root | YubiHSM2 (~$650 one-time) hoặc CloudHSM ($1.40/h) | Bao gồm |
| HA / multi-region | Bạn tự lo (Active-passive với KMS) | Built-in |
| Compliance | Bạn lo audit | SOC 2, PCI, HIPAA, FIPS 140-2 |
| Integration EKS/RDS | Cần code | Native (PCA Connector) |
| CRL/OCSP | Có nhưng tự host | Managed |
| Audit log | Bạ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:
- Generate root trên airgapped Linux laptop (Tails OS).
cfssljsonxuấtroot-ca-key.pem.- Encrypt bằng
agevới 5 recipient key (CTO, 2 SRE lead, CISO, security officer). - Split key bằng
ssss-split(Shamir Secret Sharing) —3-of-5threshold. - Print từng share lên giấy, lưu 5 location vật lý khác nhau.
- 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)
-
multirootcachạ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_totalqua 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.