核心思路是提取证书notafter时间并转为unix时间戳,与当前时间比较得剩余天数;关键命令需加-servername支持sni、多处2>/dev/null静默容错,并封装为自动识别域名/本地文件的shell函数,配合低频定时、日志记录、去重告警及webhook通知。

用 openssl 提取证书过期时间并计算剩余天数
核心思路是:从域名或本地证书文件中提取 notAfter 时间,转为 Unix 时间戳,再与当前时间比较。关键命令链是:openssl s_client -connect example.com:443 -servername example.com 2>/dev/null | openssl x509 -noout -enddate。注意必须加 -servername(SNI),否则对虚拟主机可能拿到错误证书;2>/dev/null 是为了屏蔽连接警告(如自签名、握手失败等),避免干扰解析。
提取后得到类似 notAfter=Jan 15 12:34:56 2026 GMT 的输出,用 awk 和 date -d 转换即可:
expiry=$(echo | openssl s_client -connect example.com:443 -servername example.com 2>/dev/null | openssl x509 -noout -enddate 2>/dev/null | awk -F'= ' '{print $2}')
days_left=$(( ($(date -d "$expiry" +%s 2>/dev/null) - $(date +%s)) / 86400 ))
这里 2>/dev/null 多处出现,不是偷懒——任何一步失败(连不上、无响应、证书格式异常)都会导致变量为空或报错,必须静默兜底。
写成可复用的 Shell 函数并支持多种输入源
单个域名检查不够实用,脚本得能处理:远程域名、本地 PEM 文件、甚至多个目标。建议封装为函数,接受一个参数,并自动判断类型:
- 如果参数含
:(如example.com:443),走openssl s_client远程检查 - 如果参数以
/开头且文件存在,当作本地证书路径,用openssl x509 -in $file -noout -enddate - 否则默认按域名处理(补
:443)
这样调用就灵活:check_cert example.com、check_cert /etc/ssl/certs/my.crt、check_cert api.example.org:8443。函数内务必校验 openssl 命令是否存在,以及 date -d 是否支持 GNU 扩展(macOS 的 date 不兼容,需用 gdate 替代)。
定时轮询时如何避免误报和资源浪费
直接用 crontab 每分钟跑一次 openssl s_client 很危险:大量 TCP 握手会触发目标 WAF 限流,也可能被当成探测攻击。实际应控制频率与并发:
- 对生产域名,建议每 6–24 小时检查一次,用
0 */6 * * *这类低频策略 - 多个域名不要串行阻塞执行,可用
timeout 10s check_cert domain1 &后台+超时保护 - 结果必须落盘(如追加到
/var/log/cert-check.log),带时间戳和原始输出,方便排查“为什么某次没告警” - 别只依赖天数阈值(如
days_left ),要加状态去重:连续两次都 <code> 才发通知,避免网络抖动导致的瞬时误判
告警逻辑不能只靠 echo 或 mail
Shell 脚本里写 echo "SSL expires in $days_left days" | mail -s "ALERT" admin@example.com 看似简单,但实际运维中几乎不可靠:
-
mail命令在多数现代 Linux 发行版默认未安装,且依赖本地 MTA(如 sendmail/postfix)配置正确 - 邮件可能进垃圾箱,无送达确认,更无法追踪是否已处理
- 真正可用的方式是调用 Webhook:用
curl -X POST -H "Content-Type: application/json" -d '{"text":"SSL for example.com expires in '$days_left' days"}' https://hooks.slack.com/services/XXX
如果必须用邮件,至少加上 if [ "$days_left" -lt 30 ] && ! grep -q "$(date +%Y-%m-%d)-example.com" /tmp/cert_alerted; then ... echo "$(date +%Y-%m-%d)-example.com" >> /tmp/cert_alerted; fi,防止每天重复轰炸。
证书时间解析看似简单,但 openssl 输出格式随版本微调、GMT 时区处理、系统 date 实现差异、网络超时边界——这些细节堆在一起,才是轮询脚本能长期稳定跑下去的关键。











