Appearance
CertiMan CLI 自动化实践:对接 1Panel 证书库与自动续期
CertiMan CLI 配合 1Panel API,可在证书签发或续期后更新 1Panel 证书库,并由 1Panel 将新证书应用到关联网站。
本文适用于已有正常 HTTPS 的 1Panel v2 / OpenResty 网站。将网站当前证书对应的 CertCloud 订单接入 CertiMan CLI,通过部署 Hook 更新 1Panel 中已有的证书 ID,由1Panel面板应用到关联网站。CertiMan CLI 与 1Panel 安装在同一台 Linux 服务器。
操作流程: 接入已有订单 → 核对已有证书 ID 与关联网站 → 配置 API 与 Hook → 更新并验收 → 开启计划任务。
本文不重新导入证书记录、不修改网站绑定或业务设置。尚未启用 HTTPS 时,先按1Panel 首次部署指南完成手动部署,再接入自动更新。
方案是怎样的?
CertiMan CLI 可负责证书申请、续期和下载;部署 Hook 调用 1Panel API,更新已有证书记录中的证书和私钥内容;1Panel 负责把更新后的证书应用到引用该证书 ID 的网站。证书 ID 在更新过程中保持不变。
首次手动部署验收后再开启计划任务。定期扫描只处理已开启自动续期的证书,满足续期条件才更新,不是每次扫描都重新签发。
网络放行
运行 CLI 的 1Panel 宿主机需能正常解析并通过 HTTPS(TCP 443)访问 certiman.trustasia.com(安装与更新)、certcloud.cn(认证)和 api.certcloud.cn(API)。使用 DNS Hook 时,另行放通实际 DNS 服务商 API;自定义服务端点以实际配置为准。安装后运行 certiman doctor,参考官方安装说明和环境诊断。本文 API 通过本机 127.0.0.1 和实际面板端口调用,仍需满足 1Panel API 的认证及本机来源白名单要求,不因 Hook 向公网开放管理 API。客户端需能访问各关联网站的实际 HTTPS 端口。
开始前准备什么?
- 一台已安装 1Panel v2 和 OpenResty 的 Linux 服务器,并具备 root 或 sudo 权限。
- 一个由 1Panel 管理、HTTPS 已正常工作的目标网站;尚未绑定证书时先完成独立首次部署指南。
- 网站当前证书对应的 CertCloud 订单可使用自动交付,匹配的原始私钥可用。
- 目标域名的 DNS 管理权限,以及后续验证可自动完成或长期有效的验证方式。
- 网络满足上面的要求;如果 HTTPS 在 CDN、WAF 或负载均衡处终止,还需单独更新实际公网入口。
本文使用 trustasia.com 作为示例,请替换为自己的域名。后续所有 CertiMan 命令均使用同一个 root 账号,避免读取不同用户的 ~/.certiman/ 目录。
第一步:将已有订单接入 CertiMan CLI
本步骤把已有订单及匹配私钥交给 CLI 管理,取得后续更新所需的本地材料;不会在 1Panel 中创建证书记录或重新绑定网站。已由当前账号的 CLI 管理且材料齐全时,核对状态后可直接进入第二步。
1.1 确认订单并安装 CLI
在 CertCloud 打开网站当前证书对应的订单,确认可使用“自动交付”,无需重复购买或申请。若页面没有对应入口,请联系支持确认。
进入订单接入引导,选择“命令行工具 CLI”。1Panel 场景选择 Linux 页签。 在 CertCloud 目标订单中,从“自动交付”进入该引导,确认当前账户和订单正确。

