尧图网站建设 尧图网络
  • 首页
  • 关于我们
  • 服务项目
  • 案例展示
  • 建站流程
  • 资讯中心
  • 联系我们
首页/资讯中心/详情

shell脚本监控ssl证书到期时间

shell脚本监控ssl证书到期时间
📅 发布时间:2026/6/18 13:57:15

一、需求

说明:

  (1)读取域名列表文件。

  (2)获取域名到期时间,进行告警后邮件提醒。

#!/bin/bash
## 第1步  配置文件
# 颜色定义
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
PURPLE='\033[0;35m'
CYAN='\033[0;36m'
NC='\033[0m' # No Color
# 告警阈值(天)
WARNING_DAYS=20
CRITICAL_DAYS=10
ALERT_DAYS=10
# 超时设置(秒)
CONNECT_TIMEOUT=5
# 邮件配置
EMAIL_ENABLED=false
EMAIL_TO="xxxxxxx@qq.com"     # 目标邮箱地址
EMAIL_COMMAND="mailx"             # 邮件发送命令
EMAIL_SUBJECT_PREFIX="SSL证书检查报告" # 邮件主题前缀
# 全局变量
ALERT_DOMAINS=()  # 存储需要告警的域名
ALL_CERTIFICATES=()  # 存储所有证书检查结果
DOMAIN_LIST_FILE="domains.txt"# 日志函数
log() {local level=$1local message=$2local timestamp=$(date '+%Y-%m-%d %H:%M:%S')echo -e "[$timestamp] [$level] $message"
}
## 第4步 
# 显示使用帮助
show_usage() {echo -e "${GREEN}SSL证书到期监控脚本${NC}"echo "使用方法: $0 [选项]"echo ""echo "选项:"echo "  -f, --file <文件>    从文件读取域名列表 (默认: $DOMAIN_LIST_FILE)"echo "  -d, --detailed       显示详细证书信息"echo "  -q, --quick          快速检查模式(默认)"echo "  -t, --timeout <秒>   设置连接超时时间(默认: ${CONNECT_TIMEOUT}秒)"echo "  -m, --mail           启用邮件发送功能"echo "  -h, --help           显示此帮助信息"echo ""echo "示例:"echo "  $0 -f domains.txt        # 从文件检查"echo "  $0 -f domains.txt -d     # 详细模式检查"echo "  $0 -f domains.txt -m     # 检查并发送邮件"echo ""echo "域名列表文件格式:"echo "  # 注释行"echo "  example.com"echo "  google.com"echo "  mysite.com:8443"
}
## 第9步 
# 带超时的SSL连接检查
ssl_connect_with_timeout() {local domain=$1local port=$2# 使用timeout命令设置超时if command -v timeout >/dev/null 2>&1; then# 如果有timeout命令timeout $CONNECT_TIMEOUT bash -c "echo | openssl s_client -connect \"$domain:$port\" -servername \"$domain\" 2>/dev/null" 2>/dev/nullelse# 如果没有timeout命令,使用其他方法实现超时local pid# local result# 在后台执行openssl命令(echo | openssl s_client -connect "$domain:$port" -servername "$domain" 2>/dev/null) &pid=$!# 等待进程结束,最多等待CONNECT_TIMEOUT秒local count=0while [ $count -lt $CONNECT_TIMEOUT ]; doif ! kill -0 $pid 2>/dev/null; then# 进程已经结束breakfisleep 1count=$((count + 1))done# 如果进程还在运行,杀死它if kill -0 $pid 2>/dev/null; thenkill $pid 2>/dev/nullwait $pid 2>/dev/nullreturn 124  # 超时返回码else# 获取命令执行结果wait $pidreturn $?fifi
}## 第8步 
# 获取SSL证书剩余天数
get_ssl_days_remaining() {local domain=$1local port=${2:-443}# 获取证书到期时间(带超时)local not_afternot_after=$(ssl_connect_with_timeout "$domain" "$port" | openssl x509 -noout -enddate 2>/dev/null | cut -d= -f2)local ssl_result=$?if [ $ssl_result -eq 124 ]; thenecho "TIMEOUT"return 1elif [ $ssl_result -ne 0 ] || [ -z "$not_after" ]; thenecho "ERROR"return 1fi# 转换为时间戳local expiry_timestampexpiry_timestamp=$(date -d "$not_after" +%s 2>/dev/null)if [ $? -ne 0 ]; thenecho "ERROR"return 1filocal current_timestamp=$(date +%s)local days_remaining=$(( (expiry_timestamp - current_timestamp) / 86400 ))echo "$days_remaining"
}## 第7步 
# 快速检查模式(只检查剩余天数)
quick_check_domain() {local domain=$1local port=${2:-443}local days_remaining=$(get_ssl_days_remaining "$domain" "$port")if [ "$days_remaining" == "TIMEOUT" ]; thenecho -e "${RED}⏰ $domain: 连接超时 (${CONNECT_TIMEOUT}秒)${NC}"ALERT_DOMAINS+=("${RED}⏰ $domain: 连接超时${NC}")ALL_CERTIFICATES+=("${RED}⏰ $domain: 连接超时${NC}")elif [ "$days_remaining" == "ERROR" ]; thenecho -e "${RED}❌ $domain: 检查失败${NC}"ALERT_DOMAINS+=("${RED}❌ $domain: 检查失败${NC}")ALL_CERTIFICATES+=("${RED}❌ $domain: 检查失败${NC}")elif [ "$days_remaining" -lt 0 ]; thenecho -e "${RED}🔴 $domain: 已过期 $(( -days_remaining )) 天!${NC}"ALERT_DOMAINS+=("${RED}🔴 $domain: 已过期 $(( -days_remaining )) 天${NC}")ALL_CERTIFICATES+=("${RED}🔴 $domain: 已过期 $(( -days_remaining )) 天${NC}")elif [ "$days_remaining" -le "$ALERT_DAYS" ]; thenecho -e "${RED}🔴 $domain: 剩余 $days_remaining 天 (需处理)${NC}"ALERT_DOMAINS+=("${RED}🔴 $domain: 剩余 $days_remaining 天${NC}")ALL_CERTIFICATES+=("${RED}🔴 $domain: 剩余 $days_remaining 天${NC}")elif [ "$days_remaining" -le "$WARNING_DAYS" ]; thenecho -e "${YELLOW}🟡 $domain: 剩余 $days_remaining 天${NC}"ALL_CERTIFICATES+=("${YELLOW}🟡 $domain: 剩余 $days_remaining 天${NC}")elseecho -e "${GREEN}✅ $domain: 剩余 $days_remaining 天${NC}"ALL_CERTIFICATES+=("${GREEN}✅ $domain: 剩余 $days_remaining 天${NC}")fi
}## 第6步 
# 从文件读取域名列表
read_domains_from_file() {local file_path=$1local domains=()if [ ! -f "$file_path" ]; thenecho -e "${RED}错误: 域名列表文件 $file_path 不存在${NC}" >&2return 1fiwhile IFS= read -r line || [ -n "$line" ]; do# 跳过空行和注释行line=$(echo "$line" | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')[[ -z "$line" || "$line" =~ ^[[:space:]]*# ]] && continuedomains+=("$line")done < "$file_path"printf '%s\n' "${domains[@]}"
}## 第5步 
# 批量检查域名
check_multiple_domains() {local mode=$1local file_path=$2local domains=()# 从文件读取域名if [ -n "$file_path" ] && [ -f "$file_path" ]; thenmapfile -t domains < <(read_domains_from_file "$file_path")if [ $? -ne 0 ]; thenecho -e "${RED}错误: 无法读取域名列表文件${NC}"return 1fielseecho -e "${RED}错误: 域名列表文件不存在${NC}"return 1fiecho -e "${BLUE}开始检查 ${#domains[@]} 个域名的SSL证书...${NC}"echo "告警阈值: ${ALERT_DAYS}天"echo "超时设置: ${CONNECT_TIMEOUT}秒"echo "模式: $mode"echo "=========================================="for domain_entry in "${domains[@]}"; do# 支持 domain:port 格式if [[ "$domain_entry" == *":"* ]]; thendomain="${domain_entry%:*}"port="${domain_entry#*:}"elsedomain="$domain_entry"port="443"fi# 清理域名domain=$(echo "$domain" | sed -e 's|^https://||' -e 's|^http://||' -e 's|/.*$||')case "$mode" in"detailed")get_ssl_cert_info "$domain" "$port" "$days_remaining";;"quick"|*)quick_check_domain "$domain" "$port";;esacdone
}## 第10步 
# 显示告警汇总
show_alerts() {if [ ${#ALERT_DOMAINS[@]} -eq 0 ]; thenecho -e "\n${GREEN}🎉 所有证书状态正常,无需处理!${NC}"returnfiecho -e "\n${RED}🚨 证书告警汇总 (剩余 ≤ ${ALERT_DAYS} 天/连接问题):${NC}"echo "=========================================="local has_critical_alerts=0for alert in "${ALERT_DOMAINS[@]}"; doif [[ "$alert" == *"剩余 $ALERT_DAYS"* ]] || [[ "$alert" == *"已过期"* ]] || [[ "$alert" == *"检查失败"* ]] || [[ "$alert" == *"连接超时"* ]]; thenecho -e "$alert"has_critical_alerts=1fidoneif [ $has_critical_alerts -eq 0 ]; thenecho -e "${GREEN}暂无紧急告警${NC}"fiecho "=========================================="
}## 第11步 
# 检查是否有需要告警的证书
has_alert_certificates() {for alert in "${ALERT_DOMAINS[@]}"; doreturn 0donereturn 1  # 没有需要告警的证书
}## 第13步
# 生成邮件内容
generate_email_content() {local email_content=""email_content+="SSL证书检查报告\n检查时间: $(date)\n\n"# 显示所有证书状态if [ ${#ALL_CERTIFICATES[@]} -gt 0 ]; thenemail_content+="📋 所有证书检查结果:\n"email_content+="==========================================\n"for cert in "${ALL_CERTIFICATES[@]}"; doemail_content+="$cert\n"doneemail_content+="==========================================\n\n"fi# 显示告警证书email_content+="🚨 证书告警汇总 (剩余 ≤ ${ALERT_DAYS} 天/连接问题)"    if [ ${#ALERT_DOMAINS[@]} -eq 0 ]; thenemail_content+="\n${GREEN}🎉 所有证书状态正常,无需处理!${NC}"elif [ ${#ALERT_DOMAINS[@]} -gt 0 ]; thenemail_content+="\n==========================================\n"local has_critical_alerts=0for alert in "${ALERT_DOMAINS[@]}"; doemail_content+="$alert\n" has_critical_alerts=1donefi  echo -e "$email_content"
}## 第12步
# 发送邮件
send_email() {local subject="${EMAIL_SUBJECT_PREFIX} - $(date '+%Y-%m-%d %H:%M')"local contentcontent=$(generate_email_content)#echo "$content"echo "--------------------------------------------------1111---------------------------------------------------- "echo "$content"echo "--------------------------------------------------1111---------------------------------------------------- "# 检查是否启用邮件发送if [ "$EMAIL_ENABLED" != "true" ]; thenecho -e "${YELLOW}邮件发送未启用${NC}"return 0fi# 检查邮件命令是否存在if ! command -v "$EMAIL_COMMAND" >/dev/null 2>&1; thenecho -e "${RED}错误: 邮件命令 '$EMAIL_COMMAND' 不存在${NC}"return 1fi# 发送邮件echo -e "$content" | sed 's/\x1b\[[0-9;]*m//g' | $EMAIL_COMMAND -s "$subject" "$EMAIL_TO"local mail_result=$?if [ $mail_result -eq 0 ]; thenecho -e "${GREEN}邮件发送成功${NC}"return 0elseecho -e "${RED}邮件发送失败 (返回码: $mail_result)${NC}"return 1fi
}## 第3步 
# 主函数
main() {local mode="quick"local file_path=""# 处理命令行参数while [[ $# -gt 0 ]]; docase $1 in-f|--file)if [ -n "$2" ]; thenfile_path="$2"shift 2elseecho -e "${RED}错误: --file 需要参数${NC}"show_usageexit 1fi;;-q|--quick)mode="quick"shift;;-t|--timeout)if [ -n "$2" ] && [[ "$2" =~ ^[0-9]+$ ]]; thenCONNECT_TIMEOUT="$2"shift 2elseecho -e "${RED}错误: --timeout 需要数字参数${NC}"show_usageexit 1fi;;-m|--mail)EMAIL_ENABLED=trueshift;;-h|--help)show_usageexit 0;;-*)echo -e "${RED}错误: 未知选项 $1${NC}"show_usageexit 1;;*)echo -e "${RED}错误: 不支持的参数 $1${NC}"show_usageexit 1;;esacdone# 如果没有指定文件,使用默认文件if [ -z "$file_path" ]; thenif [ -f "$DOMAIN_LIST_FILE" ]; thenfile_path="$DOMAIN_LIST_FILE"elseecho -e "${RED}错误: 未指定域名文件且默认文件 $DOMAIN_LIST_FILE 不存在${NC}"show_usageexit 1fifi# 执行检查check_multiple_domains "$mode" "$file_path"# 显示告警汇总show_alerts# 发送邮件(如果启用且有需要告警的证书)if [ "$EMAIL_ENABLED" == "true" ]; thenif has_alert_certificates; thenecho -e "\n${BLUE}检测到需要告警的证书,正在发送邮件...${NC}"send_emailelseecho -e "\n${GREEN}无需要告警的证书,无需发送邮件${NC}"fifi}## 第2步 
# 执行主函数
main "$@"

然后执行命令

./ssl_alert.sh -f domains.txt -m

在crontab中添加定时任务,进行定期执行

相关新闻

  • AI如何通过卫星图像识别刺猬栖息地
  • LeetCode热题100-75、跳跃游戏
  • rust 模块和引用

最新新闻

  • 如何免费搭建个人专属媒体中心?Jellyfin完整使用指南
  • SST39VF/LF并行NOR Flash在嵌入式低功耗高可靠系统中的应用与实战
  • UniMark:自回归图像生成模型中的统一自适应多比特水印技术解析
  • 2026恩施防水补漏靠谱服务商盘点:屋面/厨卫/外墙/地下室渗水维修详解,适配武陵山区清江沿岸峡谷雾天山体渗水防潮甄选指南 - 宅安选房屋修缮
  • 计算机Django毕设实战-基于 Django 的校园智能点餐服务系统的设计与实现 基于 Django 的餐饮在线点餐管理系统【完整源码+LW+部署说明+演示视频,全bao一条龙等】
  • 解锁Office潜能:用Office RibbonX Editor打造个性化功能区界面

日新闻

  • 5分钟掌握Python进化算法:Geatpy高性能优化工具完全指南
  • Microchip 24AA044 EEPROM选型与应用全指南:从参数解析到实战编程
  • 华为的鸿蒙到底有多牛?为什么称作遥遥领先?

周新闻

  • 3步解锁iOS设备:applera1n激活锁绕过完全指南
  • 39 2026 人工智能证书终极盘点,普通人选 AI 证书可以从这些方向入手
  • Redis 暴露公网有多危险?从端口检查到补救步骤

月新闻

  • 【总结】入门篇:50句话让你记住架构核心概念
  • WeChatMsg技术方案解析:实现Mac微信数据自主管理的完整解决方案
  • WeChatMsg:革新性微信数据备份方案,打造你的专属数字记忆库

关于尧图

  • 公司简介
  • 团队介绍
  • 企业文化
  • 荣誉资质

服务项目

  • 定制开发
  • 电商建站
  • UI 设计
  • 运维服务

快速链接

  • 案例展示
  • 建站流程
  • 常见问题
  • 资讯中心

联系方式

  • 📍北京市朝阳区互联网产业园 A 座 10 层
  • 📞400-888-8888
  • ✉️contact@rkmt.cn
  • 🕐周一至周日 9:00-21:00

© 2024 北京尧图网络科技有限公司 版权所有 | 京 ICP 备 XXXXXXXX 号