应该是24年,博客的SSL过期了,正好赶在了创业最忙的时候,懒得更新,一晃一年多过去了,今天用Deepseek帮助写了一个自动化更新SSL证书的脚本,真的好用,牛。
使用方法:
1、创建脚本文件,如pankuu-ssl-setup.sh,复制脚本内容;
2、设置执行权限:chmod +x pankuu-ssl-setup.sh;
3、运行脚本:sudo ./pankuu-ssl-setup.sh
脚本
#!/bin/bash
# pankuu-ssl-auto-setup.sh
# LNMP环境SSL证书自动配置脚本
set -e # 遇到错误立即退出
# 配置参数
DOMAIN="pankuu.com"
WWW_ROOT="/home/wwwroot/pankuu.com"
EMAIL="admin@${DOMAIN}"
LNMP_PATH="/usr/local/nginx" # LNMP默认安装路径
# 颜色输出
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m' # No Color
# 日志函数
log() {
echo -e "${GREEN}[$(date '+%Y-%m-%d %H:%M:%S')] $1${NC}"
}
error() {
echo -e "${RED}[ERROR] $1${NC}"
}
warning() {
echo -e "${YELLOW}[WARNING] $1${NC}"
}
# 检查是否以root运行
check_root() {
if [ "$EUID" -ne 0 ]; then
error "请使用sudo或以root用户运行此脚本"
exit 1
fi
}
# 检查LNMP环境
check_lnmp() {
log "检查LNMP环境..."
# 检查Nginx
if [ ! -f "${LNMP_PATH}/sbin/nginx" ]; then
error "未找到Nginx,请确认LNMP已正确安装"
exit 1
fi
# 检查网站目录
if [ ! -d "${WWW_ROOT}" ]; then
warning "网站目录不存在: ${WWW_ROOT}"
read -p "是否创建此目录? (y/n): " -n 1 -r
echo
if [[ $REPLY =~ ^[Yy]$ ]]; then
mkdir -p "${WWW_ROOT}"
chown -R www:www "${WWW_ROOT}"
else
exit 1
fi
fi
# 检查Nginx配置
if [ ! -f "${LNMP_PATH}/conf/vhost/${DOMAIN}.conf" ]; then
error "未找到域名配置文件: ${LNMP_PATH}/conf/vhost/${DOMAIN}.conf"
create_nginx_conf
fi
}
# 创建Nginx配置文件(如果需要)
create_nginx_conf() {
log "创建Nginx配置文件..."
cat > "${LNMP_PATH}/conf/vhost/${DOMAIN}.conf" << EOF
server {
listen 80;
server_name ${DOMAIN} www.${DOMAIN};
access_log /home/wwwlogs/${DOMAIN}.log;
error_log /home/wwwlogs/${DOMAIN}.error.log;
root ${WWW_ROOT};
index index.html index.htm index.php;
location / {
try_files \$uri \$uri/ =404;
}
location ~ \.php$ {
fastcgi_pass unix:/tmp/php-cgi.sock;
fastcgi_index index.php;
include fastcgi.conf;
}
location ~ /\.ht {
deny all;
}
}
EOF
log "Nginx配置文件已创建,请重启Nginx后继续"
echo "重启命令: ${LNMP_PATH}/sbin/nginx -s reload"
exit 1
}
# 安装Certbot
install_certbot() {
log "安装Certbot..."
apt update
apt install -y certbot python3-certbot-nginx
# 验证安装
if ! command -v certbot &> /dev/null; then
error "Certbot安装失败"
exit 1
fi
log "Certbot安装成功"
}
# 获取SSL证书
get_ssl_certificate() {
log "开始获取SSL证书..."
# 先停止Nginx,确保80端口可用(LNMP的certbot插件可能需要)
${LNMP_PATH}/sbin/nginx -s stop 2>/dev/null || true
# 获取证书(使用standalone模式)
certbot certonly --standalone \
-d ${DOMAIN} \
-d www.${DOMAIN} \
--email ${EMAIL} \
--agree-tos \
--non-interactive \
--force-renewal
# 启动Nginx
${LNMP_PATH}/sbin/nginx
# 检查证书是否获取成功
if [ -f "/etc/letsencrypt/live/${DOMAIN}/fullchain.pem" ]; then
log "✅ SSL证书获取成功"
log "证书位置: /etc/letsencrypt/live/${DOMAIN}/"
else
error "SSL证书获取失败"
exit 1
fi
}
# 配置Nginx SSL
configure_nginx_ssl() {
log "配置Nginx SSL..."
# 备份原配置文件
CONF_FILE="${LNMP_PATH}/conf/vhost/${DOMAIN}.conf"
BACKUP_FILE="${CONF_FILE}.backup.$(date +%Y%m%d%H%M%S)"
cp "${CONF_FILE}" "${BACKUP_FILE}"
log "配置文件已备份: ${BACKUP_FILE}"
# 创建SSL配置文件
cat > "${LNMP_PATH}/conf/ssl.conf" << 'EOF'
# SSL配置
ssl_session_timeout 1d;
ssl_session_cache shared:SSL:50m;
ssl_session_tickets off;
# 现代加密套件配置
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384;
ssl_prefer_server_ciphers off;
# HSTS (可选,启用后谨慎)
# add_header Strict-Transport-Security "max-age=63072000" always;
# OCSP Stapling
ssl_stapling on;
ssl_stapling_verify on;
resolver 8.8.8.8 1.1.1.1 valid=300s;
resolver_timeout 5s;
EOF
# 更新域名配置文件
cat > "${CONF_FILE}" << EOF
# HTTP 重定向到 HTTPS
server {
listen 80;
server_name ${DOMAIN} www.${DOMAIN};
# 用于certbot续期验证
location ^~ /.well-known/acme-challenge/ {
alias ${WWW_ROOT}/.well-known/acme-challenge/;
try_files \$uri =404;
}
location / {
return 301 https://\$host\$request_uri;
}
access_log /home/wwwlogs/${DOMAIN}.http.log;
}
# HTTPS 服务器
server {
listen 443 ssl http2;
server_name ${DOMAIN} www.${DOMAIN};
ssl_certificate /etc/letsencrypt/live/${DOMAIN}/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/${DOMAIN}/privkey.pem;
include ${LNMP_PATH}/conf/ssl.conf;
root ${WWW_ROOT};
index index.html index.htm index.php;
access_log /home/wwwlogs/${DOMAIN}.https.log;
error_log /home/wwwlogs/${DOMAIN}.https.error.log;
# 安全头部
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-XSS-Protection "1; mode=block" always;
add_header X-Content-Type-Options "nosniff" always;
add_header Referrer-Policy "no-referrer-when-downgrade" always;
# 网站内容
location / {
try_files \$uri \$uri/ =404;
}
# PHP处理
location ~ \.php$ {
fastcgi_pass unix:/tmp/php-cgi.sock;
fastcgi_index index.php;
include fastcgi.conf;
fastcgi_param HTTPS on;
}
# 禁止访问隐藏文件
location ~ /\. {
deny all;
access_log off;
log_not_found off;
}
# 静态文件缓存
location ~* \.(jpg|jpeg|png|gif|ico|css|js)$ {
expires 365d;
add_header Cache-Control "public, immutable";
}
# certbot续期验证目录
location ^~ /.well-known/acme-challenge/ {
alias ${WWW_ROOT}/.well-known/acme-challenge/;
try_files \$uri =404;
}
}
EOF
log "Nginx SSL配置完成"
}
# 配置自动续期
setup_auto_renewal() {
log "配置自动续期..."
# 创建续期脚本
cat > /usr/local/bin/renew-ssl.sh << EOF
#!/bin/bash
# SSL证书自动续期脚本
DOMAIN="${DOMAIN}"
LOG_FILE="/var/log/ssl-renewal.log"
echo "\$(date): 开始检查证书续期" >> "\$LOG_FILE"
# 停止Nginx使用80端口
${LNMP_PATH}/sbin/nginx -s stop 2>/dev/null
# 续期证书
if certbot renew --standalone --pre-hook "${LNMP_PATH}/sbin/nginx -s stop" --post-hook "${LNMP_PATH}/sbin/nginx"; then
echo "\$(date): 证书续期成功" >> "\$LOG_FILE"
# 检查证书有效期
EXPIRY=\$(openssl x509 -enddate -noout -in /etc/letsencrypt/live/\${DOMAIN}/fullchain.pem | cut -d= -f2)
echo "\$(date): 新证书到期时间: \$EXPIRY" >> "\$LOG_FILE"
# 发送通知(需要配置邮件)
# echo "SSL证书续期成功,到期时间: \$EXPIRY" | mail -s "SSL证书续期通知" ${EMAIL}
else
echo "\$(date): 证书续期失败" >> "\$LOG_FILE"
# 发送告警
# echo "SSL证书续期失败,请手动检查" | mail -s "SSL证书续期告警" ${EMAIL}
fi
# 确保Nginx启动
if ! pgrep -x nginx > /dev/null; then
${LNMP_PATH}/sbin/nginx
fi
EOF
chmod +x /usr/local/bin/renew-ssl.sh
# 创建续期钩子目录
mkdir -p /etc/letsencrypt/renewal-hooks/{pre,post,deploy}
# 创建Nginx重启钩子
cat > /etc/letsencrypt/renewal-hooks/deploy/restart-nginx.sh << EOF
#!/bin/bash
${LNMP_PATH}/sbin/nginx -s reload
EOF
chmod +x /etc/letsencrypt/renewal-hooks/deploy/restart-nginx.sh
# 添加到crontab(每周日凌晨3点检查续期)
(crontab -l 2>/dev/null; echo "0 3 * * 0 /usr/bin/certbot renew --quiet --pre-hook \"${LNMP_PATH}/sbin/nginx -s stop\" --post-hook \"${LNMP_PATH}/sbin/nginx\"") | crontab -
# 添加续期检查脚本到cron(每天检查一次)
(crontab -l 2>/dev/null; echo "0 2 * * * /usr/local/bin/renew-ssl.sh >/dev/null 2>&1") | crontab -
log "自动续期配置完成"
log "续期日志: /var/log/ssl-renewal.log"
}
# 创建证书监控脚本
create_monitoring_script() {
log "创建证书监控脚本..."
cat > /usr/local/bin/check-ssl-expiry.sh << EOF
#!/bin/bash
# 证书过期监控脚本
DOMAIN="${DOMAIN}"
DAYS_WARNING=30
CERT_FILE="/etc/letsencrypt/live/\${DOMAIN}/fullchain.pem"
LOG_FILE="/var/log/ssl-check.log"
if [ -f "\$CERT_FILE" ]; then
EXPIRY_DATE=\$(openssl x509 -enddate -noout -in "\$CERT_FILE" | cut -d= -f2)
EXPIRY_SECONDS=\$(date -d "\$EXPIRY_DATE" +%s)
CURRENT_SECONDS=\$(date +%s)
DAYS_LEFT=\$(( (EXPIRY_SECONDS - CURRENT_SECONDS) / 86400 ))
echo "\$(date): 证书 \${DOMAIN} 剩余 \$DAYS_LEFT 天" >> "\$LOG_FILE"
if [ "\$DAYS_LEFT" -lt "\$DAYS_WARNING" ]; then
echo "\$(date): 警告! 证书 \${DOMAIN} 将在 \$DAYS_LEFT 天后过期" >> "\$LOG_FILE"
# 这里可以添加通知逻辑,例如发送邮件
# echo "证书 \${DOMAIN} 将在 \$DAYS_LEFT 天后过期" | mail -s "证书过期警告" ${EMAIL}
# 如果少于7天,尝试自动续期
if [ "\$DAYS_LEFT" -lt 7 ]; then
echo "\$(date): 尝试紧急续期证书..." >> "\$LOG_FILE"
/usr/local/bin/renew-ssl.sh
fi
fi
else
echo "\$(date): 错误: 证书文件不存在: \${CERT_FILE}" >> "\$LOG_FILE"
fi
EOF
chmod +x /usr/local/bin/check-ssl-expiry.sh
# 添加到crontab(每天凌晨1点检查)
(crontab -l 2>/dev/null; echo "0 1 * * * /usr/local/bin/check-ssl-expiry.sh") | crontab -
log "监控脚本已安装"
}
# 设置防火墙规则
setup_firewall() {
log "配置防火墙..."
# 检查UFW
if command -v ufw &> /dev/null; then
ufw allow 80/tcp
ufw allow 443/tcp
ufw reload
log "UFW防火墙已配置"
fi
# 检查iptables
if command -v iptables &> /dev/null && ! command -v ufw &> /dev/null; then
iptables -A INPUT -p tcp --dport 80 -j ACCEPT
iptables -A INPUT -p tcp --dport 443 -j ACCEPT
log "iptables防火墙已配置"
fi
}
# 测试SSL配置
test_ssl_configuration() {
log "测试SSL配置..."
# 测试Nginx配置
if ${LNMP_PATH}/sbin/nginx -t; then
log "✅ Nginx配置测试通过"
else
error "Nginx配置测试失败"
exit 1
fi
# 重启Nginx
${LNMP_PATH}/sbin/nginx -s reload
sleep 2
# 测试SSL连接
log "测试SSL连接..."
if curl -s -o /dev/null -w "%{http_code}" "https://${DOMAIN}" | grep -q "200\|301\|302"; then
log "✅ SSL连接测试成功"
else
warning "SSL连接测试可能有问题,请手动检查"
fi
# 显示证书信息
log "证书信息:"
openssl x509 -in /etc/letsencrypt/live/${DOMAIN}/fullchain.pem -text -noout | grep -A2 "Not Before\|Not After"
}
# 显示完成信息
show_completion() {
echo ""
echo "================================================"
echo "✅ SSL证书自动配置完成!"
echo "================================================"
echo ""
echo "📋 配置摘要:"
echo " 域名: ${DOMAIN}"
echo " 网站目录: ${WWW_ROOT}"
echo " 证书位置: /etc/letsencrypt/live/${DOMAIN}/"
echo " Nginx配置: ${LNMP_PATH}/conf/vhost/${DOMAIN}.conf"
echo ""
echo "⚙️ 自动续期设置:"
echo " 每周日3:00自动检查续期"
echo " 每天1:00检查证书过期状态"
echo " 续期日志: /var/log/ssl-renewal.log"
echo ""
echo "🔧 手动操作命令:"
echo " 重启Nginx: ${LNMP_PATH}/sbin/nginx -s reload"
echo " 手动续期: certbot renew"
echo " 查看证书: certbot certificates"
echo ""
echo "📊 验证SSL配置:"
echo " https://www.ssllabs.com/ssltest/analyze.html?d=${DOMAIN}"
echo ""
echo "⚠️ 注意事项:"
echo " 1. 确保DNS已正确解析到本服务器"
echo " 2. 首次访问可能需要清除浏览器缓存"
echo " 3. 建议在ssllabs.com测试SSL评分"
echo " 4. 原配置文件已备份: ${LNMP_PATH}/conf/vhost/${DOMAIN}.conf.backup.*"
echo ""
echo "================================================"
}
# 主函数
main() {
log "开始为 ${DOMAIN} 配置SSL证书"
log "LNMP路径: ${LNMP_PATH}"
log "网站目录: ${WWW_ROOT}"
check_root
check_lnmp
install_certbot
get_ssl_certificate
configure_nginx_ssl
setup_auto_renewal
create_monitoring_script
setup_firewall
test_ssl_configuration
show_completion
log "所有配置完成!"
}
# 执行主函数
main