通过 1Panel 的“终端”或 SSH 连接宿主机,以 root 身份按引导安装。使用 sudo 账号时,先执行 sudo -i 切换到 root;执行 whoami,确认输出为 root。
bash
curl -sSL https://certiman.trustasia.com/install.sh | sh
certiman version登录、申请、后续部署与定时任务应使用同一个 root 账号,避免读取不同用户的配置目录。遇到安装失败或需要升级时,再按需查看 CertiMan 安装说明。
1.2 登录并接入订单
在 1Panel 宿主机上,以前述 root 账号执行:
bash
certiman login status缓存有效且账户正确时无需重新授权;未登录或凭据失效时才运行 certiman login。在浏览器中核对登录账户及应用名称为 CertiMan 后完成授权。终端没有浏览器时,在自己的电脑上打开 CLI 输出的授权地址。需要查看授权图示时,再按需查看 CertiMan 快速上手。 
登录完成后,接入订单。将 YOUR_ORDER_ID 替换为接入引导中的实际订单号:
bash
certiman issue --reuse-order YOUR_ORDER_ID --private-key /root/cert-files/privkey.pem将私钥路径替换为与网站当前证书匹配的原始私钥路径。找不到私钥时参见文末找不到原始私钥时重新签发。
1.3 确认订单状态和本地材料
接入已签发订单后,查看 CLI 状态及本地文件。
bash
certiman status -d trustasia.com
ls -l /root/.certiman/cc/certs/trustasia.com/将示例域名替换为订单实际主域名。确认已签发,且目录中已有 fullchain.pem、privkey.pem 和 cert.yaml。目录名由订单主域名决定,请以 CLI 输出为准。

