脚本如何检测证书是否过期

wen 实用脚本 2

自动化监控与告警实战指南

目录导读

  1. 为什么需要自动化检测证书过期?
  2. 证书过期检测的核心原理
  3. 主流脚本方案详解
  4. 高级功能与最佳实践
  5. 常见问题与问答
  6. 总结与建议

为什么需要自动化检测证书过期?

在Web安全与运维领域,SSL/TLS证书过期是导致服务中断的常见原因之一,根据相关安全报告,超过30%的HTTPS服务中断事件与证书未及时续期有关,手动检查证书有效期不仅效率低下,而且容易遗漏,特别是在管理多个域名或微服务架构中。

脚本如何检测证书是否过期

自动化脚本检测证书过期可以实现:

  • 24/7不间断监控:定时执行检查,无需人工介入。
  • 提前告警:在证书到期前7天、3天、1天等关键时间点发送通知。
  • 批量管理:一次性检查所有受管域名,生成可视化报告。
  • 历史追踪:记录证书状态变化,便于审计与合规。

证书过期检测的核心原理

所有证书检测脚本都基于同一个底层机制:解析服务器在TLS握手阶段返回的证书信息,具体步骤如下:

  1. 建立TCP连接:连接到目标主机的443端口(HTTPS默认端口)。
  2. 发起TLS握手:通过OpenSSL或编程语言的TLS库发起握手请求。
  3. 提取证书数据:从握手响应中提取X.509证书,解析其有效期字段(notBeforenotAfter)。
  4. 计算剩余天数:将证书到期时间与当前系统时间对比,计算剩余有效天数。
  5. 触发告警:根据预设阈值(如小于30天)采取相应动作(日志记录、邮件通知、Webhook调用等)。

主流脚本方案详解

1 基于OpenSSL的Bash脚本

这是最轻量级的方法,适用于Linux/Unix服务器,无需安装额外依赖。

核心命令示例

echo | openssl s_client -servername example.com -connect example.com:443 2>/dev/null | \
openssl x509 -noout -dates

自动化检测脚本

#!/bin/bash
DOMAINS=("example.com" "api.example.com" "blog.example.net")
THRESHOLD=30
ALERT_EMAIL="admin@company.com"
for DOMAIN in "${DOMAINS[@]}"; do
    EXPIRY_DATE=$(echo | openssl s_client -servername "$DOMAIN" -connect "$DOMAIN":443 2>/dev/null | \
                  openssl x509 -noout -enddate | cut -d= -f2)
    EXPIRY_EPOCH=$(date -d "$EXPIRY_DATE" +%s)
    CURRENT_EPOCH=$(date +%s)
    REMAINING=$(( (EXPIRY_EPOCH - CURRENT_EPOCH) / 86400 ))
    if [ "$REMAINING" -lt 0 ]; then
        echo "[CRITICAL] $DOMAIN 证书已过期!"
        echo "证书已过期: $DOMAIN" | mail -s "证书过期告警" $ALERT_EMAIL
    elif [ "$REMAINING" -le "$THRESHOLD" ]; then
        echo "[WARNING] $DOMAIN 证书将在 $REMAINING 天后过期"
        echo "证书即将过期: $DOMAIN (剩余 $REMAINING 天)" | mail -s "证书即将过期" $ALERT_EMAIL
    else
        echo "[OK] $DOMAIN 证书有效期剩余 $REMAINING 天"
    fi
done

特点:依赖OpenSSL,适用于最小化部署环境;但需注意部分旧版OpenSSL输出的时间格式可能不同。

2 Python脚本方案

Python方案具有更好的跨平台性和错误处理能力,尤其适合需要集成到现有监控系统中的场景。

使用ssl和socket模块

import ssl
import socket
import datetime
import smtplib
def check_certificate(hostname, port=443):
    context = ssl.create_default_context()
    with socket.create_connection((hostname, port), timeout=5) as sock:
        with context.wrap_socket(sock, server_hostname=hostname) as ssock:
            cert = ssock.getpeercert()
            expire_date = datetime.datetime.strptime(cert['notAfter'], '%b %d %H:%M:%S %Y %Z')
            remaining = (expire_date - datetime.datetime.now()).days
            return remaining
domains = ['example.com', 'example.org']
threshold = 30
for domain in domains:
    try:
        days_left = check_certificate(domain)
        status = "OK" if days_left > threshold else "WARNING" if days_left > 0 else "CRITICAL"
        print(f"{status}: {domain} 剩余 {days_left} 天")
        if days_left <= threshold:
            # 调用邮件或Webhook发送告警
            pass
    except Exception as e:
        print(f"[ERROR] {domain} 检测失败: {str(e)}")

