【Let‘s Encrypt SSL】使用 acme.sh 给 Nginx 安装 Let’s Encrypt 提供的免费 SSL 证书(脚本+更新成功/失败通知)

成功后效果

在这里插入图片描述

安装acme.sh

安装 acme.sh 并设置邮箱用来接受重要通知,如证书快过期未更新通知

curl https://get.acme.sh | sh -s email=my@example.com

执行命令后几秒就安装好了,如果半天没有反应请 Ctrl+C 后重新执行命令。acme.sh 安装在 ~/.acme.sh 目录下,并自动创建了一个 cronjob,每天 0:00 点自动检测所有的证书,如果快过期了, 则会自动更新。安装后,理论上会自动添加一个 acme.sh 全局应用别名,但有时候会 command not found,需要手动执行以下命令:source ~/.bashrc 或 source ~/.bash_profile,或关掉终端重新打开,然后再继续下一步。也可以在 ~/.acme.sh 目录下使用 sh acme.sh 执行命令(例如:sh acme.sh --list)

编写脚本

申请域名脚本(acme_apply.sh)

#!/bin/bash

###################### 写入日志 ######################
log() {
	LOG_PATH="acme_apply.sh.log"

	echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" >> $LOG_PATH
}


###################### 证书申请 ######################
log "开始接收参数"

# 域名
read -p "请输入需申请证书的域名(多个域名使用空格符隔开): " DOMAIN
if [ -z "$DOMAIN" ]; then
	log "输入不能为空,请重新运行脚本并输入有效字符。"
    echo "输入不能为空,请重新运行脚本并输入有效字符。"
    exit 1
fi

read -p "请输入更新证书的天数(默认 60 天后自动更新): " UPDATE_DAY
if [ -z "$UPDATE_DAY" ]; then
    UPDATE_DAY="60"
fi

# 证书目录,最后面不需要"/"符号
read -p "请输入证书安装路径(默认为/etc/nginx/ssl): " CERTIFICATE_PATH
if [ -z "$CERTIFICATE_PATH" ]; then
    CERTIFICATE_PATH="/etc/nginx/ssl"
fi

# nginx 安装环境
read -p "请输入 nginx 安装环境(0:Docker; 1:Linix; 默认为 0): " NGINX_ENV
if [ -z "$NGINX_ENV" ]; then
    NGINX_ENV="0"
elif [ $NGINX_ENV != "0" ] && [ $NGINX_ENV != "1" ]; then
    log "输入的参数错误,请重新运行脚本并输入正确字符。"
    echo "输入的参数错误,请重新运行脚本并输入正确字符。"
    exit 1
fi

# 部署环境
read -p "请输入部署环境(dev|test|prod 默认为 dev): " DEPLOY_ENV
if [ -z "$DEPLOY_ENV" ]; then
    DEPLOY_ENV="dev"
elif [ $DEPLOY_ENV != "dev" ] && [ $DEPLOY_ENV != "test" ] && [ $DEPLOY_ENV != "prod" ]; then
    log "输入的参数错误,请重新运行脚本并输入正确字符。"
    echo "输入的参数错误,请重新运行脚本并输入正确字符。"
    exit 1
fi

log "接收的参数信息: {证书域名: $DOMAIN, 更新证书的天数: $UPDATE_DAY, 安装路径=$CERTIFICATE_PATH, nginx 安装环境: ${NGINX_ENV}, 部署环境: ${DEPLOY_ENV}}"