1.4 确认后续验证可自动完成
按订单提示保留验证记录:dns-persist 保留 TXT,dns-proxy 保留 CNAME,不设置会删除这些记录的清理操作。若每次验证都需要新记录,先按官方 DNS Hook 示例配置自动写入,再开启自动续期。安装网站部署脚本不会自动配置 DNS 验证。
第二步:核对已有证书 ID 和关联网站
打开「网站 → 证书」,记录目标手动导入证书的 ID、域名、有效期和关联网站;逐个确认网站 HTTPS 使用该记录,访问正常。
确认 OpenResty 和目标网站正常运行,证书覆盖各关联网站的实际访问域名。若已有其他证书更新任务,先确认影响范围,暂停可能同时修改该记录或关联网站的任务。
保留当前有效证书和匹配私钥的受限备份,以及原网站绑定信息。若API 更新后验收失败,应通过受信任面板将备份材料恢复到同一记录,核对所有关联网站。
第三步:配置 1Panel API 与部署 Hook
完成第二步的核对后,为既有证书 ID 配置 API 和部署 Hook。Hook 更新记录中的证书与私钥内容,原网站绑定保持不变。
3.1 创建 1Panel API 配置
在 1Panel 中按以下路径打开 API 设置:
text
左下角面板设置
→「API Key」
→「编辑/创建 API Key」不同版本入口可能不同,以实际界面为准;创建时可仅允许本机 127.0.0.1访问,并设置密钥到期时间。记录 API Key,不要公开。
回到 root 终端,创建 Hook 目录:
bash
mkdir -p /root/.certiman/hooks
chmod 700 /root/.certiman/hooks创建配置文件:
bash
nano /root/.certiman/hooks/1panel-api.conf写入:
ini
api_url=http://127.0.0.1:YOUR_1PANEL_PORT/api/v2
api_key=YOUR_1PANEL_API_KEY
ssl_id=YOUR_SSL_ID替换以下内容:
- 将
YOUR_1PANEL_PORT替换为浏览器访问 1Panel 管理页面时使用的端口号,即网址中冒号后的数字。 - 将
YOUR_1PANEL_API_KEY替换为 API Key。 - 将
YOUR_SSL_ID替换为第二步核对的目标证书 ID。
重要:填写
ssl_id前核对证书 ID
ssl_id指定部署 Hook 要更新的证书记录,填写错误可能覆盖其他网站的证书。请在 1Panel 证书列表中确认:
- ID 对应第二步核对的已有证书记录。
- 关联网站为本次需要更新证书的网站。
确认无误后,再将该 ID 写入
ssl_id。不要根据证书列表顺序猜测 ID。
设置权限:
bash
chmod 600 /root/.certiman/hooks/1panel-api.conf3.2 创建部署脚本
脚本适用范围
脚本用于 1Panel v2,更新第二步记录的证书 ID。执行后在面板核对该记录和关联网站,其他版本不要直接套用。
创建脚本:
bash
nano /root/.certiman/hooks/1panel-ssl.py写入:
python
#!/usr/bin/env python3
import hashlib
import hmac
import json
import os
import sys
import time
import urllib.error
import urllib.request
CONFIG_FILE = os.environ.get(
"CERTIMAN_1PANEL_CONFIG", "/root/.certiman/hooks/1panel-api.conf"
)
def fail(message):
print(f"Error: {message}", file=sys.stderr)
raise SystemExit(1)
def read_config():
config = {}
try:
with open(CONFIG_FILE, encoding="utf-8") as stream:
for raw_line in stream:
line = raw_line.strip()
if not line or line.startswith("#"):
continue
key, value = line.split("=", 1)
config[key.strip()] = value.strip()
except (OSError, ValueError) as exc:
fail(f"cannot read {CONFIG_FILE}: {exc}")
for key in ("api_url", "api_key", "ssl_id"):
if not config.get(key):
fail(f"missing {key} in {CONFIG_FILE}")
return config
def main():
config = read_config()
domain = os.environ.get("CERTIMAN_CERT_NAME")
cert_path = os.environ.get("CERTIMAN_CERT_PATH")
key_path = os.environ.get("CERTIMAN_KEY_PATH")
if not domain or not cert_path or not key_path:
fail("missing CertiMan deploy variables")
if not os.path.isfile(cert_path):
fail(f"certificate file not found: {cert_path}")
if not os.path.isfile(key_path):
fail(f"private key file not found: {key_path}")
timestamp = str(int(time.time()))
token = hmac.new(
config["api_key"].encode(),
f"1panel:{timestamp}".encode(),
hashlib.sha256,
).hexdigest()
payload = json.dumps(
{
"type": "local",
"sslID": int(config["ssl_id"]),
"privateKeyPath": key_path,
"certificatePath": cert_path,
"description": "CertiMan managed",
"pushNode": False,
"nodes": "",
}
).encode()
request = urllib.request.Request(
f"{config['api_url'].rstrip('/')}/websites/ssl/upload",
data=payload,
method="POST",
headers={
"Content-Type": "application/json",
"1Panel-Token": token,
"1Panel-Timestamp": timestamp,
},
)
try:
with urllib.request.urlopen(request, timeout=30) as response:
result = json.loads(response.read().decode())
except urllib.error.HTTPError as exc:
detail = exc.read().decode(errors="replace")
fail(f"1Panel returned HTTP {exc.code}: {detail}")
except (OSError, urllib.error.URLError, json.JSONDecodeError) as exc:
fail(f"1Panel request failed: {exc}")
if not isinstance(result, dict) or result.get("code") not in (0, 200):
fail(f"1Panel rejected the update: {result}")
print(
f"Updated 1Panel SSL ID {config['ssl_id']} for {domain}"
)
if __name__ == "__main__":
main()设置权限:
bash
chmod 700 /root/.certiman/hooks/1panel-ssl.py检查脚本语法:
bash
python3 -m py_compile /root/.certiman/hooks/1panel-ssl.py命令没有输出表示语法正确。
3.3 绑定部署脚本
先备份配置:
bash
cp -a \
/root/.certiman/cc/certs/trustasia.com/cert.yaml \
/root/.certiman/cc/certs/trustasia.com/cert.yaml.backup编辑配置:
bash
nano /root/.certiman/cc/certs/trustasia.com/cert.yaml找到 deploy,改为:
yaml
deploy: "/root/.certiman/hooks/1panel-ssl.py"不要改动其他订单和域名配置。
第四步:测试部署并验证网站访问
确认第二步核对的目标未变化:1Panel 中已有本次要更新的手动证书记录,并已绑定目标网站;Hook 配置中的 ssl_id 与该记录一致。
4.1 执行部署
bash
certiman deploy -d trustasia.com如果命令提示已经部署,请强制重新同步:
bash
certiman deploy -d trustasia.com -f需要单独排查 Hook 时,可使用 certiman hook test -d trustasia.com --step deploy,详见 Hook 试运行说明。该命令也会调用真实的 1Panel API,不是只检查语法;正常接入无需再重复执行一次。
4.2 检查 CertiMan 部署状态
执行:
bash
certiman status -d trustasia.com -o json确认部署退出码为 0:
json
"deploy": {
"exit_code": 0
}4.3 检查 1Panel 与实际 HTTPS
登录 1Panel,打开“网站 > 证书”。
确认目标记录的生效时间和到期时间与 CertiMan 本地证书一致。接入时部署的是当前同一张证书,日期和指纹可能不变;以后取得新材料时,再核对更新结果。