扩展建议:集成requests库处理HTTP层的证书验证,或使用cryptography库进行更底层的证书解析。

3 PowerShell脚本方案

适用于Windows环境,特别是需要与Active Directory或其他Windows服务集成的场景。

$domains = @("example.com", "example.org")
$threshold = 30
foreach ($domain in $domains) {
    try {
        $tcpClient = New-Object System.Net.Sockets.TcpClient
        $tcpClient.Connect($domain, 443)
        $sslStream = New-Object System.Net.Security.SslStream($tcpClient.GetStream(), $false)
        $sslStream.AuthenticateAsClient($domain)
        $cert = $sslStream.RemoteCertificate
        $expiryDate = [DateTime]::Parse($cert.GetExpirationDateString())
        $remaining = ($expiryDate - (Get-Date)).Days
        if ($remaining -le 0) {
            Write-Host "[CRITICAL] $domain 证书已过期"
        } elseif ($remaining -le $threshold) {
            Write-Host "[WARNING] $domain 证书剩余 $remaining 天"
        } else {
            Write-Host "[OK] $domain 证书剩余 $remaining 天"
        }
        $sslStream.Close()
        $tcpClient.Close()
    } catch {
        Write-Host "[ERROR] $domain 检测失败: $_"
    }
}

高级功能与最佳实践

1 多域名批量扫描

将域名列表存储在domains.txt文件中,脚本按行读取,支持通配符域名(如*.example.com)和SNI(Server Name Indication)自动协商。

2 告警通知集成

  • 邮件告警:使用SMTP发送HTML格式邮件,包含证书详情、剩余天数和历史对比。
  • Webhook集成:调用企业微信、Slack、钉钉的Webhook接口,实时推送告警到团队群组。
  • 日志输出:记录到系统日志(syslog)或JSON格式文件,便于导入ELK、Splunk等日志分析平台。

3 定时调度

  • Linux Cron:配置crontab -e,每小时或每天执行一次脚本。
  • Windows Task Scheduler:创建计划任务,设置触发器为每天凌晨运行。
  • Docker化部署:将脚本打包为Docker镜像,配合Kubernetes CronJob实现容器化调度。

4 证书链完整性检查

除了检查叶子证书,还应验证中间证书和根证书是否也即将过期,使用OpenSSL的-showcerts参数可以获取完整证书链。

常见问题与问答

Q1:脚本检测证书时出现“连接超时”怎么办? A:检查目标服务器是否开放443端口,防火墙是否放行,可以使用telnet domain.com 443测试基础连通性,如果目标使用非标准端口(如8443),需要在脚本中手动指定端口参数。

Q2:自签名证书是否会影响检测结果? A:不会影响有效期检测,但脚本在TLS握手时可能因证书不受信任而抛出异常,建议修改ssl.create_default_context()ssl._create_unverified_context()绕过证书验证,仅提取有效期字段。

Q3:如何处理域名使用了CDN或反向代理的情况? A:CDN节点通常会缓存证书,检测到的有效期是CDN提供的证书,而非源站证书,此时需要结合CDN平台API(如Cloudflare的SSL分析接口)或通过特定路由方式(如指定Host头)获取源站证书。

Q4:脚本运行正常但邮件告警未收到,可能原因是什么? A:常见原因包括:(1) SMTP服务器配置错误(如端口、用户名密码);(2) 邮件被收件服务器判定为垃圾邮件;(3) 脚本中邮件发送函数的异常未被捕获,建议先测试mail命令或smtplib的简单发送示例,确认邮件功能独立可用。

Q5:免费证书(如Let's Encrypt)有效期仅90天,是否适合使用本脚本? A:非常适合,这类证书过期频繁,更需要自动化监控,可以结合certbot的自动续期脚本,在续期失败时通过本脚本及时告警。

总结与建议

通过脚本自动化检测证书过期,可以有效降低因证书失效导致的服务中断风险,在具体实施时,应关注以下几点:

  • 选择适合环境的脚本语言:Linux首选Bash+OpenSSL,跨平台需求选Python,Windows环境用PowerShell。
  • 设置合理的告警阈值:建议至少30天、7天、1天三级告警,避免过度打扰。
  • 定期测试告警通路:每月手动触发一次测试告警,确保通知渠道畅通。
  • 备份与冗余检测:建议在至少两台不同的服务器上部署检测脚本,避免单点故障。

证书过期检测是基础设施自动化运维中成本最低、收益最高的一环,无论您的环境规模如何,花15分钟部署一个检测脚本,远比处理一次证书到期导致的全站HTTPS错误更划算。

抱歉,评论功能暂时关闭!