3. 证书颁发机构(CA)搭建:开源CA选型与私有CA实战
说到CA搭建,我先泼盆冷水——别一上来就想着自建CA。我见过太多团队,设备量不到一千台,却花了两周搭CA,最后证书轮换时把自己坑了。选型这事,说白了就是看你的规模、团队能力和运维成本。
这一章,我会把三个主流开源CA——EasyRSA、CFSSL、cert-manager——掰开揉碎讲清楚。最后带你把私有CA搭起来,跑通证书签发全流程。
核心观点:没有最好的CA,只有最适合你场景的CA。选型前先回答三个问题——你的设备量级是多少?团队有Go/Shell能力吗?证书轮换频率多高?
3.1 三大开源CA横向对比
先看一张对比表,心里有个底。
| 特性 | EasyRSA | CFSSL | cert-manager |
|---|---|---|---|
| 开发语言 | Shell | Go | Go |
| 部署复杂度 | 低(一条命令) | 中(需配置JSON) | 高(需K8s集群) |
| API支持 | 无 | RESTful API | K8s CRD + API |
| 证书轮换 | 手动 | 半自动(脚本+API) | 全自动(CRD驱动) |
| 适用场景 | 小规模、测试环境 | 中等规模、API集成 | 大规模、K8s原生 |
| CRL支持 | 有限 | 完整 | 完整 |
3.2 EasyRSA:小团队的救星
EasyRSA 是 OpenVPN 项目孵化的CA工具。说白了,它就是一堆Shell脚本的集合。我最早做IoT设备证书时就用它,那时候设备才几百台,够用。
安装与初始化
# 下载EasyRSA
wget https://github.com/OpenVPN/easy-rsa/releases/download/v3.1.7/EasyRSA-3.1.7.tgz
tar xzf EasyRSA-3.1.7.tgz
cd EasyRSA-3.1.7
# 初始化PKI目录
./easyrsa init-pki
# 生成CA根证书和私钥(nopass表示不加密私钥)
./easyrsa build-ca nopass
我的习惯:生产环境一定要用密码保护CA私钥。虽然每次签名要输密码麻烦点,但安全第一。我见过有人把无密码的CA私钥传到Git仓库里——嗯,那画面太美我不敢看。
签发设备证书
# 为设备生成证书请求并签名
./easyrsa gen-req device-001 nopass
./easyrsa sign-req client device-001
# 查看证书
openssl x509 -in pki/issued/device-001.crt -text -noout
避坑指南
- EasyRSA 默认使用 SHA256,如果你的设备只支持 SHA1,记得改
vars文件 - 证书有效期默认10年,大规模轮换时建议改成1-2年
- 没有API,批量签发只能写Shell循环——我曾经写过3000台设备的循环脚本,跑了整整一个下午
3.3 CFSSL:API驱动的灵活之选
CFSSL 是 CloudFlare 开源的PKI工具包。它最大的优势是RESTful API,可以集成到你的自动化平台里。我个人比较喜欢它的多签功能——一个CA可以同时处理多个签名请求。
部署与配置
# 下载CFSSL
wget https://github.com/cloudflare/cfssl/releases/download/v1.6.4/cfssl_1.6.4_linux_amd64
wget https://github.com/cloudflare/cfssl/releases/download/v1.6.4/cfssljson_1.6.4_linux_amd64
chmod +x cfssl_* cfssljson_*
sudo mv cfssl_* /usr/local/bin/cfssl
sudo mv cfssljson_* /usr/local/bin/cfssljson
# 创建CA配置文件 ca-config.json
cat > ca-config.json << 'EOF'
{
"signing": {
"default": {
"expiry": "8760h"
},
"profiles": {
"server": {
"usages": ["signing", "key encipherment", "server auth"],
"expiry": "8760h"
},
"client": {
"usages": ["signing", "key encipherment", "client auth"],
"expiry": "8760h"
}
}
}
}
EOF
# 创建CA证书请求 ca-csr.json
cat > ca-csr.json << 'EOF'
{
"CN": "My Private CA",
"key": {
"algo": "rsa",
"size": 2048
},
"names": [
{
"C": "CN",
"ST": "Beijing",
"L": "Beijing",
"O": "MyOrg",
"OU": "Security"
}
]
}
EOF
# 生成CA根证书
cfssl gencert -initca ca-csr.json | cfssljson -bare ca
启动API服务
# 启动CFSSL API服务(监听9443端口)
cfssl serve -address=0.0.0.0 -port=9443 \
-ca=ca.pem -ca-key=ca-key.pem \
-config=ca-config.json
通过API签发证书
# 创建设备证书请求
cat > device-csr.json << 'EOF'
{
"CN": "device-001",
"hosts": ["device-001.local"],
"key": {
"algo": "rsa",
"size": 2048
},
"names": [
{"O": "MyOrg", "OU": "Devices"}
]
}
EOF
# 调用API签名
curl -s -X POST http://localhost:9443/api/v1/cfssl/sign \
-H "Content-Type: application/json" \
-d '{
"certificate_request": '$(cat device-csr.json | base64 -w0)',
"profile": "client"
}' | cfssljson -bare device-001
注意:CFSSL API 默认没有认证。生产环境一定要加TLS和认证,或者放在内网。我曾经在客户现场看到CFSSL暴露在公网——嗯,那台服务器第二天就被挖矿了。
3.4 cert-manager:K8s环境的首选
如果你的基础设施跑在Kubernetes上,cert-manager 几乎是唯一选择。它通过CRD(Custom Resource Definition)管理证书生命周期,自动轮换、自动续期。
安装
# 使用Helm安装
helm repo add jetstack https://charts.jetstack.io
helm repo update
helm install cert-manager jetstack/cert-manager \
--namespace cert-manager \
--create-namespace \
--version v1.14.5 \
--set installCRDs=true
创建ClusterIssuer(集群级CA)
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: selfsigned-issuer
spec:
selfSigned: {}
---
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: my-ca
namespace: cert-manager
spec:
isCA: true
commonName: "My Private CA"
secretName: ca-key-pair
privateKey:
algorithm: RSA
size: 2048
issuerRef:
name: selfsigned-issuer
kind: ClusterIssuer
自动签发设备证书
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: device-001
namespace: default
spec:
commonName: "device-001"
dnsNames:
- "device-001.local"
secretName: device-001-tls
duration: 2160h # 90天
renewBefore: 360h # 提前15天续期
issuerRef:
name: my-ca
kind: ClusterIssuer
我的经验:cert-manager 的自动轮换是它最大的卖点。我在一个10万节点的K8s集群上跑过,证书轮换时几乎零感知。但要注意——CRD数量多了以后,etcd压力会上升,建议给cert-manager单独配一个etcd集群。
3.5 私有CA搭建实战:从零到签发
好,理论讲完了。现在咱们动手搭一个私有CA。我选CFSSL作为演示,因为它兼顾了灵活性和自动化能力。
步骤一:准备环境
# 创建目录结构
mkdir -p /opt/private-ca/{certs,crl,csr,private}
cd /opt/private-ca
# 生成CA根证书(使用OpenSSL直接生成,不依赖CFSSL)
openssl genrsa -out private/ca.key 4096
openssl req -new -x509 -days 3650 \
-key private/ca.key \
-out certs/ca.crt \
-subj "/C=CN/ST=Beijing/L=Beijing/O=MyOrg/OU=CA/CN=My Private CA Root"
步骤二:配置CFSSL
# 创建CFSSL配置文件
cat > cfssl-config.json << 'EOF'
{
"signing": {
"default": {
"expiry": "43800h",
"usages": ["signing", "key encipherment"]
},
"profiles": {
"device": {
"usages": ["signing", "key encipherment", "client auth"],
"expiry": "8760h"
},
"server": {
"usages": ["signing", "key encipherment", "server auth"],
"expiry": "8760h"
}
}
}
}
EOF
# 启动CFSSL服务(使用我们自己的CA)
cfssl serve -address=0.0.0.0 -port=9443 \
-ca=certs/ca.crt \
-ca-key=private/ca.key \
-config=cfssl-config.json \
-loglevel=0 &
步骤三:批量签发脚本
#!/bin/bash
# batch-sign.sh - 批量签发设备证书
CA_API="http://localhost:9443/api/v1/cfssl/sign"
DEVICE_LIST="devices.txt"
OUTPUT_DIR="./issued-certs"
mkdir -p $OUTPUT_DIR
while IFS= read -r device; do
echo "Signing certificate for $device..."
# 生成CSR
cat > /tmp/${device}-csr.json << EOF
{
"CN": "$device",
"hosts": ["$device.local"],
"key": {"algo": "rsa", "size": 2048},
"names": [{"O": "MyOrg", "OU": "Devices"}]
}
EOF
# 调用API签名
curl -s -X POST $CA_API \
-H "Content-Type: application/json" \
-d "{\"certificate_request\": \"$(cat /tmp/${device}-csr.json | base64 -w0)\", \"profile\": \"device\"}" \
| cfssljson -bare ${OUTPUT_DIR}/${device}
echo "Done: $device"
done < "$DEVICE_LIST"
步骤四:验证证书链
# 验证设备证书是否由CA签发
openssl verify -CAfile certs/ca.crt issued-certs/device-001.pem
# 查看证书有效期
openssl x509 -in issued-certs/device-001.pem -noout -dates
关键检查点:
- CA私钥权限必须为600,属主root
- 证书链必须完整(CA证书 + 设备证书)
- CRL(证书吊销列表)要定期生成,我一般每周跑一次cron
- 备份CA私钥到离线存储——丢了它,所有设备都得重新注册
3.6 选型决策清单
最后,给你一个决策清单。下次选型时,对着打勾就行。
| 条件 | 推荐方案 | 理由 |
|---|---|---|
| 设备 < 1万,无API需求 | EasyRSA | 零依赖,一条命令搞定 |
| 设备 1万-10万,需API集成 | CFSSL | RESTful API,灵活扩展 |
| 设备 > 10万,K8s环境 | cert-manager | 自动轮换,CRD驱动 |
| 设备 > 10万,非K8s环境 | CFSSL + 自研调度 | API + 自定义轮换策略 |
| 测试/开发环境 | EasyRSA | 快速验证,用完即弃 |