外观
SSL 证书过期检测
约 1551 字大约 5 分钟
ShellSSL证书监控运维工具
2026-01-16
批量检测 SSL 证书有效期,支持多域名检测、自定义告警阈值,输出详细的证书信息报告。
功能特性
| 特性 | 说明 |
|---|---|
| 🔍 批量检测 | 支持同时检测多个域名的 SSL 证书 |
| 📋 配置文件 | 支持从文件读取域名列表 |
| ⚠️ 告警阈值 | 可配置警告和严重两级告警阈值 |
| 🎨 彩色输出 | 不同状态彩色区分 |
| 📊 详细报告 | 显示颁发者、有效期等详细信息 |
快速开始
# 下载脚本
curl -sL https://script.merma.cn/scripts/shell/MonitorScript/ssl_cert_check.sh -o ssl_cert_check.sh
chmod +x ssl_cert_check.sh
# 检测单个域名
./ssl_cert_check.sh example.com
# 检测多个域名
./ssl_cert_check.sh example.com google.com github.com
# 检测非标准端口
./ssl_cert_check.sh example.com:8443
# 自定义告警阈值
./ssl_cert_check.sh -w 60 -c 14 example.com
# 从文件批量检测
./ssl_cert_check.sh -f domains.txt配置文件格式
创建 domains.txt 文件,每行一个检测目标:
# 格式: 域名 或 域名:端口
example.com
google.com
api.example.com:8443演示效果
================================================================================
SSL 证书过期检测
2026-01-16 10:30:00
================================================================================
检测目标: 4 个域名
告警阈值: 警告 30 天, 严重 7 天
超时时间: 10 秒
--------------------------------------------------------------------------------
域名 剩余有效期 状态
--------------------------------------------------------------------------------
github.com:443 256 天 ✓ 正常
google.com:443 68 天 ✓ 正常
example.com:443 25 天 ⚠ 警告
expiring.example.com:443 5 天 ⚠ 严重
--------------------------------------------------------------------------------
================================================================================
检测结果汇总
================================================================================
检测总数: 4
正常: 2
警告 (≤30天): 1
严重 (≤7天): 1
失败/过期: 0
================================================================================参数说明
| 参数 | 说明 |
|---|---|
-f <file> | 从文件读取域名列表 |
-p <port> | 指定默认端口,默认 443 |
-w <days> | 警告阈值天数,默认 30 天 |
-c <days> | 严重阈值天数,默认 7 天 |
-t <seconds> | 连接超时时间,默认 10 秒 |
-h | 显示帮助信息 |
退出码
| 退出码 | 含义 |
|---|---|
| 0 | 所有证书正常 |
| 1 | 存在证书已过期或获取失败 |
| 2 | 存在证书即将过期(严重) |
| 3 | 存在证书即将过期(警告) |
环境要求
- 操作系统: Linux (RockyLinux, Ubuntu, Debian, CentOS)
- Shell: Bash 4.0+
- 依赖: openssl, coreutils
脚本源码
点击展开查看完整源码
#!/bin/bash
#
# SSL 证书过期检测脚本
# 功能: 批量检测 SSL 证书有效期,支持多域名检测和过期告警
# 兼容: RockyLinux, Ubuntu, Debian, CentOS 等主流 Linux 发行版
#
set -o pipefail
# 颜色定义
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[0;33m'
BLUE='\033[0;34m'
CYAN='\033[0;36m'
NC='\033[0m'
# 默认配置
TIMEOUT=10
WARN_DAYS=30
CRIT_DAYS=7
DOMAINS_FILE=""
PORT=443
TOTAL_COUNT=0
VALID_COUNT=0
WARN_COUNT=0
CRIT_COUNT=0
ERROR_COUNT=0
RESULTS=()
# 使用说明
usage() {
cat << EOF
用法: $0 [选项] [域名...]
SSL 证书过期检测脚本 - 批量检测 SSL 证书有效期
选项:
-f <file> 从文件读取域名列表
-p <port> 指定端口 (默认: 443)
-w <days> 警告阈值天数 (默认: 30)
-c <days> 严重阈值天数 (默认: 7)
-t <seconds> 连接超时时间 (默认: 10)
-h 显示此帮助信息
示例:
$0 example.com
$0 example.com google.com github.com
$0 -p 8443 example.com
$0 -w 60 -c 14 example.com
$0 -f domains.txt
退出码:
0 - 所有证书有效且未达到警告阈值
1 - 存在证书已过期或获取失败
2 - 存在证书即将过期(严重)
3 - 存在证书即将过期(警告)
EOF
exit 0
}
die() { echo -e "${RED}错误: $1${NC}" >&2; exit 1; }
check_dependencies() {
command -v openssl &> /dev/null || die "未找到 openssl 命令,请先安装 openssl"
}
print_line() { echo "================================================================================"; }
print_dash_line() { echo "--------------------------------------------------------------------------------"; }
print_header() {
print_line
echo -e " ${CYAN}SSL 证书过期检测${NC}"
echo " $(date '+%Y-%m-%d %H:%M:%S')"
print_line
echo ""
}
get_cert_info() {
local domain=$1 port=$2
local cert_info issuer subject not_before not_after status expire_ts now_ts days_left
cert_info=$(echo | timeout "$TIMEOUT" openssl s_client -servername "$domain" -connect "${domain}:${port}" 2>/dev/null)
[ -z "$cert_info" ] || ! echo "$cert_info" | grep -q "BEGIN CERTIFICATE" && { echo "||||||ERROR"; return 1; }
local cert_text=$(echo "$cert_info" | openssl x509 -noout -dates -issuer -subject 2>/dev/null)
[ -z "$cert_text" ] && { echo "||||||ERROR"; return 1; }
issuer=$(echo "$cert_text" | grep "^issuer=" | sed 's/^issuer=//' | sed 's/.*CN *= *//' | cut -d',' -f1 | cut -d'/' -f1)
subject=$(echo "$cert_text" | grep "^subject=" | sed 's/^subject=//' | sed 's/.*CN *= *//' | cut -d',' -f1 | cut -d'/' -f1)
not_before=$(echo "$cert_text" | grep "^notBefore=" | sed 's/^notBefore=//')
not_after=$(echo "$cert_text" | grep "^notAfter=" | sed 's/^notAfter=//')
expire_ts=$(date -d "$not_after" +%s 2>/dev/null)
[ -z "$expire_ts" ] && expire_ts=$(date -j -f "%b %d %H:%M:%S %Y %Z" "$not_after" +%s 2>/dev/null)
[ -z "$expire_ts" ] && { echo "||||||PARSE_ERROR"; return 1; }
now_ts=$(date +%s)
days_left=$(( (expire_ts - now_ts) / 86400 ))
if [ "$days_left" -lt 0 ]; then status="EXPIRED"
elif [ "$days_left" -le "$CRIT_DAYS" ]; then status="CRITICAL"
elif [ "$days_left" -le "$WARN_DAYS" ]; then status="WARNING"
else status="OK"; fi
echo "${issuer}|${subject}|${not_before}|${not_after}|${days_left}|${status}"
}
check_domain() {
local input=$1 domain port result issuer subject not_before not_after days_left status
if [[ "$input" == *":"* ]]; then
domain=$(echo "$input" | cut -d':' -f1); port=$(echo "$input" | cut -d':' -f2)
else
domain="$input"; port="$PORT"
fi
((TOTAL_COUNT++))
result=$(get_cert_info "$domain" "$port")
IFS='|' read -r issuer subject not_before not_after days_left status <<< "$result"
case "$status" in
OK) ((VALID_COUNT++)); printf " %-35s %6d 天 ${GREEN}✓ 正常${NC}\n" "${domain}:${port}" "$days_left" ;;
WARNING) ((WARN_COUNT++)); printf " %-35s %6d 天 ${YELLOW}⚠ 警告${NC}\n" "${domain}:${port}" "$days_left" ;;
CRITICAL) ((CRIT_COUNT++)); printf " %-35s %6d 天 ${RED}⚠ 严重${NC}\n" "${domain}:${port}" "$days_left" ;;
EXPIRED) ((ERROR_COUNT++)); printf " %-35s %6d 天 ${RED}✗ 已过期${NC}\n" "${domain}:${port}" "$days_left" ;;
*) ((ERROR_COUNT++)); printf " %-35s %6s ${RED}✗ 获取失败${NC}\n" "${domain}:${port}" "-" ;;
esac
RESULTS+=("${domain}|${port}|${issuer}|${subject}|${not_before}|${not_after}|${days_left}|${status}")
}
check_from_file() {
local file=$1 domains=()
[ ! -f "$file" ] && die "文件 '$file' 不存在"
while IFS= read -r line || [ -n "$line" ]; do
line=$(echo "$line" | sed 's/#.*//' | xargs); [ -z "$line" ] && continue
domains+=("$line")
done < "$file"
[ ${#domains[@]} -eq 0 ] && die "配置文件中没有有效的域名"
echo "检测目标: ${#domains[@]} 个域名"
echo "告警阈值: 警告 ${WARN_DAYS} 天, 严重 ${CRIT_DAYS} 天"
echo "超时时间: ${TIMEOUT} 秒"; echo ""
print_dash_line; printf " %-35s %10s %s\n" "域名" "剩余有效期" "状态"; print_dash_line
for domain in "${domains[@]}"; do check_domain "$domain"; done
print_dash_line
}
print_summary() {
echo ""; print_line
echo -e " ${CYAN}检测结果汇总${NC}"
print_line
echo " 检测总数: $TOTAL_COUNT"
echo -e " 正常: ${GREEN}$VALID_COUNT${NC}"
echo -e " 警告 (≤${WARN_DAYS}天): ${YELLOW}$WARN_COUNT${NC}"
echo -e " 严重 (≤${CRIT_DAYS}天): ${RED}$CRIT_COUNT${NC}"
echo -e " 失败/过期: ${RED}$ERROR_COUNT${NC}"
print_line
}
get_exit_code() {
[ "$ERROR_COUNT" -gt 0 ] && echo 1 && return
[ "$CRIT_COUNT" -gt 0 ] && echo 2 && return
[ "$WARN_COUNT" -gt 0 ] && echo 3 && return
echo 0
}
while getopts "f:p:w:c:t:h" opt; do
case $opt in
f) DOMAINS_FILE="$OPTARG" ;;
p) PORT="$OPTARG"; [[ ! "$PORT" =~ ^[0-9]+$ ]] || [ "$PORT" -lt 1 ] || [ "$PORT" -gt 65535 ] && die "无效的端口号: $PORT" ;;
w) WARN_DAYS="$OPTARG"; [[ ! "$WARN_DAYS" =~ ^[0-9]+$ ]] && die "警告阈值必须是正整数" ;;
c) CRIT_DAYS="$OPTARG"; [[ ! "$CRIT_DAYS" =~ ^[0-9]+$ ]] && die "严重阈值必须是正整数" ;;
t) TIMEOUT="$OPTARG"; [[ ! "$TIMEOUT" =~ ^[0-9]+$ ]] && die "超时时间必须是正整数" ;;
h) usage ;; *) usage ;;
esac
done
shift $((OPTIND - 1))
check_dependencies
[ "$CRIT_DAYS" -gt "$WARN_DAYS" ] && die "严重阈值不能大于警告阈值"
print_header
if [ -n "$DOMAINS_FILE" ]; then
check_from_file "$DOMAINS_FILE"
elif [ $# -ge 1 ]; then
echo "检测目标: $# 个域名"
echo "告警阈值: 警告 ${WARN_DAYS} 天, 严重 ${CRIT_DAYS} 天"
echo "超时时间: ${TIMEOUT} 秒"; echo ""
print_dash_line; printf " %-35s %10s %s\n" "域名" "剩余有效期" "状态"; print_dash_line
for domain in "$@"; do check_domain "$domain"; done
print_dash_line
else
usage
fi
print_summary
exit "$(get_exit_code)"