Nginx搭配acme.sh实现证书自动续期

306 阅读7分钟

背景:

当前最新的个人免费版本的https证书由于更新,在云平台上申请之后,有效期只有三个月,由于证书有效期时间比较短,手动更新比较麻烦,这里采用acme.sh 加自定义shell脚本的方式来完成自动更新ssl证书

acms.sh

介绍

acme.sh是一个用于自动化证书申请和部署的开源工具,支持多种验证方式和CA机构。能够简化证书的申请和管理过程,尤其适合那些需要频繁更新证书的用户。它通过完全实现ACME协议,帮助用户从Let's Encrypt、ZeroSSL等多个CA免费获取SSL/TLS证书

安装

由于acme.sh的源码是在git上面 拉取会有无法访问的问题 此时我们使用国内的码云进行下载安装

源码地址:github.com/acmesh-offi…
码云地址:gitee.com/neilpang/ac…
安装参考:github.com/acmesh-offi…

#拉取码云上的代码 
git clone https://gitee.com/neilpang/acme.sh.git 

#进入到指定目录 
cd acme.sh 

#安acme my@example.com 修改为自己的邮箱地址  
./acme.sh --install -m my@example.com 

#重新加载用户的bashrc文件 
source ~/.bashrc  

#验证是否安装完成 能够打印出版本信息已经帮助信息即可 
acme.sh -h 

https://github.com/acmesh-official/acme.sh 
v3.0.5 
Usage: acme.sh <command> ... [parameters ...]

默认CA机构

目前 acme.sh 支持 5 个正式环境 CA,分别是 Let’s Encrypt、Buypass、ZeroSSL、SSL.com 和 Google Public CA,默认使用 ZeroSSL,如果需要更换可以使用如下命令:

 #切换Let’s Encrypt
 acme.sh --set-default-ca --server letsencrypt
 
 #切换 Buypass
 acme.sh --set-default-ca --server buypass
 
 #切换 ZeroSSL
 acme.sh --set-default-ca --server zerossl
 
 #切换 SSL.com
 acme.sh --set-default-ca --server ssl.com
 
 #切换 Google Public CA
 acme.sh --set-default-ca --server google

结合阿里云使用

前提

1 已经拥有阿里云账户
2 该账户下有相应的域名

获取阿里云秘钥

image.png

新建AccessKey

新建AccessKey

image.png

服务器配置环境并更新证书

#设置环境变量 
export Ali_Key="阿里云kry" 
export Ali_Secret="阿里云Secret" 

#使用acme.sh 申请证书 
#--issue 申请证书 
#--dns dns_ali 阿里云的自动验证dns的工作流 
#testdemo.demo.com 需要申请证书的域名 
#--force 强制更新证书 
acme.sh --issue --dns dns_ali -d testdemo.demo.com --force

查看证书

配置无误后 会申请到证书并保存到相应的位置

image.png

Nginx更新证书

#使用acme获取到证书之后 可以使用--install-cert来安装证书 
#该命令会和获取证书的命令绑定 下次再执行获取证书的命令后 会自动执行安装证书的命令 
#--install-cert 将证书安装到nginx或者是其他服务器上 
#-d 指定域名的证书 
#--key-file nginx中key文件中位置 与nginx配置文件中的路径一致 
#--fullchain-file nginx中pem文件的位置 与nginx配置文件的李静一致 
#-reloadcmd 安装证书后需要执行的命令 一般是重启nginx 
acme.sh --install-cert -d testdemo.wisdcloud.com  --key-file /etc/nginx/ssl/testdemo.key  --fullchain-file /etc/nginx/ssl/testdemo.pem  --reloadcmd "systemctl restart nginx"

结合华为云使用

前提

1 拥有华为云账户
2 该账户下有相应的域名
3 使用华为云IAM账户

注意

使用华为云的时候,一定要用IAM子账户,否则在获取证书的步骤会报错

华为云设置

主账户生成IAM子账户

image.png

image.png

image.png

image.png

