通过letsencrypt实现永久免费的SSL证书续订

letsencrypt是一个CA机构,提供免费的SSL证书,通过certbot向letsenctypt申请证书,结合corn定时任务可以实现自动更新证书,并且永久免费

letsencrypt官网

参考文章

cerbot官网文档

通过snap安装Certbot

如何没有snap,需先安装snap

#安装有snapd安装包的资源仓库
yum install epel-release
#安装snap
yum install snapd
#启动snapd
systemctl start snapd
#创建软链接
ln -s /var/lib/snapd/snap /snap

使用snap安装certbot

#安装certbot
snap install certbot
#创建软连接
ln -s /snap/bin/certbot /usr/bin/certbot

使用cerbot向letsenctypt申请SSL证书

  • 使用DNS验证的方式
--manual:表示手动输入 DNS 记录
--preferred-challenges dns:指定使用 DNS 验证方式
-d api.moon.com:需要申请证书的域名
certbot certonly --manual --preferred-challenges dns -d www.xxx.cn
  • 使用webroot验证,需要使用到80端口

CA会通过80端口验证webroot下的文件

需要nginx配置反向代理

#certbot自动续签使用的验证路径映射
location ^~ /.well-known/acme-challenge/ {
	root /root/webroot;
}
certbot certonly --webroot -w /root/webroot -d tool.anyi.space

证书的有效期为90天

使用corn定时任务,每两个月就向letsenctypt申请一个新的证书

0 0 15 */2 * certbot renew && nginx -s reload
#!/bin/bash
#仅申请证书,域名是git.anyi.space,验证文件生成路径为(需要CA验证服务器的这个验证文件)/root/webroot
#certbot certonly --webroot -w /root/webroot -d git.anyi.space

#查看当前certbot申请的证书状态
#certbot certificates

#测试续订证书命令能否正常运行
#certbot renew --dry-run

#续订证书,并更新到文件中
echo '续订证书'
certbot renew
echo '拷贝证书'
cat /etc/letsencrypt/live/git.anyi.space/fullchain.pem > /usr/local/nginx/cert/git.anyi.space.pem
echo '拷贝私钥'
cat /etc/letsencrypt/live/git.anyi.space/privkey.pem > /usr/local/nginx/cert/git.anyi.space.key
echo '重新加载nginx'
nginx -s reload

使用hook将申请的证书复制到指定的目录

例如在使用nginx用户运行nginx时,有些目录nginx用户没有权限读取,需要将证书拷贝到有读取权限的目录

  • 编辑vim /etc/letsencrypt/renewal-hooks/deploy/sync-to-nginx.sh写入
    #!/bin/bash
    set -euo pipefail
    
    CERT_ROOT="/usr/local/nginx/cert"
    NGINX_BIN="/usr/local/nginx/sbin/nginx"
    
    # RENEWED_LINEAGE 形如 /etc/letsencrypt/live/blog.anyi.space
    DOMAIN="$(basename "$RENEWED_LINEAGE")"
    TARGET_DIR="$CERT_ROOT/$DOMAIN"
    
    mkdir -p "$TARGET_DIR"
    chmod 755 "$CERT_ROOT" "$TARGET_DIR"
    
    # 先写临时文件再原子替换,避免 reload 时读到半截文件
    umask 077
    cp "$RENEWED_LINEAGE/fullchain.pem" "$TARGET_DIR/fullchain.pem.tmp"
    cp "$RENEWED_LINEAGE/privkey.pem"   "$TARGET_DIR/privkey.pem.tmp"
    mv "$TARGET_DIR/fullchain.pem.tmp" "$TARGET_DIR/fullchain.pem"
    mv "$TARGET_DIR/privkey.pem.tmp"   "$TARGET_DIR/privkey.pem"
    
    chown root:nginx "$TARGET_DIR/fullchain.pem" "$TARGET_DIR/privkey.pem"
    chmod 640 "$TARGET_DIR/fullchain.pem" "$TARGET_DIR/privkey.pem"
    
    if "$NGINX_BIN" -t -q; then
        systemctl reload nginx
    else
        echo "nginx -t failed, skip reload" >&2
        exit 1
    fi
    
    • RENEWED_LINEAGE 的 basename 就是证书名,一域名一证书时正好等于域名,天然一一对应。
    • 每次续期都重新 chown root:nginx + chmod 640,防止 Certbot 重置权限后 Nginx 读不到。
    • 先 nginx -t 再 reload,坏配置不会导致服务中断。
  • 赋予可执行权限chmod +x /etc/letsencrypt/renewal-hooks/deploy/sync-to-nginx.sh
  • 手动同步一次现有的证书
    for lineage in /etc/letsencrypt/live/*/; do
        RENEWED_LINEAGE="${lineage%/}" \
        /etc/letsencrypt/renewal-hooks/deploy/sync-to-nginx.sh
    done
    
  • 对应的nginx SSL配置
    ssl_certificate     /usr/local/nginx/cert/www.anyi.space/fullchain.pem;
    ssl_certificate_key /usr/local/nginx/cert/www.anyi.space/privkey.pem;