PVE 集群证书自动化:Let’s Encrypt 泛域名+ACME 续期
本文基于 2025-04 生产环境 PVE 8.2 集群编写,全部命令与脚本可直接复制使用。
关键词:ACME、Let’s Encrypt、泛域名、DNS-01、acme.sh、PVE API、Ansible。
一、痛点:自签名证书 = “不安全” + “人肉运维”
• 浏览器红屏警告,用户第一反应是“这网站有毒”。 • 证书 365 天到期,运维日历写满“记得换证书”。 • 集群节点 > 3 个时,手动更新会疯——时间成本 = 节点数 × 10 min × 4 次/年。
二、方案总览:一条命令,终身免维护
三、前置准备:10 分钟搞定 3 件事
1. 域名
购买阿里云example.com,新增泛解析*.pve.example.com→ 集群浮动 IP。2. RAM 子账号(最小权限)
权限策略:AliyunDNSFullAccess
记录AccessKey ID/Secret,后面脚本会用到。3. PVE API Token(每节点一个)
WebUI → 数据中心 → API Token → 创建
权限:PVEAuditor即可,过期时间:无限
得到格式:USER@REALM!TOKENID=UUID及Secret
四、步骤 1:安装 & 初始化 acme.sh
# 任意一台管理机(可离线)
curl https://get.acme.sh | sh -s email=ops@example.com
source ~/.bashrc
# 让 acme.sh 使用阿里 DNS 插件
export Ali_Key="LTAIxxxxxxxxxxxxx"
export Ali_Secret="xxxxxxxxxxxxxxxxxxxxxxxxxxxx"五、步骤 2:申请泛域名证书(一次性)
# 测试(无额度风险)
acme.sh --issue \
-d '*.pve.example.com' \
--dns dns_ali \
--staging
# 正式
acme.sh --issue \
-d '*.pve.example.com' \
--dns dns_ali申请成功后会看到:*.pve.example.com_ecc/ 目录下生成 4 个文件,其中
• fullchain.cer→ 证书链• *.key→ 私钥
六、步骤 3:编写 PVE 部署钩(官方已集成)
新建 /etc/acme.sh/pve-deploy.sh
#!/usr/bin/env bash
# 入参:$1=cer_path $2=key_path $3=domain
CERT=$1
KEY=$2
NODE="pve-01" # 当前节点名称,Ansible 会批量替换
TOKEN="root@pam!acme" # 上一步创建的 Token
SECRET="xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
# 调用 PVE API 上传证书
curl -sf -X PUT "https://localhost:8006/api2/json/nodes/$NODE/certificates/custom" \
-H "Authorization: PVEAPIToken=$TOKEN=$SECRET" \
-F "certificates=@$CERT" \
-F "key=@$KEY" \
&& systemctl reload pveproxy赋可执行权限chmod +x /etc/acme.sh/pve-deploy.sh
七、步骤 4:让 acme.sh 自动部署
acme.sh --deploy \
-d '*.pve.example.com' \
--deploy-hook pve \
--deploy-pve-node pve-01 \
--deploy-pve-tokenid "root@pam!acme" \
--deploy-pve-tokensecret "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"成功后,刷新 WebUI → 数据中心 → 凭证 → 服务器证书,可看到新证书,浏览器瞬间变绿锁。
八、步骤 5:Ansible 批量下发(30 节点 2 分钟)
目录结构
acme-pve/
├── inventory
├── playbook.yml
└── templates/
└── pve-deploy.sh.j2inventory 示例
[pve]
pve-[01:30] ansible_host=10.1.10.{{ groups.pve.index(inventory_hostname) + 101 }}playbook.yml 关键片段
- hosts: pve
vars:
domain: "*.pve.example.com"
tasks:
- name: 1. 复制 acme.sh 证书
copy:
src: "/root/.acme.sh/{{ domain }}_ecc/"
dest: "/root/.acme.sh/{{ domain }}_ecc/"
mode: preserve
- name: 2. 渲染部署钩
template:
src: pve-deploy.sh.j2
dest: /etc/acme.sh/pve-deploy.sh
mode: '0755'
vars:
node: "{{ inventory_hostname }}"
- name: 3. 调用部署钩
shell: /etc/acme.sh/pve-deploy.sh执行
ansible-playbook -i inventory playbook.yml
九、步骤 6:续期自动化(systemd 方式,比 cron 更稳)
生成 timer & service 单元
acme.sh --install-cert \
-d '*.pve.example.com' \
--reloadcmd "systemctl reload pveproxy"查看下次运行时间
systemctl list-timers | grep acme
默认 每 60 天执行一次续期检查,可自定义。
十、验证:4 条命令确认终身有效
1. 证书信息 openssl x509 -in /etc/pve/nodes/pve-01/pve-ssl.pem -noout -text | grep "Not After"2. 续期 dry-run acme.sh --renew -d '*.pve.example.com' --dns dns_ali --staging --force3. 节点同步 for n in pve-{01..03}; do ssh $n "openssl x509 -in /etc/pve/nodes/$n/pve-ssl.pem -noout -subject"; done4. 浏览器访问 访问 https://pve-01.pve.example.com:8006,地址栏绿锁 + 证书有效期 90 天,且 下次访问仍在有效期内。
十一、踩坑锦囊
AliyunDNSFullAccess 即可*.example.com{{ inventory_hostname }} 动态替换fullchain.cer,勿只传 cer
十二、结语:把“换证书”从日历里删掉
借助 ACME + DNS-01 + acme.sh 官方 PVE 钩子,我们实现了:
• 申请无人值守 • 部署一键批量 • 续期永不遗忘
从此,PVE 集群的 HTTPS 证书生命周期进入 “自动驾驶” 模式。
把省下的时间拿去优化业务,而不是每年 4 次机械劳动。
本文链接:https://kinber.cn/post/6181.html 转载需授权!
推荐本站淘宝优惠价购买喜欢的宝贝:

支付宝微信扫一扫,打赏作者吧~