创建用户组

image.png

image.png

针对用户组授权 确保该用户组拥有添加dns解析记录的权限

image.png

image.png

image.png

将用户授权 使用将用户加入到用户组的方式

将用户授权 使用将用户加入到用户组的方式

image.png

image.png

image.png

登录IAM账户 获取相应的信息

登录IAM账户 获取相应的信息

image.png

image.png

记录下 IAM用户名 和 账号名

image.png

服务器侧设置

#这是华为云登录环境变量 
export HUAWEICLOUD_Username="IAM用户名" 
export HUAWEICLOUD_Password="IAM用户登录密码" 
export HUAWEICLOUD_DomainName="账户名" 

#获取证书 
acme.sh --issue --dns dns_huaweicloud -d 需要获取证书的域名 

#配置无误后 会获取到相应的证书 
[2024年 07月 10日 星期三 17:58:11 CST] Your cert is in: /root/.acme.sh/testdemo.sqklrq.com/testdemo.demo.com.cer 
[2024年 07月 10日 星期三 17:58:11 CST] Your cert key is in: /root/.acme.sh/testdemo.sqklrq.com/testdemo.demo.com.key 
[2024年 07月 10日 星期三 17:58:11 CST] The intermediate CA cert is in: /root/.acme.sh/testdemo.demo.com/ca.cer 
[2024年 07月 10日 星期三 17:58:11 CST] And the full chain certs is there: /root/.acme.sh/testdemo.demo/fullchain.cer

Nginx更新证书

#使用acme获取到证书之后 可以使用--install-cert来安装证书 
#该命令会和获取证书的命令绑定 下次再执行获取证书的命令后 会自动执行安装证书的命令 
#--install-cert 将证书安装到nginx或者是其他服务器上 
#-d 指定域名的证书 
#--key-file nginx中key文件中位置 与nginx配置文件中的路径一致 
#--fullchain-file nginx中pem文件的位置 与nginx配置文件的李静一致 
#-reloadcmd 安装证书后需要执行的命令 一般是重启nginx 
acme.sh --install-cert -d testdemo.wdemo.com  --key-file /etc/nginx/ssl/testdemo.key  --fullchain-file /etc/nginx/ssl/testdemo.pem  --reloadcmd "systemctl restart nginx"

常见错误

Not enough information provided to dns_huaweicloud    环境变量没设置好 检查设置的环境变量  
dns_api(dns_huaweicloud): Error getting token.        用户名密码和domain设置错误  
dns_api(dns_huaweicloud): Error getting zone id.      子账号云解析服务没有进行授权

结合其他厂商使用

当需要结合其他厂商使用的时候 需要调用不同的API接口
具体格式参考:github.com/acmesh-offi…

搭配shell脚本使用

思路

  1. 通过shell脚本结合crontab定时任务 来自动完成更新ssl证书 将以上内容自动化完成
  2. 通过判断相应的证书文件 来获取到证书的过期时间 并将时间戳转化为标准格式 进行时间对比 如果有效期小于15天 则进行更新证书的操作
  3. 支持在同一个服务器上面配置多个不同的阿里云或者是华为云的key
  4. 使用came.sh来更新证书
  5. 更新完成后 使用邮件来通知(需要提前配置好邮件设置) 也可以更换其他的通知方式

需提前安装邮件服务器,可自行查找安装教程.本文不做介绍

shell脚本

#/bin/bash
# 整体逻辑:使用acme.sh来获取证书 并更新 使用邮件通知更新结果

# 需要更新的域名
Domain_name=demo.demo.cn

# 域名ssl文件所在地址
key_path=/usr/local/nginx/conf/ssl/demo.demo.cn.cn_server.key
pem_path=/usr/local/nginx/conf/ssl/demo.demo.cn.cn_server.crt

# 证书有效期 设置一个空值 后续步骤给此变量赋值
ssl_validity=""

# 邮件通知的邮箱地址
mailbox=demo@163.com


