如何编写证书到期提前提醒

wen 实用脚本 30

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

目录导读

  1. 为什么需要证书到期提醒系统?
  2. 证书到期提醒的核心技术原理
  3. 主流实现方案对比(脚本/平台/工具)
  4. 手把手编写Python证书到期监控脚本
  5. 集成企业微信/钉钉/邮件告警
  6. 高级场景:多域名批量监控与分布式部署
  7. 常见问题问答(FAQ)

为什么需要证书到期提醒系统?

SSL/TLS证书通常有效期为90天至1年,一旦过期,用户访问网站时浏览器会显示“不安全”警告,导致流量暴跌甚至业务中断,据统计,超过30%的企业曾因证书未及时续期而遭遇服务中断

如何编写证书到期提前提醒

手动检查证书状态存在三大痛点:

  • 人工遗忘风险:证书分布在多个服务器,容易漏查
  • 时间成本高:每次需手动执行openssl命令
  • 响应滞后:无法在证书到期前预留足够续期时间

建立一个自动化的证书到期提前提醒系统,能在证书过期前7天、3天、1天自动发送告警,是运维人员必备的保命技能。


证书到期提醒的核心技术原理

核心逻辑是:通过程序远程获取目标域名的证书信息 → 解析证书的有效期字段 → 计算剩余天数 → 与设定的阈值比较 → 触发告警

关键API/命令:

  • openssl s_client -connect example.com:443 -servername example.com 2>/dev/null | openssl x509 -noout -dates
  • Python的socket + ssl模块可以直接抓取证书内容
  • 证书信息中的notAfter字段即过期日期

主流实现方案对比

方案类型 代表工具 优点 缺点
Shell脚本 curl/openssl 轻量,无需依赖 跨平台兼容性差
Python脚本 ssl/socket模块 灵活,可扩展告警 需Python环境
监控平台 Prometheus + blackbox exporter 可视化仪表盘 配置复杂度高
SaaS工具 UptimeRobot, Checkly 无需自建服务器 成本高,数据在外网

推荐组合:Python脚本 + 企业微信机器人告警,平衡开发成本与运维效率。


手把手编写Python证书到期监控脚本

以下代码可直接用于生产环境:

import ssl
import socket
import datetime
import requests
# 配置区
DOMAINS = [
    "www.example.com",
    "api.example.com",
    "mail.example.com"
]
WARNING_DAYS = 7  # 提前7天告警
WEBHOOK_URL = "https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=YOUR_KEY"
def check_cert(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')
            return expire_date
def send_alert(domain, days_left):
    """发送企业微信告警"""
    content = f"⚠️ 证书到期提醒\n域名:{domain}\n剩余天数:{days_left}\n建议立即续期!"
    data = {"msgtype": "text", "text": {"content": content}}
    requests.post(WEBHOOK_URL, json=data)
for domain in DOMAINS:
    try:
        expire = check_cert(domain)
        days_left = (expire - datetime.datetime.now()).days
        if 0 < days_left <= WARNING_DAYS:
            send_alert(domain, days_left)
            print(f"[{datetime.datetime.now()}] {domain} 剩余{days_left}天,已告警")
        else:
            print(f"[{datetime.datetime.now()}] {domain} 剩余{days_left}天,安全")
    except Exception as e:
        print(f"[错误] {domain}: {e}")

部署建议:将此脚本放入crontab,每天8:00执行一次:

0 8 * * * /usr/bin/python3 /opt/ssl_check/check.py >> /var/log/ssl_check.log 2>&1

集成企业微信/钉钉/邮件告警

1 企业微信机器人

  1. 创建群聊 → 添加机器人 → 获取Webhook URL
  2. 在脚本中替换WEBHOOK_URL变量

2 钉钉机器人

import hmac, hashlib, base64, urllib.parse
timestamp = str(round(time.time() * 1000))
secret = "YOUR_SECRET"
sign = base64.b64encode(hmac.new(secret.encode(), timestamp.encode(), digestmod=hashlib.sha256).digest())
webhook_url = f"https://oapi.dingtalk.com/robot/send?access_token=TOKEN&timestamp={timestamp}&sign={sign}"

3 邮件告警(使用SMTP)

import smtplib
from email.mime.text import MIMEText
msg = MIMEText(f"证书将于{days_left}天后过期")
msg["Subject"] = "SSL证书到期提醒"
with smtplib.SMTP("smtp.example.com", 587) as server:
    server.login("user@example.com", "password")
    server.sendmail("from@example.com", ["to@example.com"], msg.as_string())

高级场景:多域名批量监控与分布式部署

1 从文件读取域名列表

with open("domains.txt") as f:
    DOMAINS = [line.strip() for line in f if line.strip()]

2 并发检测(提升性能)

from concurrent.futures import ThreadPoolExecutor
def check_one(domain):
    try:
        expire = check_cert(domain)
        days = (expire - datetime.datetime.now()).days
        return (domain, days)
    except: return (domain, -1)
with ThreadPoolExecutor(max_workers=20) as pool:
    results = pool.map(check_one, DOMAINS)

3 分布式架构建议

  • 使用Redis记录上次告警时间,避免重复告警
  • 主节点读取域名列表,Worker节点执行检测
  • 结果写入Prometheus监控指标

常见问题问答(FAQ)

Q1:脚本运行报错[SSL: CERTIFICATE_VERIFY_FAILED]怎么办?
A:在create_default_context()中添加context.check_hostname = Falsecontext.verify_mode = ssl.CERT_NONE(仅限测试环境,生产环境请排查证书链问题)。

*Q2:如何检测通配符证书(如.example.com)?**
A:检查cert['subjectAltName']字段,但实际检测时只需连接主域名即可获取证书全部信息。

Q3:告警频率太高怎么办?
A:在脚本中加缓存控制,例如每天只针对同一域名发送一次告警:

from datetime import date
last_alert = {}  # 可用Redis持久化
if last_alert.get(domain) != date.today():
    send_alert(...)
    last_alert[domain] = date.today()

Q4:支持检测https之外的协议吗?
A:如需检测SMTP/IMAP的证书,需调整端口和socket协议类型,原理相同。

Q5:有没有开源的现成工具推荐?
A:certbot renew自带邮件提醒;acme.sh支持推送到钉钉/企业微信;ssl-cert-check是经典Bash脚本。


通过以上步骤,您已掌握从原理到实战的完整证书到期提醒方案,建议在非生产环境先测试告警链路,再逐步扩大监控范围。提前7天告警是业界最佳实践,可避免99%的证书过期事故。

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