3. 证书发现与资产盘点:如何自动发现网络中的设备证书
说实话,很多团队做证书管理,第一步就卡住了。
我问你一个问题:你手头到底有多少张设备证书?分布在哪些IP上?哪些快要过期了?
我见过不少运维同事,拍着胸脯说「证书都在CMDB里管着呢」。结果一查,CMDB里只录了三分之一。剩下的证书,散落在各个业务服务器、负载均衡器、甚至是开发测试环境里。嗯,这就是典型的「证书黑户」问题。
所以这一章,咱们就聊聊怎么把这些「黑户」揪出来。
3.1 证书发现的核心思路
证书发现,说白了就是回答三个问题:
- 在哪找?——扫描范围
- 怎么找?——扫描手段
- 找到以后怎么办?——资产入库
我个人习惯把证书发现分成两个维度:
| 维度 | 目标 | 典型工具 |
|---|---|---|
| 主动扫描 | 从外部探测网络中的证书 | nmap、sslscan、masscan |
| 被动采集 | 从服务器本地读取证书文件 | Agent、SSH命令、配置管理工具 |
你想想看,主动扫描适合「摸家底」,被动采集适合「精细化管理」。两者结合,才能把证书资产盘清楚。
核心原则:先粗后细,先扫后采。先用主动扫描把网络里的证书「照」出来,再用被动采集把证书的详细信息「拉」回来。
3.2 主动扫描:从外部发现证书
主动扫描是最直接的方法。我记得有一次帮一个金融客户做证书审计,他们说自己只有200张证书。我用nmap扫了一圈,好家伙,扫出来将近400张。多出来的那些,全是「僵尸证书」——证书还在,服务早停了。
3.2.1 使用nmap进行证书扫描
nmap的ssl-cert脚本,是发现证书的利器。来看一个例子:
# 扫描单个网段,发现所有HTTPS证书
nmap -p 443 --script ssl-cert 192.168.1.0/24
# 扫描多个常见端口
nmap -p 443,8443,9443 --script ssl-cert 10.0.0.0/8
# 输出为XML格式,方便后续处理
nmap -p 443 --script ssl-cert -oX cert_scan.xml 192.168.1.0/24
这个脚本能返回什么信息?我列一下:
- 证书主题(Subject)
- 颁发者(Issuer)
- 有效期(Not Before / Not After)
- 序列号
- SAN(Subject Alternative Names)
小技巧:如果网段很大,建议先用masscan快速扫出开放443端口的IP,再用nmap做精细扫描。masscan号称「10分钟扫全网」,效率比nmap高一个数量级。
3.2.2 使用sslscan做深度检测
nmap能拿到证书基本信息,但如果你想看证书链是否完整、有没有弱加密套件,我推荐用sslscan。
# 扫描单个目标
sslscan --show-certificate example.com:443
# 批量扫描
for ip in $(cat ip_list.txt); do
sslscan --show-certificate $ip:443 >> sslscan_output.txt
done
sslscan的输出里,我最关注的是证书链部分。曾经有个项目,客户说他们的证书是正规CA签发的,结果我一扫发现中间证书没配全,导致移动端访问报错。这种问题,光靠nmap是看不出来的。
3.3 被动采集:从内部获取证书
主动扫描有个局限——它只能发现对外暴露的证书。那些内部服务之间用的mTLS证书、Kubernetes集群里的Ingress证书,主动扫描是扫不到的。这时候就需要被动采集了。
3.3.1 通过SSH远程采集
对于Linux服务器,我习惯写一个采集脚本,通过SSH批量执行:
#!/bin/bash
# 采集服务器上的所有PEM证书文件
CERT_DIRS="/etc/ssl/certs /etc/pki /etc/kubernetes/pki /opt/app/certs"
for dir in $CERT_DIRS; do
if [ -d "$dir" ]; then
find "$dir" -name "*.crt" -o -name "*.pem" -o -name "*.cert" | while read cert; do
echo "=== $cert ==="
openssl x509 -in "$cert" -noout -subject -dates -issuer
done
fi
done
然后配合ansible或者pssh批量执行:
# 使用ansible批量采集
ansible all -m script -a "collect_certs.sh" -o cert_collection/
注意:采集脚本要处理好权限问题。有些证书文件只有root才能读。我建议用sudo执行,但要在脚本里做好日志审计,避免留下安全漏洞。
3.3.2 从Kubernetes集群采集
K8s环境里的证书,藏得比较深。我常用的方法是:
# 采集所有Secret中的证书
kubectl get secrets --all-namespaces -o json | jq -r '
.items[] |
select(.type == "kubernetes.io/tls") |
{namespace: .metadata.namespace, name: .metadata.name,
cert: .data["tls.crt"] | @base64d}
'
这里有个坑——很多团队把证书放在Secret里,但Secret的命名不规范。有的叫tls-secret,有的叫cert-secret,还有的直接叫my-cert。所以光靠名字过滤是不够的,必须按type来过滤。
3.4 资产入库与去重
证书找到了,接下来就是入库。这一步看似简单,但坑不少。
3.4.1 证书指纹:唯一标识
同一张证书可能出现在多个IP上(比如负载均衡场景)。怎么判断是不是同一张证书?看指纹。
# 计算证书的SHA256指纹
openssl x509 -in cert.pem -noout -fingerprint -sha256
我建议用SHA256指纹作为证书的唯一标识。为什么不用序列号?因为不同CA签发的证书,序列号可能重复。指纹是内容哈希,理论上不会重复。
3.4.2 资产表结构
这是我常用的证书资产表结构:
| 字段 | 类型 | 说明 |
|---|---|---|
| fingerprint | VARCHAR(64) | SHA256指纹,主键 |
| subject | VARCHAR(512) | 证书主题 |
| issuer | VARCHAR(512) | 颁发者 |
| not_before | DATETIME | 生效时间 |
| not_after | DATETIME | 过期时间 |
| san_list | TEXT | SAN列表,JSON格式 |
| discovered_ips | TEXT | 发现该证书的IP列表 |
| source | VARCHAR(32) | 发现方式:scan/agent/manual |
| first_seen | DATETIME | 首次发现时间 |
| last_seen | DATETIME | 最近发现时间 |
经验之谈:一定要记录first_seen和last_seen。这两个字段能帮你发现「僵尸证书」——如果一张证书超过30天没被任何扫描任务发现,大概率已经没人用了。
3.5 自动化发现流程
手动扫描一次不难,难的是持续发现。我建议搭一个自动化流水线:
# 一个简单的定时任务示例(crontab)
# 每天凌晨2点执行全量扫描
0 2 * * * /opt/cert_monitor/scripts/full_scan.sh
# 每4小时执行增量扫描
0 */4 * * * /opt/cert_monitor/scripts/incremental_scan.sh
全量扫描和增量扫描的区别:
- 全量扫描:扫所有网段,适合周末执行
- 增量扫描:只扫新增IP或变更过的IP,适合工作日执行
我曾经踩过一个坑——全量扫描频率太高,把网络设备扫挂了。后来我学乖了,全量扫描控制在每周一次,增量扫描控制在每4小时一次。网络设备扛得住,数据也够实时。
3.6 本章知识体系
下面这张图,把证书发现与资产盘点的核心逻辑串起来了:
这张图你看懂了吗?从上到下,就是一条完整的证书发现流水线。从主动扫描和被动采集两条腿走路,到数据汇聚去重,再到资产入库,最后形成持续监控的闭环。
本章小结:证书发现不是一次性工作,而是一个持续的过程。我建议你从主动扫描入手,快速摸清家底;然后逐步建立被动采集能力,覆盖内部证书;最后用资产表把证书管起来,配合定时任务实现自动化发现。