# 需要使用不同的云平台的时候 将相应的注释取消就可以
# 阿里云秘钥 
# 修改为对应域名所在阿里云的key和Secret
#export Ali_Key="阿里云Key"
#export Ali_Secret="阿里云Secret"

# 华为云秘钥
export HUAWEICLOUD_Username="test"
export HUAWEICLOUD_Password="demo"
export HUAWEICLOUD_DomainName="demo"

# 检查现有证书的有效期 定义函数获取证书有效期,接受一个参数:证书文件路径
# 根据传入的证书文件 获取证书有效期 并转换为可读的年月日
get_ssl_expiry_date() {
    pem_path="$1"
    notAfterRaw="$(openssl x509 -in "$pem_path" -noout -enddate)"
    notAfterDate="${notAfterRaw#*=}"
    notAfterDateFormatted=$(date -ud "${notAfterDate% *}" +"%Y-%m-%d")
    echo $notAfterDateFormatted
}

# 定义发送邮件的函数 接收4个参数 1更新的域名 2是否成功 3证书有效期 4备注
send_email() {
    domain="$1"
    update_result="$2"
    certificate_expiry="$3"
    remarks="$4"

    # 设置邮件内容
    subject="域名更新通知"
    body="更新域名: $domain
更新结果: $update_result
证书有效期: $certificate_expiry
备注: $remarks"

    # 发送邮件
    echo "$body" | mail -s "$subject" $mailbox
}

# 获取现有证书到期时间
output_date=$(get_ssl_expiry_date "$pem_path")

# 将现有证书到期时间转换为秒数
output_seconds=$(date -d "$output_date" +%s)

# 获取当前时间的秒数
current_seconds=$(date +%s)

# 计算两个时间之间的差值(以秒为单位)
time_difference=$((output_seconds - current_seconds))

# 将差值转换为天数
days_difference=$((time_difference / 86400))

# 判断两个差值(即距离证书到期)是否小于15天 是则进行证书更新操作
if [ $days_difference -lt 15 ]; then

    # 使用acme来获取证书 判断域名所在的云厂商 如果是在阿里云 则使用第一个  华为云使用第二个
    #/root/acme.sh/acme.sh --issue --dns dns_ali -d $Domain_name --force   			#阿里云使用
    /root/acme.sh/acme.sh --issue --dns dns_huaweicloud -d $Domain_name --force 	#华为云使用

    # 检查获取证书的命令是否正确完成
    if [ $? -eq 0 ]; then
        # 正确获取到证书 则更新证书并重启nginx
        /root/acme.sh/acme.sh --install-cert -d $Domain_name --key-file $key_path --fullchain-file $pem_path --reloadcmd "systemctl restart nginx"

        # 查看更新证书的状态
        if [ $? -eq 0 ]; then
	        # 正确更新证书 则获取证书有效期 并邮件通知
            ssl_validity=$(get_ssl_expiry_date "$pem_path")
            send_email "$Domain_name" "成功" "$ssl_validity" "无"
        else
	        # 更新证书有误  获取已有证书有效期 并邮件通知
            ssl_validity=$(get_ssl_expiry_date "$pem_path")
	        send_email "$Domain_name" "失败" "$ssl_validity" "已获取证书,但更新失败,请及时检查"
        fi
    else
        # 获取证书步骤有误 则查看现有证书有效期 并邮件通知
        ssl_validity=$(get_ssl_expiry_date "$pem_path")
        send_email "$Domain_name" "失败" "$ssl_validity" "获取证书失败,请及时检查"
    fi

# 证书到期大于15天 则获取证书时间 并邮件通知
else
    ssl_validity=$(get_ssl_expiry_date "$pem_path")
	send_email "$Domain_name" "未执行" "$ssl_validity" "证书有效期足够,未执行更新操作,下次检查时间:$(date -d "+7 days" +"%Y-%m-%d")"
fi

设置crontab 定时任务

作者设置的是每周日晚上10点进行检查更新操作

0 22 * * 0 sh /root/shell/update.sh