Skip to content

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 目标订单中,从“自动交付”进入该引导,确认当前账户和订单正确。

CertiMan CLI 接入引导

通过 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 快速上手。 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.conf

3.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 本地证书一致。接入时部署的是当前同一张证书,日期和指纹可能不变;以后取得新材料时,再核对更新结果。

1Panel 证书更新结果

先在运行 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: 0

auto_cert 开启订单服务期内的自动续期;auto_order: false 表示不自动创建付费续订订单,请提前安排订单续费。

5.2 安装并检查定时任务 ​

由 CLI 为当前账号注册系统计划任务,定期执行 certiman cron;已经注册时无需重复安装:

bash
certiman cron install

检查定时任务:

bash
certiman cron status
crontab -l

以上命令即可完成 Linux 定时任务注册。需要了解其他平台或高级注册选项时,再按需查看 cron install 命令说明。

进入续期窗口后,按以下流程自动更新:

  1. CertiMan 自动续期证书,并下载新证书和新私钥。
  2. CertiMan 自动调用部署 Hook,更新已有 1Panel 证书 ID 对应的证书和私钥文件内容;证书 ID 不变。
  3. 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

**重颁发可能使旧证书失效。**完成所选操作后,返回第一步的“确认订单状态和本地材料”检查状态和本地文件;签发完成后,立即执行后续部署步骤。

相关文档 ​

参考链接 ​