纯公网 IP 申请 HTTPS 证书记录 (Docker + Lego + Let’s Encrypt)

纯公网 IP 申请 HTTPS 证书指南 (Docker + Lego 自动续期)

最近折腾一些内部服务,不想绑定域名,直接用 IP 访问,但浏览器那个“不安全”的红色警告看着实在难受。于是研究了一下怎么给公网 IP 申请免费的 SSL 证书。

这里必须要提一个重磅消息Let’s Encrypt 官方已于本月(2025年12月19日)正式支持公网 IP 证书签发。这意味着我们不再需要去凑合使用 HiCA 或 ZeroSSL,直接用官方接口即可,稳得一匹。

本教程使用 Docker + Lego 工具来实现。这套方案最大的优点是兼容机器上现有的 Nginx/Caddy 等服务——只有在真正需要续期的那几秒钟才会短暂占用 80 端口,平时互不干扰。

1. 搞定环境:Docker

有 Docker 的跳过这一步。没装的跑一下命令:

非大陆服务器 (Github 直连):

Bash

curl -fsSL https://get.docker.com | sh

国内服务器 (加速镜像):

Bash

bash <(curl -sSL https://linuxmirrors.cn/docker.sh)

2. 首次手动申请

为了不污染宿主机环境,我们直接拉个 Lego 容器来跑。因为申请证书需要验证 80 端口,所以如果你的 80 端口被占用了(比如跑着 Nginx),得先停一下

# 1. 暂停现有的 Web 服务 (根据你的实际情况改,没有就跳过)
# systemctl stop nginx

# 2. 定义一下变量,方便后面复用
DATA_DIR="$HOME/lego"
IP=$(curl -s -4 ip.sb)  # 自动获取公网IP

# 3. 启动 Lego 容器申请证书
# 这里的 --email 记得改成你自己的,虽然乱填也能过,但最好填真实的
docker run --rm -it \
  -v "$DATA_DIR":/.lego \
  -p 80:8888 \
  goacme/lego \
  --email="example@gmail.com" \
  --accept-tos \
  --server="https://acme-v02.api.letsencrypt.org/directory" \
  --http \
  --http.port=":8888" \
  --key-type="ec384" \
  --domains="$IP" \
  --disable-cn \
  run --profile "shortlived" \
  && echo -e "\n✅ 申请成功! 证书位置如下:" \
  && echo "证书 (Public): $DATA_DIR/certificates/$IP.crt" \
  && echo "私钥 (Private): $DATA_DIR/certificates/$IP.key"

# 4. 恢复 Web 服务
# systemctl restart nginx

小贴士:拿到证书后,赶紧配置到你的 Nginx 或面板里去,测试一下 HTTPS 访问是否正常。

3. 编写智能续期脚本 (核心步骤)

Lego 虽然自带 renew 命令,但不管三七二十一直接停 Web 服务有点太暴力了。

所以在论坛找了个脚本:先检查证书有效期,只有剩余时间不足 3 天时,才停止 Nginx 进行续期。最大程度保证服务在线率。

创建脚本:nano $HOME/auto_ssl.sh

#!/bin/bash

# --- 核心配置区 ---
# 证书存储目录 (务必使用绝对路径)
DATA_DIR="$HOME/lego"
EMAIL="example@gmail.com"
# 触发续期的剩余天数 (建议设为 3-7 天)
RENEW_DAYS=3

# 自动获取本机 IP
IP=$(curl -s -4 ip.sb)
CERT_PATH="$DATA_DIR/certificates/$IP.crt"

# --- 核心逻辑 ---
check_validity() {
    # 如果证书都没了,那必须续期
    if [ ! -f "$CERT_PATH" ]; then
        echo "❌ 未找到证书文件: $CERT_PATH"
        return 1
    fi
    
    # 计算剩余天数
    EXP_DATE=$(openssl x509 -enddate -noout -in "$CERT_PATH" | cut -d= -f2)
    EXP_EPOCH=$(date -d "$EXP_DATE" +%s)
    NOW_EPOCH=$(date +%s)
    LEFT_SECONDS=$((EXP_EPOCH - NOW_EPOCH))
    LEFT_DAYS=$((LEFT_SECONDS / 86400))

    echo "📅 证书剩余有效期: $LEFT_DAYS 天 (续期阈值: $RENEW_DAYS 天)"

    if [ "$LEFT_DAYS" -le "$RENEW_DAYS" ]; then
        return 1 # 时间不够了,要续期
    else
        return 0 # 时间还早,继续睡
    fi
}

# 1. 检查阶段
if check_validity; then
    echo "✅ 证书坚挺中,无需操作,Web 服务保持运行。"
    exit 0
fi

# 2. 续期阶段 (只有返回 1 才会走到这里)
echo "⚠️ 证书即将过期,开始执行续期流程..."

echo "🛑 正在暂停 Web 服务以释放 80 端口..."
# --- 在这里修改你的停止命令 ---
systemctl stop nginx 
# ---------------------------

echo "🚀 启动 Docker 容器进行续期..."
docker run --rm \
  -v "$DATA_DIR":/.lego \
  -p 80:8888 \
  goacme/lego \
  --email="$EMAIL" \
  --path="/.lego" \
  --server="https://acme-v02.api.letsencrypt.org/directory" \
  --http --http.port=":8888" \
  --domains="$IP" \
  --disable-cn \
  renew --profile "shortlived" --days "$RENEW_DAYS" --reuse-key --no-random-sleep

LEGO_STATUS=$?

echo "▶️ 正在恢复 Web 服务..."
# --- 在这里修改你的启动命令 ---
systemctl start nginx
# ---------------------------

# 3. 报告结果
if [ $LEGO_STATUS -eq 0 ]; then
    echo "🎉 续期成功,服务已恢复。"
else
    echo "❌ Lego 续期失败,请检查日志!Web 服务已尝试重启。"
fi

记得给脚本加执行权限:

chmod +x $HOME/auto_ssl.sh

4. 设个闹钟 (Crontab)

让服务器每天早上 5 点自己检查一遍。

输入 crontab -e,添加一行:

代码段

# 每天凌晨 5:00 执行 IP 证书续期检查
0 5 * * * /bin/bash /root/auto_ssl.sh >> /root/lego_renew.log 2>&1

(注意:请将 /root/auto_ssl.sh 替换为你脚本的实际绝对路径)


💡 避坑指南

  1. 路径问题:脚本里所有的路径(DATA_DIR 等)一定要用绝对路径,Crontab 执行时的环境和我不一样。
  2. Web 服务冲突:脚本里的 systemctl stop nginx 是核心,如果你用的不是 Nginx(比如 Caddy、Apache 或者 Docker 跑的 Nginx),记得把那两行命令改掉。
  3. 生效验证:续期成功后,虽然证书文件更新了,但你的 Web 服务器可能加载的还是内存里的旧证书。务必确保脚本里的重启/重载命令是有效的

搞定收工!以后这个 IP 的 HTTPS 就不用操心了。

上一篇文章

Remnawave 面板搭建记录 (Docker + CF Tunnel + XHTTP)

下一篇文章

Linux VPS 新手必备:5分钟创建 2GB Swap 交换空间完整教程

写评论

发表评论

您的邮箱地址不会被公开。 必填项已用 * 标注