
在HTTPS已成为标配的今天,一个即将过期的SSL/TLS证书轻则导致浏览器警告,重则直接中断服务,影响用户体验和业务连续性。手动管理成百上千个域名的证书到期时间,无异于一场噩梦。今天,我们就用Python打造一个轻量级的自动化监控与更新系统,让证书管理从此无忧。
往期阅读>>>
Python 自动化管理Jenkins的15个实用脚本,提升效率
App2Docker:如何无需编写Dockerfile也可以创建容器镜像
Python 自动化识别Nginx配置并导出为excel文件,提升Nginx管理效率
一、 为什么需要自动化监控?
想象一下,凌晨三点,你的核心业务网站因为证书过期而无法访问,警报响起,团队被紧急唤醒… 这并非危言耸听,而是许多运维团队曾经历或担忧的场景。手动记录、人工巡检的方式在域名数量激增、多云混合部署的复杂环境下,极易出现疏漏。自动化监控的核心价值在于 “防患于未然” ,通过程序定期检查,在证书到期前足够长的时间(如30天)发出预警,为人工或自动更新预留充足的操作窗口。
二、 系统核心设计思路
数据源输入: 获取需要监控的域名列表。
状态探测: 检查域名是否启用了HTTPS。
信息提取: 对于启用了HTTPS的域名,获取其SSL证书的过期时间。
状态判定与告警: 计算剩余天数,若低于阈值则触发告警(邮件、钉钉、企业微信等)。
自动更新(可选): 对于支持API的云平台或证书管理服务,可集成自动更新逻辑。
三、 分步实现与代码示例
下面我们分模块实现核心功能。假设我们的域名列表存储在一个简单的文本文件 domains.txt 中,每行一个域名。
1. 获取域名列表(数据源)
# 从文件读取域名列表defload_domains(file_path='domains.txt'):withopen(file_path, 'r') asf:# 去除空行和前后空格domains = [line.strip() forlineinfifline.strip()]returndomains# 示例:domains.txt 内容# www.example.com# api.example.com# blog.example.org
2. 检查HTTPS支持我们使用 requests 库尝试访问域名的HTTPS端口,并设置合理的超时时间。
importrequestsfromrequests.exceptionsimportSSLError, ConnectionError, Timeoutdefcheck_https_support(domain, timeout=5):url = f'https://{domain}'try:# 仅发起HEAD请求,节省带宽,只关心连接和证书状态response = requests.head(url, timeout=timeout, allow_redirects=True)# 如果请求成功(包括重定向),则认为支持HTTPSreturnTrue, NoneexceptSSLErrorase:# SSL错误,可能证书无效或不匹配returnFalse, f"SSL Error: {e}"except (ConnectionError, Timeout) ase:# 连接超时或错误,可能未启用HTTPS或网络不通returnFalse, f"Connection Error: {e}"exceptExceptionase:returnFalse, f"Unexpected Error: {e}"# 使用示例domain = 'www.example.com'https_enabled, error_msg = check_https_support(domain)ifhttps_enabled:print(f"{domain} 支持HTTPS")else:print(f"{domain} HTTPS检查失败: {error_msg}")
3. 获取证书过期时间这里我们使用 ssl 标准库和 cryptography 库(比 pyopenssl 更现代、维护更好)来获取证书信息。
importsslimportsocketfromdatetimeimportdatetimefromcryptographyimportx509fromcryptography.hazmat.backendsimportdefault_backenddefget_cert_expiry_date(domain, port=443):try:# 创建原始socket连接sock = socket.create_connection((domain, port), timeout=10)context = ssl.create_default_context()# 包装socket为SSL socketssock = context.wrap_socket(sock, server_hostname=domain)# 获取二进制格式的证书cert_bin = ssock.getpeercert(binary_form=True)ssock.close()sock.close()# 使用cryptography解析证书cert = x509.load_der_x509_certificate(cert_bin, default_backend())# 获取证书过期时间expiry_date = cert.not_valid_afterreturnTrue, expiry_dateexceptExceptionase:returnFalse, f"Failed to get certificate for {domain}: {e}"defget_days_until_expiry(expiry_date):"""计算证书剩余有效天数"""now = datetime.utcnow() # 证书时间通常是UTCdelta = expiry_date-nowreturndelta.days# 使用示例domain = 'www.example.com'success, expiry = get_cert_expiry_date(domain)ifsuccess:days_left = get_days_until_expiry(expiry)print(f"域名 {domain} 的证书将于 {expiry} 过期,剩余 {days_left} 天。")else:print(expiry) # 输出错误信息
4. 整合与定时任务将上述模块整合,并加入告警逻辑。我们可以使用 schedule 库或操作系统级的 cron/Task Scheduler 来定时执行。
importjsonfromdatetimeimportdatetimedefmonitor_domains(domain_list, alert_threshold_days=30):results = []alert_list = []fordomainindomain_list:print(f"正在检查 {domain}...")# 检查HTTPShttps_ok, _ = check_https_support(domain)ifnothttps_ok:results.append({'domain': domain, 'https': False, 'error': 'HTTPS not supported or unreachable'})continue# 获取证书信息cert_ok, cert_data = get_cert_expiry_date(domain)ifnotcert_ok:results.append({'domain': domain, 'https': True, 'error': cert_data})continue# 计算剩余天数days_left = get_days_until_expiry(cert_data)record = {'domain': domain,'https': True,'expiry_date': cert_data.isoformat(),'days_left': days_left,'check_time': datetime.utcnow().isoformat() }results.append(record)# 触发告警判断ifdays_left<= alert_threshold_days:alert_list.append(record)# 此处可以调用发送告警的函数,例如 send_alert(record)print(f"【告警】域名 {domain} 证书剩余 {days_left} 天即将过期!")# 将本次监控结果保存为JSON日志文件log_filename = f"cert_monitor_{datetime.utcnow().strftime('%Y%m%d_%H%M%S')}.json"withopen(log_filename, 'w') asf:json.dump({'check_time': datetime.utcnow().isoformat(), 'results': results}, f, indent=2)returnresults, alert_list# 主程序if__name__ == '__main__':domains = load_domains('domains.txt')monitor_domains(domains, alert_threshold_days=30)
5. 进阶:集成自动更新(以Let’s Encrypt为例)对于使用 Let‘s Encrypt 免费证书的场景,可以集成 certbot 的自动化命令。
importsubprocessimportloggingdefauto_renew_certbot(domain):""" 使用certbot自动续签指定域名的证书。 前提:服务器上已安装并配置好certbot。 """try:# 使用--dry-run先测试,实际运行时移除--dry-runcmd = ['sudo', 'certbot', 'renew', '--cert-name', domain, '--dry-run']result = subprocess.run(cmd, capture_output=True, text=True, timeout=300)ifresult.returncode == 0:logging.info(f"证书续签模拟成功 for {domain}")# 模拟成功,实际续签(移除--dry-run)# cmd = ['sudo', 'certbot', 'renew', '--cert-name', domain]# subprocess.run(cmd, check=True)returnTrue, "Renewal simulation successful"else:logging.error(f"证书续签模拟失败 for {domain}: {result.stderr}")returnFalse, result.stderrexceptsubprocess.TimeoutExpired:logging.error(f"证书续签命令超时 for {domain}")returnFalse, "Command timeout"exceptExceptionase:logging.error(f"证书续签过程异常 for {domain}: {e}")returnFalse, str(e)# 可以在告警逻辑中,对满足条件的域名调用自动更新# if days_left <= 7: # 剩余7天时尝试自动更新# auto_renew_certbot(domain)
四、 部署与优化建议
安全存储密钥: 如果使用云API,务必通过环境变量或密钥管理服务存储AccessKey,切勿硬编码在代码中。
结果持久化: 将监控结果存入数据库(如SQLite、MySQL)或时序数据库(如InfluxDB),便于历史查询和趋势分析。
多样化告警: 除了控制台打印,集成邮件、钉钉机器人、企业微信、Slack等告警渠道。
容器化部署: 使用Docker将监控程序容器化,便于在任意环境部署和扩展。
监控程序本身: 别忘了为这个监控脚本也设置一个健康检查,确保它自己一直在正常运行。
通过以上约150行代码的核心框架,构建了一个HTTPS证书过期监控系统的雏形。它麻雀虽小,五脏俱全,涵盖了从数据采集、状态判断到告警触发的基本流程。你可以根据实际运维环境,轻松扩展数据源(从云API、CMDB获取域名)、丰富告警策略、对接不同的证书更新接口。

https://ima.qq.com/wiki/?shareId=f2628818f0874da17b71ffa0e5e8408114e7dbad46f1745bbd1cc1365277631c
