
在HTTPS已成为标配的今天,一个即将过期的SSL/TLS证书轻则导致浏览器警告,重则直接中断服务,影响用户体验和业务连续性。手动管理成百上千个域名的证书到期时间,无异于一场噩梦。今天,我们就用Python打造一个轻量级的自动化监控与更新系统,让证书管理从此无忧。
一、 为什么需要自动化监控?
想象一下,凌晨三点,你的核心业务网站因为证书过期而无法访问,警报响起,团队被紧急唤醒… 这并非危言耸听,而是许多运维团队曾经历或担忧的场景。手动记录、人工巡检的方式在域名数量激增、多云混合部署的复杂环境下,极易出现疏漏。自动化监控的核心价值在于 “防患于未然” ,通过程序定期检查,在证书到期前足够长的时间(如30天)发出预警,为人工或自动更新预留充足的操作窗口。
二、 系统核心设计思路
三、 分步实现与代码示例
下面我们分模块实现核心功能。假设我们的域名列表存储在一个简单的文本文件 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.org2. 检查HTTPS支持我们使用 requests 库尝试访问域名的HTTPS端口,并设置合理的超时时间。
importrequests
fromrequests.exceptionsimportSSLError, ConnectionError, Timeout
defcheck_https_support(domain, timeout=5):
url = f'https://{domain}'
try:
# 仅发起HEAD请求,节省带宽,只关心连接和证书状态
response = requests.head(url, timeout=timeout, allow_redirects=True)
# 如果请求成功(包括重定向),则认为支持HTTPS
returnTrue, None
exceptSSLErrorase:
# 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 更现代、维护更好)来获取证书信息。
importssl
importsocket
fromdatetimeimportdatetime
fromcryptographyimportx509
fromcryptography.hazmat.backendsimportdefault_backend
defget_cert_expiry_date(domain, port=443):
try:
# 创建原始socket连接
sock = socket.create_connection((domain, port), timeout=10)
context = ssl.create_default_context()
# 包装socket为SSL socket
ssock = 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_after
returnTrue, expiry_date
exceptExceptionase:
returnFalse, f"Failed to get certificate for {domain}: {e}"
defget_days_until_expiry(expiry_date):
"""计算证书剩余有效天数"""
now = datetime.utcnow() # 证书时间通常是UTC
delta = expiry_date-now
returndelta.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 来定时执行。
importjson
fromdatetimeimportdatetime
defmonitor_domains(domain_list, alert_threshold_days=30):
results = []
alert_list = []
fordomainindomain_list:
print(f"正在检查 {domain}...")
# 检查HTTPS
https_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 的自动化命令。
importsubprocess
importlogging
defauto_renew_certbot(domain):
"""
使用certbot自动续签指定域名的证书。
前提:服务器上已安装并配置好certbot。
"""
try:
# 使用--dry-run先测试,实际运行时移除--dry-run
cmd = ['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.stderr
exceptsubprocess.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)四、 部署与优化建议
AccessKey,切勿硬编码在代码中。通过以上约150行代码的核心框架,构建了一个HTTPS证书过期监控系统的雏形。它麻雀虽小,五脏俱全,涵盖了从数据采集、状态判断到告警触发的基本流程。你可以根据实际运维环境,轻松扩展数据源(从云API、CMDB获取域名)、丰富告警策略、对接不同的证书更新接口。
如果你觉得这篇文章有用,欢迎点赞、转发、收藏、留言、推荐❤!
本文分享自 Nicholas与Pypi 微信公众号,前往查看
如有侵权,请联系 cloudcommunity@tencent.com 删除。
本文参与 腾讯云自媒体同步曝光计划 ,欢迎热爱写作的你一起参与!