# 处理域名参数
DOMAIN_ARRAY=(${DOMAIN// / })
FIRST_DOMIAN=${DOMAIN_ARRAY[0]}
NEW_DOMAIN=""
for var in ${DOMAIN_ARRAY[@]}
do
   NEW_DOMAIN="${NEW_DOMAIN} -d $var"
done

log "处理后的域名参数: {FIRST_DOMIAN: $FIRST_DOMIAN, NEW_DOMAIN: $NEW_DOMAIN}"
log "$DOMAIN 证书开始申请"

cd /root/.acme.sh

log "sh acme.sh --issue --dns dns_dp$NEW_DOMAIN --days $UPDATE_DAY"
# --days 2 表示更新证书的天数,默认值为 60 天
sh acme.sh --issue --dns dns_dp$NEW_DOMAIN --days $UPDATE_DAY

# 检查退出状态码并输出结果
if [ $? -eq 0 ]; then
	# 申请证书成功后安装证书
	log "sh acme.sh --install-cert -d $FIRST_DOMIAN \
	--key-file $CERTIFICATE_PATH/$FIRST_DOMIAN.key \
	--fullchain-file $CERTIFICATE_PATH/$FIRST_DOMIAN.cer \
	--reloadcmd \"sh /root/.acme.sh/install_after.sh $FIRST_DOMIAN $NGINX_ENV $DEPLOY_ENV\""
	sh acme.sh --install-cert -d $FIRST_DOMIAN \
	--key-file $CERTIFICATE_PATH/$FIRST_DOMIAN.key \
	--fullchain-file $CERTIFICATE_PATH/$FIRST_DOMIAN.cer \
	--reloadcmd "sh /root/.acme.sh/install_after.sh $FIRST_DOMIAN $NGINX_ENV $DEPLOY_ENV"
	log "申请证书成功"
else
	log "申请证书失败"
fi
log "$DOMAIN 证书申请结束"

更新成功/失败通知(install_after.sh)

本文通过URL发起通知

#!/bin/bash

###################### 写入日志 ######################
log() {
	LOG_PATH="install_after.sh.log"

	echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" >> $LOG_PATH
}


###################### 验证证书是否更新成功和生效(废弃) ######################
verify() {
	# 设置要查询的域名和 HTTPS 端口
	PORT="443"

	# 使用 OpenSSL 获取证书信息并提取到期时间
	END_DATE=$(echo | openssl s_client -servername $DOMAIN -connect $DOMAIN:$PORT 2>/dev/null | openssl x509 -noout -enddate | sed 's/.*=//')
	log "使用 OpenSSL 获取证书信息并提取到期时间: $END_DATE"

	# 将到期时间转换成 Unix 时间戳
	END_TIMESTAMP=$(date +%s -d "$END_DATE")
	log "将到期时间转换成 Unix 时间戳: $END_TIMESTAMP"

	# 获取当前时间的 Unix 时间戳
	NOW_TIMESTAMP=$(date +%s)
	log "当前时间的 Unix 时间戳: $NOW_TIMESTAMP"

	# 计算两个时间戳之间的间隔(单位:秒)
	INTERVAL=$((END_TIMESTAMP - NOW_TIMESTAMP))
	log "两个时间戳之间的间隔: $INTERVAL"

	# 判断间隔是否小于 30 天(单位:秒) 7776000
	WARNING_THRESHOLD=$((30 * 24 * 60 * 60)) # 30 天的秒数
	if [ $INTERVAL -lt $WARNING_THRESHOLD ]; then
		log "SSL/TLS 证书更新失败: $DOMAIN"
		early_warning_push "{\"environment\": \"$DEPLOY_ENV\", \"logLevel\":\"warn\", \"projectName\":\"域名证书\",\"moduleName\":\"证书更新\",\"detailContent\":\"证书更新失败\"}"
	else
		log "SSL/TLS 证书更新成功: $DOMAIN"
		early_warning_push "{\"environment\": \"$DEPLOY_ENV\", \"logLevel\":\"info\", \"projectName\":\"域名证书\",\"moduleName\":\"证书更新\",\"detailContent\":\"证书更新成功\"}"
	fi
}


###################### 发送通知 ######################
early_warning_push() {
	log "请求信息: curl -H 'Content-Type: application/json' -X POST -d '$1' $EARLY_WARNING_PUSH_URL"
	curl -H 'Content-Type: application/json' -X POST -d '$1' $EARLY_WARNING_PUSH_URL
}


###################### 配置更新证书成功后执行的命令 ######################
# 域名
DOMAIN=$1
# nginx 安装环境(0:Docker; 1:Linix);
NGINX_ENV=$2
# 部署环境(dev|test|prod)
DEPLOY_ENV=$3
log "接收的参数信息: {域名: $DOMAIN, nginx 安装环境: $NGINX_ENV, 部署环境: $DEPLOY_ENV}"

# 设置通知URL
if [ $DEPLOY_ENV = "prod" ]; then
	EARLY_WARNING_PUSH_URL="http://127.0.0.1:8000/push"
elif [ $DEPLOY_ENV = "test" ]; then
	EARLY_WARNING_PUSH_URL="http://127.0.0.1:8000/push"
else
	EARLY_WARNING_PUSH_URL="http://127.0.0.1:8000/push"
fi

log "开始验证 $DOMAIN 证书更新"

# 更新 nginx 配置信息
if [ $NGINX_ENV = "0" ]; then
	log "docker exec -it nginx nginx -s reload"
	docker exec -it nginx nginx -s reload
elif [ $NGINX_ENV = "1" ]; then
	log "nginx -s reload"
	nginx -s reload
fi

# 检查退出状态码并输出结果
if [ $? -eq 0 ]; then
	# 输出日志
	log "重新加载 Nginx 配置文件成功"
	early_warning_push "{\"environment\": \"$DEPLOY_ENV\", \"logLevel\":\"info\", \"projectName\":\"域名证书\",\"moduleName\":\"证书更新\",\"detailContent\":\"证书更新成功\"}"
	# verify
else
	log "重新加载 Nginx 配置文件失败"
	early_warning_push "{\"environment\": \"$DEPLOY_ENV\", \"logLevel\":\"error\", \"projectName\":\"域名证书\",\"moduleName\":\"证书更新\",\"detailContent\":\"重新加载 Nginx 配置文件失败\"}"
fi

log "验证 $DOMAIN 证书更新结束"

注意

  1. 两个脚本推荐存放在 /root/.acme.sh 目录下,并通过 chmod +x 授权。自定义存放目录,需要调整 acme_apply.sh 脚本。
    在这里插入图片描述
  2. 执行脚本之后,会生成对应的日志文件(acme_apply.sh -> acme_apply.sh.log , install_after.sh -> install_after.sh.log)

使用

泛域名申请(如果单独使用 *.example.com 申请会出现问题,在安装证书时如果同目录下存在泛域名所包含的域名时证书会安装失败):
在这里插入图片描述
非泛域名申请
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值