先在运行 CLI 的 1Panel 服务器终端查看本地证书的域名和有效期:
bash
openssl x509 -in /root/.certiman/cc/certs/trustasia.com/fullchain.pem \
-noout -subject -dates再从可访问站点的客户端检查 HTTPS,核对浏览器证书的域名及有效期;若 HTTPS 在 CDN、WAF 或负载均衡处终止,还需分别检查实际公网入口和源站:
bash
openssl s_client -connect trustasia.com:443 \
-servername trustasia.com -verify_hostname trustasia.com \
-verify_return_error </dev/null网站页面、登录和接口均正常后,再进入第五步启用计划任务。
完成条件: 更新的是预期证书 ID,域名及有效期正确,没有证书警告,原网站设置保留,信任链校验通过,网站页面、登录和接口正常。
怀疑入口仍使用旧证书时,再按旧证书排查指南比较指纹。
第五步:开启并确认自动续期
后续域名验证沿用第一步已确认的方式:持久记录在授权有效期间继续保留;需要动态验证时维护对应 DNS Hook。不要配置会删除持久记录的清理操作。
5.1 开启证书续期
第四步验收通过后,编辑证书目录中的 cert.yaml,修改已有的续期字段并保留其他配置:
yaml
renew:
auto_cert: true
auto_order: false
days_before: 0auto_cert 开启订单服务期内的自动续期;auto_order: false 表示不自动创建付费续订订单,请提前安排订单续费。
5.2 安装并检查定时任务
由 CLI 为当前账号注册系统计划任务,定期执行 certiman cron;已经注册时无需重复安装:
bash
certiman cron install检查定时任务:
bash
certiman cron status
crontab -l以上命令即可完成 Linux 定时任务注册。需要了解其他平台或高级注册选项时,再按需查看 cron install 命令说明。
进入续期窗口后,按以下流程自动更新:
- CertiMan 自动续期证书,并下载新证书和新私钥。
- CertiMan 自动调用部署 Hook,更新已有 1Panel 证书 ID 对应的证书和私钥文件内容;证书 ID 不变。
- 1Panel 自动将更新后的证书应用到使用该证书 ID 的网站。
管理多张证书
每张证书在 CertiMan 中都有自己的 cert.yaml。Cron 会逐张检查证书,并执行各自 cert.yaml 中的 deploy 命令。
以下以 trustasia.com 和 trustasia.cn 两张独立证书为例。两张证书及网站 HTTPS 应已完成手动部署;按第一、二步分别接入已有订单,核对各自的 1Panel 证书 ID 与关联网站,不重新导入或绑定。
为每张证书创建配置文件
创建第一张证书的配置文件:
bash
nano /root/.certiman/hooks/trustasia.com.conf写入:
ini
api_url=http://127.0.0.1:YOUR_1PANEL_PORT/api/v2
api_key=YOUR_1PANEL_API_KEY
ssl_id=TRUSTASIA_COM_SSL_ID创建第二张证书的配置文件:
bash
nano /root/.certiman/hooks/trustasia.cn.conf写入:
ini
api_url=http://127.0.0.1:YOUR_1PANEL_PORT/api/v2
api_key=YOUR_1PANEL_API_KEY
ssl_id=TRUSTASIA_CN_SSL_ID将端口、API Key 和证书 ID 替换为实际值。两张证书位于同一个 1Panel 时,可以使用同一个 API Key,但两个 ssl_id 必须分别对应正确的证书记录。
收紧两个配置文件的权限:
bash
chmod 600 /root/.certiman/hooks/trustasia.com.conf
chmod 600 /root/.certiman/hooks/trustasia.cn.conf为每张证书创建入口 Hook
创建第一张证书的入口 Hook:
bash
nano /root/.certiman/hooks/deploy-trustasia.com.sh写入:
sh
#!/bin/sh
set -eu
export CERTIMAN_1PANEL_CONFIG=/root/.certiman/hooks/trustasia.com.conf
exec /root/.certiman/hooks/1panel-ssl.py创建第二张证书的入口 Hook:
bash
nano /root/.certiman/hooks/deploy-trustasia.cn.sh写入:
sh
#!/bin/sh
set -eu
export CERTIMAN_1PANEL_CONFIG=/root/.certiman/hooks/trustasia.cn.conf
exec /root/.certiman/hooks/1panel-ssl.py设置执行权限:
bash
chmod 700 /root/.certiman/hooks/deploy-trustasia.com.sh
chmod 700 /root/.certiman/hooks/deploy-trustasia.cn.sh分别绑定到 CertiMan 证书
先分别备份两份证书配置:
bash
cp -a /root/.certiman/cc/certs/trustasia.com/cert.yaml \
/root/.certiman/cc/certs/trustasia.com/cert.yaml.backup
cp -a /root/.certiman/cc/certs/trustasia.cn/cert.yaml \
/root/.certiman/cc/certs/trustasia.cn/cert.yaml.backup编辑第一张证书的配置:
bash
nano /root/.certiman/cc/certs/trustasia.com/cert.yaml将 deploy 改为:
yaml
deploy: "/root/.certiman/hooks/deploy-trustasia.com.sh"编辑第二张证书的配置:
bash
nano /root/.certiman/cc/certs/trustasia.cn/cert.yaml将 deploy 改为:
yaml
deploy: "/root/.certiman/hooks/deploy-trustasia.cn.sh"只替换各自文件中的 deploy 字段,不要改动订单、域名或其他配置。每张证书的 renew.auto_cert 都需要按第五步设置为 true。
逐张测试
分别执行部署,再按第四步验收;正常接入无需先执行 Hook 试运行:
bash
certiman deploy -d trustasia.com
certiman deploy -d trustasia.cn分别核对两条 1Panel 证书记录和对应网站。需要排查时再使用 Hook 试运行,它也会真实调用本机 1Panel API。
检查定时任务
bash
certiman cron status只需要安装一次 Cron。它会检查当前 CertiMan 数据目录中的受管证书;对已启用 renew.auto_cert: true 且进入续期窗口的证书完成续期后,调用该证书 cert.yaml 中配置的入口 Hook。
如有第三张证书,按上述步骤新增该证书专属的 .conf、入口 Hook 和 deploy 绑定。需要不同部署逻辑时,也可以在该证书的 deploy 字段指定完全不同的脚本;其他证书不受影响。
后续如何维护?
定期检查证书、调度和 1Panel 网站状态:
bash
certiman status -d trustasia.com
certiman cron status
certiman doctor检查 ~/.certiman/logs/ 中的错误。证书已下载但部署失败时,先确认 1Panel API Key、IP 白名单、证书 ID 和网站绑定是否变化;修复后运行 certiman deploy -d trustasia.com,上次已成功但需要重新同步时添加 -f,无需为部署失败重新下单。
1Panel 重新安装、迁移或重新导入证书后,证书 ID 可能变化。应更新对应 .conf 中的 ssl_id,重新执行第四步验收,再恢复自动调度。API Key 轮换后,也要同步更新配置文件并保持 0600 权限。
auto_order: false 不会自动创建付费续订订单,请在订单服务期结束前安排续费,并及时处理审核、域名验证或订单状态异常。
卸载 CertiMan(不再使用时)
如不再使用 CertiMan,请在 1Panel 宿主机上,以安装和配置定时任务时的 root 账号执行官方卸载脚本,并按提示确认:
bash
curl -sSL https://certiman.trustasia.com/uninstall.sh | sh卸载脚本会移除 CertiMan 注册的定时任务和程序,默认保留 /root/.certiman 中的证书、私钥及授权数据。卸载后不再由该任务自动续期,请自行安排后续证书更新。详见官方卸载指引。
找不到原始私钥时重新签发
有匹配私钥时按第一步导入,不执行本节。只有无法找回原始私钥时才重新签发;将 YOUR_ORDER_ID 替换为实际订单号。
如果无法找回原始私钥,首次接管时指定订单并重颁发:
bash
certiman issue --reuse-order YOUR_ORDER_ID --on-existing-cert reissue如果该订单已由本机 CertiMan 管理,使用已有的本地订单配置:
bash
certiman issue -d trustasia.com --on-existing-cert reissue**重颁发可能使旧证书失效。**完成所选操作后,返回第一步的“确认订单状态和本地材料”检查状态和本地文件;签发完成后,立即执行后续部署步骤。
