发帖

关于毅融盾支持Certd自动部署的公告

前言

关于毅融盾

毅融盾是我们近期发布的全新平台,深度融合网宿与白山 CDN 资源,提供多源智能调度、全网极速分发与立体安全防护,助力业务实现高可用、低延迟的边缘加速。

痛点与解决方案

随着 SSL 证书有效期不断缩短(从传统的 1-2 年逐步过渡至 90 天),频繁手动更换证书已成为运维工作中的痛点。手动操作不仅耗时费力,还容易因疏忽导致证书过期,进而影响业务可用性。

为解决这一难题,我们自主研发了毅融盾 CDN 的 SSL 证书自动部署模块,并成功将其集成至Certd系统中。然而遗憾的是,该功能暂未被Certd 官方项目采纳。

为让更多有需要的用户受益,我们决定将实现方案整理成教程分享出来,供大家参考。教程中涉及的所有修改文件均已附于文末附件中。

一、导入部署插件

二、创建流水线并选择部署插件并配置

获取域名ID

  1. 登录毅融盾 CDN 控制台
  2. 进入「我的域名」页面
  3. 点击目标域名的「配置」按钮
  4. 从浏览器地址栏复制域名 ID

注意事项

  1. 域名ID 必须与证书域名完全匹配
  2. 首次部署建议手动验证证书是否生效
  3. 建议开启自动续期,确保证书持续有效

插件:

name: HYRCDNDeploy
icon: fa-brands:keycdn:#007AFF
title: 毅融盾-部署证书至CDN
group: cdn
desc: 自动登录毅融盾平台并将 SSL 证书部署到指定 CDN 域名
setting: null
sysSetting: null
type: custom
disabled: false
version: 1.0.0
pluginType: deploy
author: hydun
default:
  strategy:
    runStrategy: 1
input:
  cert:
    title: 前置任务证书
    helper: 请选择前置任务产生的证书(需包含 fullchain 和 privateKey)
    component:
      name: output-selector
      vModel: modelValue
      from:
        - ':cert:'
    required: true
  certDomains:
    title: 当前证书域名
    component:
      name: cert-domains-getter
    mergeScript: |
      return {
        component: {
          inputKey: ctx.compute(({ form }) => form.cert)
        }
      }
    required: false
  username:
    title: 控制台账号
    helper: 毅融盾 CDN 控制台登录账号
    required: true
  password:
    title: 控制台密码
    helper: 毅融盾 CDN 控制台登录密码
    encrypt: true
    required: true
  domainId:
    title: 域名ID
    helper: 在毅融盾控制台域名列表查看,一般为数字
    required: true
showRunStrategy: false
content: |-
  const { AbstractTaskPlugin } = await import("@certd/pipeline");

  return class YrdcdnDeployTask extends AbstractTaskPlugin {
    cert;
    certDomains;
    username;
    password;
    domainId;

    async execute() {
      this.logger.info("开始部署证书到毅融盾 CDN");

      // 必要参数校验
      if (!this.username) throw new Error("缺少配置:username(控制台账号)");
      if (!this.password) throw new Error("缺少配置:password(控制台密码)");
      if (!this.domainId) throw new Error("缺少配置:domainId(域名ID)");

      // 可选:打印证书对象(调试用)
      // this.logger.info("证书对象内容: ", JSON.stringify(this.cert, null, 2));

      // 自动适配证书字段
      const fullchain = this.cert?.fullchain || this.cert?.crt;
      const privatekey = this.cert?.privateKey || this.cert?.key;

      if (!fullchain || !privatekey) {
        throw new Error("证书内容不完整,请检查前置任务输出的证书字段");
      }

      // 1. 登录获取 Token(表单格式)
      this.logger.info("正在登录毅融盾平台...");

      const formBody = `userAccount=${encodeURIComponent(this.username)}&userPwd=${encodeURIComponent(this.password)}&remember=true`;

      const loginRes = await this.ctx.http.request({
        url: "https://rcdn.hydun.com/login/loginUser",
        method: "post",
        headers: {
          "Content-Type": "application/x-www-form-urlencoded",
        },
        data: formBody,
      });

      // 获取响应体(可能是字符串或对象)
      const responseBody = loginRes?.data ?? loginRes;
      this.logger.info("登录响应类型:", typeof responseBody);
      this.logger.info("登录响应内容(前100字符):", typeof responseBody === 'string' ? responseBody.substring(0, 100) : JSON.stringify(responseBody).substring(0, 100));

      let token = null;

      // 情况1:响应是纯字符串(JWT token),直接视为成功
      if (typeof responseBody === 'string' && responseBody.trim().length > 0 && responseBody.includes('.')) {
        // 简单判断:包含两个点,很像JWT
        token = responseBody.trim();
        this.logger.info("检测到直接返回的Token字符串,登录成功");
      }
      // 情况2:响应是JSON对象,包含 code 字段
      else if (responseBody && typeof responseBody === 'object') {
        if (responseBody.code === "SUCCESS") {
          token = responseBody.data;
          this.logger.info("登录成功(JSON格式),Token已获取");
        } else {
          const errMsg = responseBody.message || responseBody.msg || "未知错误";
          throw new Error(`登录失败:${errMsg},完整响应:${JSON.stringify(responseBody)}`);
        }
      }
      // 情况3:其他情况(空字符串、无效内容)
      else {
        throw new Error(`登录失败:响应格式异常,内容:${typeof responseBody === 'string' ? responseBody : JSON.stringify(responseBody)}`);
      }

      if (!token) {
        throw new Error("登录成功但未提取到Token");
      }

      // 2. 部署证书(JSON + Cookie)
      this.logger.info(`正在为域名ID ${this.domainId} 部署证书...`);
      const deployRes = await this.ctx.http.request({
        url: "https://rcdn.hydun.com/CdnDomainHttps/httpsConfiguration",
        method: "post",
        headers: {
          "Content-Type": "application/json",
          Cookie: `kuocai_cdn_token=${token}`,
        },
        data: {
          doMainId: this.domainId,
          https: {
            certificate_name: `certd_${Date.now()}`,
            certificate_source: "0",
            certificate_value: fullchain,
            https_status: "on",
            private_key: privatekey,
          },
        },
      });

      const deployBody = deployRes?.data ?? deployRes;
      // 部署接口可能也返回纯文本或JSON,同样做兼容
      if (typeof deployBody === 'string' && deployBody.includes('SUCCESS')) {
        this.logger.info(`域名ID ${this.domainId} 证书部署成功(纯文本响应)!`);
      } else if (deployBody && typeof deployBody === 'object' && deployBody.code === "SUCCESS") {
        this.logger.info(`域名ID ${this.domainId} 证书部署成功!`);
      } else {
        const errMsg = deployBody?.message || deployBody?.msg || (typeof deployBody === 'string' ? deployBody : JSON.stringify(deployBody));
        throw new Error(`证书部署失败:${errMsg}`);
      }
    }
  };
上一篇 关于彩虹聚合DNS系统接入毅融盾的教程 下一篇 关于火毅盾CDN启动联通、移动方向节点扩容解决跨网问题的公告
评论区
游客
游客

还没有人评论,快来抢沙发吧~

在线客服
工作日在线,欢迎咨询购买、授权、部署与售后问题。
在线时间周一至周五 09:00 - 18:00
如遇支付、授权、安装、定制等问题,可优先联系在线客服,我们会尽快协助处理。