2025-01-02 00:28:13 +08:00
|
|
|
|
import { CancelError, IsTaskPlugin, pluginGroups, RunStrategy, TaskInput } from "@certd/pipeline";
|
2024-11-04 15:14:56 +08:00
|
|
|
|
import { utils } from "@certd/basic";
|
|
|
|
|
|
|
2025-01-03 00:12:15 +08:00
|
|
|
|
import type { CertInfo, CnameVerifyPlan, DomainsVerifyPlan, HttpVerifyPlan, PrivateKeyType, SSLProvider } from "./acme.js";
|
2024-07-18 21:10:13 +08:00
|
|
|
|
import { AcmeService } from "./acme.js";
|
2024-11-08 23:43:19 +08:00
|
|
|
|
import * as _ from "lodash-es";
|
2025-04-11 12:13:57 +08:00
|
|
|
|
import { createDnsProvider, DnsProviderContext, IDnsProvider, ISubDomainsGetter } from "../../dns-provider/index.js";
|
2024-07-15 00:30:33 +08:00
|
|
|
|
import { CertReader } from "./cert-reader.js";
|
2024-07-18 21:10:13 +08:00
|
|
|
|
import { CertApplyBasePlugin } from "./base.js";
|
2024-10-10 13:29:08 +08:00
|
|
|
|
import { GoogleClient } from "../../libs/google.js";
|
2024-10-14 03:17:10 +08:00
|
|
|
|
import { EabAccess } from "../../access";
|
2025-04-11 12:13:57 +08:00
|
|
|
|
import { DomainParser } from "../../dns-provider/domain-parser.js";
|
2025-04-25 01:26:04 +08:00
|
|
|
|
import { ossClientFactory } from "@certd/plugin-lib";
|
2025-01-15 01:05:34 +08:00
|
|
|
|
export * from "./base.js";
|
2023-05-23 18:01:20 +08:00
|
|
|
|
export type { CertInfo };
|
2024-09-05 15:36:35 +08:00
|
|
|
|
export * from "./cert-reader.js";
|
2024-10-07 03:21:16 +08:00
|
|
|
|
export type CnameRecordInput = {
|
|
|
|
|
|
id: number;
|
|
|
|
|
|
status: string;
|
|
|
|
|
|
};
|
2025-01-02 00:28:13 +08:00
|
|
|
|
|
|
|
|
|
|
export type HttpRecordInput = {
|
|
|
|
|
|
domain: string;
|
|
|
|
|
|
httpUploaderType: string;
|
|
|
|
|
|
httpUploaderAccess: number;
|
|
|
|
|
|
httpUploadRootDir: string;
|
|
|
|
|
|
};
|
2024-10-07 03:21:16 +08:00
|
|
|
|
export type DomainVerifyPlanInput = {
|
|
|
|
|
|
domain: string;
|
2025-01-02 00:28:13 +08:00
|
|
|
|
type: "cname" | "dns" | "http";
|
2024-10-07 03:21:16 +08:00
|
|
|
|
dnsProviderType?: string;
|
2025-04-12 01:21:50 +08:00
|
|
|
|
dnsProviderAccessType?: string;
|
2024-10-07 03:21:16 +08:00
|
|
|
|
dnsProviderAccessId?: number;
|
|
|
|
|
|
cnameVerifyPlan?: Record<string, CnameRecordInput>;
|
2025-01-02 00:28:13 +08:00
|
|
|
|
httpVerifyPlan?: Record<string, HttpRecordInput>;
|
2024-10-07 03:21:16 +08:00
|
|
|
|
};
|
|
|
|
|
|
export type DomainsVerifyPlanInput = {
|
|
|
|
|
|
[key: string]: DomainVerifyPlanInput;
|
|
|
|
|
|
};
|
2023-01-11 20:39:48 +08:00
|
|
|
|
|
2022-12-29 23:52:51 +08:00
|
|
|
|
@IsTaskPlugin({
|
|
|
|
|
|
name: "CertApply",
|
2024-07-21 02:26:03 +08:00
|
|
|
|
title: "证书申请(JS版)",
|
2024-09-19 17:38:51 +08:00
|
|
|
|
icon: "ph:certificate",
|
2024-07-21 02:26:03 +08:00
|
|
|
|
group: pluginGroups.cert.key,
|
2022-12-29 23:52:51 +08:00
|
|
|
|
desc: "免费通配符域名证书申请,支持多个域名打到同一个证书上",
|
|
|
|
|
|
default: {
|
2022-11-08 22:10:42 +08:00
|
|
|
|
input: {
|
2024-10-28 15:31:45 +08:00
|
|
|
|
renewDays: 35,
|
2022-12-29 23:52:51 +08:00
|
|
|
|
forceUpdate: false,
|
2022-11-08 22:10:42 +08:00
|
|
|
|
},
|
2022-12-29 23:52:51 +08:00
|
|
|
|
strategy: {
|
|
|
|
|
|
runStrategy: RunStrategy.AlwaysRun,
|
2022-11-08 22:10:42 +08:00
|
|
|
|
},
|
2022-12-29 23:52:51 +08:00
|
|
|
|
},
|
2022-11-08 22:10:42 +08:00
|
|
|
|
})
|
2024-07-18 21:10:13 +08:00
|
|
|
|
export class CertApplyPlugin extends CertApplyBasePlugin {
|
2024-10-07 03:21:16 +08:00
|
|
|
|
@TaskInput({
|
|
|
|
|
|
title: "域名验证方式",
|
|
|
|
|
|
value: "dns",
|
|
|
|
|
|
component: {
|
|
|
|
|
|
name: "a-select",
|
|
|
|
|
|
vModel: "value",
|
|
|
|
|
|
options: [
|
|
|
|
|
|
{ value: "dns", label: "DNS直接验证" },
|
2024-10-10 22:13:07 +08:00
|
|
|
|
{ value: "cname", label: "CNAME代理验证" },
|
2025-01-02 00:28:13 +08:00
|
|
|
|
{ value: "http", label: "HTTP文件验证" },
|
2024-10-07 03:21:16 +08:00
|
|
|
|
],
|
|
|
|
|
|
},
|
|
|
|
|
|
required: true,
|
2025-05-06 10:57:07 +08:00
|
|
|
|
helper: `1. DNS直接验证:域名是在阿里云/腾讯云/华为云/CF/NameSilo/西数/火山/dns.la/京东云注册的,选它;
|
|
|
|
|
|
2. CNAME代理验证:支持任何注册商注册的域名,但第一次需要手动添加CNAME记录(建议直接修改为阿里云/腾讯云的DNS服务器地址,然后使用DNS直接验证);
|
|
|
|
|
|
3. HTTP文件验证:不支持泛域名,需要配置网站文件上传`,
|
2024-10-07 03:21:16 +08:00
|
|
|
|
})
|
|
|
|
|
|
challengeType!: string;
|
|
|
|
|
|
|
2024-12-24 23:14:12 +08:00
|
|
|
|
@TaskInput({
|
|
|
|
|
|
title: "证书颁发机构",
|
|
|
|
|
|
value: "letsencrypt",
|
|
|
|
|
|
component: {
|
|
|
|
|
|
name: "icon-select",
|
|
|
|
|
|
vModel: "value",
|
|
|
|
|
|
options: [
|
|
|
|
|
|
{ value: "letsencrypt", label: "Let's Encrypt", icon: "simple-icons:letsencrypt" },
|
|
|
|
|
|
{ value: "google", label: "Google", icon: "flat-color-icons:google" },
|
|
|
|
|
|
{ value: "zerossl", label: "ZeroSSL", icon: "emojione:digit-zero" },
|
|
|
|
|
|
],
|
|
|
|
|
|
},
|
|
|
|
|
|
helper: "Let's Encrypt:申请最简单\nGoogle:大厂光环,兼容性好,仅首次需要翻墙获取EAB授权\nZeroSSL:需要EAB授权,无需翻墙",
|
|
|
|
|
|
required: true,
|
|
|
|
|
|
})
|
|
|
|
|
|
sslProvider!: SSLProvider;
|
|
|
|
|
|
|
2024-10-07 03:21:16 +08:00
|
|
|
|
@TaskInput({
|
2024-10-30 09:55:42 +08:00
|
|
|
|
title: "DNS解析服务商",
|
2024-10-07 03:21:16 +08:00
|
|
|
|
component: {
|
|
|
|
|
|
name: "dns-provider-selector",
|
|
|
|
|
|
},
|
|
|
|
|
|
mergeScript: `
|
|
|
|
|
|
return {
|
|
|
|
|
|
show: ctx.compute(({form})=>{
|
|
|
|
|
|
return form.challengeType === 'dns'
|
2025-04-12 01:21:50 +08:00
|
|
|
|
}),
|
|
|
|
|
|
component:{
|
|
|
|
|
|
on:{
|
|
|
|
|
|
selectedChange({form,$event}){
|
|
|
|
|
|
form.dnsProviderAccessType = $event.accessType
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2024-10-07 03:21:16 +08:00
|
|
|
|
}
|
|
|
|
|
|
`,
|
|
|
|
|
|
required: true,
|
2024-10-30 10:35:14 +08:00
|
|
|
|
helper: "您的域名注册商,或者域名的dns服务器属于哪个平台\n如果这里没有,请选择CNAME代理验证校验方式",
|
2024-10-07 03:21:16 +08:00
|
|
|
|
})
|
|
|
|
|
|
dnsProviderType!: string;
|
|
|
|
|
|
|
2025-04-25 01:26:04 +08:00
|
|
|
|
// dns解析授权类型,勿删
|
2025-04-12 01:21:50 +08:00
|
|
|
|
dnsProviderAccessType!: string;
|
|
|
|
|
|
|
2024-10-07 03:21:16 +08:00
|
|
|
|
@TaskInput({
|
|
|
|
|
|
title: "DNS解析授权",
|
|
|
|
|
|
component: {
|
|
|
|
|
|
name: "access-selector",
|
|
|
|
|
|
},
|
|
|
|
|
|
required: true,
|
2024-10-30 09:55:42 +08:00
|
|
|
|
helper: "请选择dns解析服务商授权",
|
2024-10-07 03:21:16 +08:00
|
|
|
|
mergeScript: `return {
|
|
|
|
|
|
component:{
|
|
|
|
|
|
type: ctx.compute(({form})=>{
|
2025-04-12 01:21:50 +08:00
|
|
|
|
return form.dnsProviderAccessType || form.dnsProviderType
|
2024-10-07 03:21:16 +08:00
|
|
|
|
})
|
|
|
|
|
|
},
|
|
|
|
|
|
show: ctx.compute(({form})=>{
|
|
|
|
|
|
return form.challengeType === 'dns'
|
|
|
|
|
|
})
|
|
|
|
|
|
}
|
|
|
|
|
|
`,
|
|
|
|
|
|
})
|
|
|
|
|
|
dnsProviderAccess!: number;
|
|
|
|
|
|
|
|
|
|
|
|
@TaskInput({
|
|
|
|
|
|
title: "域名验证配置",
|
|
|
|
|
|
component: {
|
|
|
|
|
|
name: "domains-verify-plan-editor",
|
|
|
|
|
|
},
|
2025-01-03 00:12:15 +08:00
|
|
|
|
rules: [{ type: "checkDomainVerifyPlan" }],
|
2024-10-07 03:21:16 +08:00
|
|
|
|
required: true,
|
|
|
|
|
|
col: {
|
|
|
|
|
|
span: 24,
|
|
|
|
|
|
},
|
|
|
|
|
|
mergeScript: `return {
|
|
|
|
|
|
component:{
|
|
|
|
|
|
domains: ctx.compute(({form})=>{
|
|
|
|
|
|
return form.domains
|
2025-01-03 00:12:15 +08:00
|
|
|
|
}),
|
|
|
|
|
|
defaultType: ctx.compute(({form})=>{
|
|
|
|
|
|
return form.challengeType || 'cname'
|
2024-10-07 03:21:16 +08:00
|
|
|
|
})
|
|
|
|
|
|
},
|
|
|
|
|
|
show: ctx.compute(({form})=>{
|
2025-01-03 00:12:15 +08:00
|
|
|
|
return form.challengeType === 'cname' || form.challengeType === 'http'
|
|
|
|
|
|
}),
|
|
|
|
|
|
helper: ctx.compute(({form})=>{
|
|
|
|
|
|
if(form.challengeType === 'cname' ){
|
|
|
|
|
|
return '请按照上面的提示,给要申请证书的域名添加CNAME记录,添加后,点击验证,验证成功后不要删除记录,申请和续期证书会一直用它'
|
|
|
|
|
|
}else if (form.challengeType === 'http'){
|
|
|
|
|
|
return '请按照上面的提示,给每个域名设置文件上传配置,证书申请过程中会上传校验文件到网站根目录下'
|
|
|
|
|
|
}
|
2024-10-07 03:21:16 +08:00
|
|
|
|
})
|
|
|
|
|
|
}
|
|
|
|
|
|
`,
|
|
|
|
|
|
})
|
|
|
|
|
|
domainsVerifyPlan!: DomainsVerifyPlanInput;
|
|
|
|
|
|
|
2024-10-14 03:17:10 +08:00
|
|
|
|
@TaskInput({
|
|
|
|
|
|
title: "Google公共EAB授权",
|
|
|
|
|
|
isSys: true,
|
|
|
|
|
|
show: false,
|
|
|
|
|
|
})
|
|
|
|
|
|
googleCommonEabAccessId!: number;
|
|
|
|
|
|
|
2024-10-15 12:59:40 +08:00
|
|
|
|
@TaskInput({
|
|
|
|
|
|
title: "ZeroSSL公共EAB授权",
|
|
|
|
|
|
isSys: true,
|
|
|
|
|
|
show: false,
|
|
|
|
|
|
})
|
|
|
|
|
|
zerosslCommonEabAccessId!: number;
|
|
|
|
|
|
|
2024-09-04 15:49:00 +08:00
|
|
|
|
@TaskInput({
|
|
|
|
|
|
title: "EAB授权",
|
|
|
|
|
|
component: {
|
2024-10-07 03:21:16 +08:00
|
|
|
|
name: "access-selector",
|
2024-09-04 15:49:00 +08:00
|
|
|
|
type: "eab",
|
|
|
|
|
|
},
|
|
|
|
|
|
maybeNeed: true,
|
2024-10-10 13:29:08 +08:00
|
|
|
|
required: false,
|
|
|
|
|
|
helper:
|
2024-10-25 21:47:28 +08:00
|
|
|
|
"需要提供EAB授权\nZeroSSL:请前往[zerossl开发者中心](https://app.zerossl.com/developer),生成 'EAB Credentials'\n Google:请查看[google获取eab帮助文档](https://certd.docmirror.cn/guide/use/google/),用过一次后会绑定邮箱,后续复用EAB要用同一个邮箱",
|
2024-10-10 13:29:08 +08:00
|
|
|
|
mergeScript: `
|
|
|
|
|
|
return {
|
|
|
|
|
|
show: ctx.compute(({form})=>{
|
2024-10-15 17:12:42 +08:00
|
|
|
|
return (form.sslProvider === 'zerossl' && !form.zerosslCommonEabAccessId) || (form.sslProvider === 'google' && !form.googleCommonEabAccessId)
|
2024-10-10 13:29:08 +08:00
|
|
|
|
})
|
|
|
|
|
|
}
|
|
|
|
|
|
`,
|
|
|
|
|
|
})
|
2024-10-10 15:12:39 +08:00
|
|
|
|
eabAccessId!: number;
|
2024-10-10 13:29:08 +08:00
|
|
|
|
|
|
|
|
|
|
@TaskInput({
|
2024-10-10 13:57:30 +08:00
|
|
|
|
title: "服务账号授权",
|
2024-10-10 13:29:08 +08:00
|
|
|
|
component: {
|
|
|
|
|
|
name: "access-selector",
|
|
|
|
|
|
type: "google",
|
|
|
|
|
|
},
|
|
|
|
|
|
maybeNeed: true,
|
|
|
|
|
|
required: false,
|
2025-03-21 11:07:15 +08:00
|
|
|
|
helper: "google服务账号授权与EAB授权选填其中一个,[服务账号授权获取方法](https://certd.docmirror.cn/guide/use/google/)\n服务账号授权需要配置代理或者服务器本身在海外",
|
2024-10-10 13:29:08 +08:00
|
|
|
|
mergeScript: `
|
|
|
|
|
|
return {
|
|
|
|
|
|
show: ctx.compute(({form})=>{
|
2024-10-14 03:17:10 +08:00
|
|
|
|
return form.sslProvider === 'google' && !form.googleCommonEabAccessId
|
2024-10-10 13:29:08 +08:00
|
|
|
|
})
|
|
|
|
|
|
}
|
|
|
|
|
|
`,
|
|
|
|
|
|
})
|
|
|
|
|
|
googleAccessId!: number;
|
|
|
|
|
|
|
2024-08-23 17:41:02 +08:00
|
|
|
|
@TaskInput({
|
2024-08-25 11:57:07 +08:00
|
|
|
|
title: "加密算法",
|
2024-08-25 11:56:15 +08:00
|
|
|
|
value: "rsa_2048",
|
2024-08-23 17:41:02 +08:00
|
|
|
|
component: {
|
|
|
|
|
|
name: "a-select",
|
|
|
|
|
|
vModel: "value",
|
|
|
|
|
|
options: [
|
2024-08-25 11:56:15 +08:00
|
|
|
|
{ value: "rsa_1024", label: "RSA 1024" },
|
|
|
|
|
|
{ value: "rsa_2048", label: "RSA 2048" },
|
|
|
|
|
|
{ value: "rsa_3072", label: "RSA 3072" },
|
|
|
|
|
|
{ value: "rsa_4096", label: "RSA 4096" },
|
2024-09-23 14:32:57 +08:00
|
|
|
|
{ value: "rsa_2048_pkcs1", label: "RSA 2048 pkcs1 (旧版)" },
|
2024-08-25 11:56:15 +08:00
|
|
|
|
{ value: "ec_256", label: "EC 256" },
|
|
|
|
|
|
{ value: "ec_384", label: "EC 384" },
|
2024-08-25 12:07:47 +08:00
|
|
|
|
// { value: "ec_521", label: "EC 521" },
|
2024-08-23 17:41:02 +08:00
|
|
|
|
],
|
|
|
|
|
|
},
|
2025-01-20 23:29:03 +08:00
|
|
|
|
helper: "如无特殊需求,默认即可\n选择RSA 2048 pkcs1可以获得旧版RSA证书",
|
2024-08-23 17:41:02 +08:00
|
|
|
|
required: true,
|
|
|
|
|
|
})
|
|
|
|
|
|
privateKeyType!: PrivateKeyType;
|
|
|
|
|
|
|
2024-07-25 10:38:45 +08:00
|
|
|
|
@TaskInput({
|
|
|
|
|
|
title: "使用代理",
|
2024-08-06 11:23:23 +08:00
|
|
|
|
value: false,
|
2024-07-25 10:38:45 +08:00
|
|
|
|
component: {
|
|
|
|
|
|
name: "a-switch",
|
|
|
|
|
|
vModel: "checked",
|
|
|
|
|
|
},
|
2024-10-07 03:21:16 +08:00
|
|
|
|
helper: "如果acme-v02.api.letsencrypt.org或dv.acme-v02.api.pki.goog被墙无法访问,请尝试开启此选项\n默认情况会进行测试,如果无法访问,将会自动使用代理",
|
2024-07-25 10:38:45 +08:00
|
|
|
|
})
|
|
|
|
|
|
useProxy = false;
|
|
|
|
|
|
|
2024-10-10 15:32:25 +08:00
|
|
|
|
@TaskInput({
|
|
|
|
|
|
title: "自定义反代地址",
|
|
|
|
|
|
component: {
|
|
|
|
|
|
placeholder: "google.yourproxy.com",
|
|
|
|
|
|
},
|
|
|
|
|
|
helper: "填写你的自定义反代地址,不要带http://\nletsencrypt反代目标:acme-v02.api.letsencrypt.org\ngoogle反代目标:dv.acme-v02.api.pki.goog",
|
|
|
|
|
|
})
|
|
|
|
|
|
reverseProxy = "";
|
|
|
|
|
|
|
2024-07-08 15:35:58 +08:00
|
|
|
|
@TaskInput({
|
2024-07-08 15:47:36 +08:00
|
|
|
|
title: "跳过本地校验DNS",
|
2024-08-06 11:23:23 +08:00
|
|
|
|
value: false,
|
2024-07-08 15:35:58 +08:00
|
|
|
|
component: {
|
|
|
|
|
|
name: "a-switch",
|
|
|
|
|
|
vModel: "checked",
|
|
|
|
|
|
},
|
2024-10-07 03:21:16 +08:00
|
|
|
|
helper: "跳过本地校验可以加快申请速度,同时也会增加失败概率。",
|
2024-07-08 15:35:58 +08:00
|
|
|
|
})
|
|
|
|
|
|
skipLocalVerify = false;
|
|
|
|
|
|
|
2025-03-21 11:07:15 +08:00
|
|
|
|
@TaskInput({
|
|
|
|
|
|
title: "检查解析重试次数",
|
2025-03-24 00:10:01 +08:00
|
|
|
|
value: 20,
|
2025-03-21 11:07:15 +08:00
|
|
|
|
component: {
|
|
|
|
|
|
name: "a-input-number",
|
|
|
|
|
|
vModel: "value",
|
|
|
|
|
|
},
|
|
|
|
|
|
helper: "检查域名验证解析记录重试次数,如果你的域名服务商解析生效速度慢,可以适当增加此值",
|
|
|
|
|
|
})
|
2025-03-24 00:10:01 +08:00
|
|
|
|
maxCheckRetryCount = 20;
|
2025-03-21 11:07:15 +08:00
|
|
|
|
|
2023-06-25 23:25:56 +08:00
|
|
|
|
acme!: AcmeService;
|
|
|
|
|
|
|
2024-10-14 03:17:10 +08:00
|
|
|
|
eab!: EabAccess;
|
2024-07-18 21:10:13 +08:00
|
|
|
|
async onInit() {
|
2024-10-14 03:17:10 +08:00
|
|
|
|
let eab: EabAccess = null;
|
2024-10-10 13:29:08 +08:00
|
|
|
|
|
|
|
|
|
|
if (this.sslProvider === "google") {
|
|
|
|
|
|
if (this.googleAccessId) {
|
2024-10-14 10:27:11 +08:00
|
|
|
|
this.logger.info("当前正在使用 google服务账号授权获取EAB");
|
2025-04-12 00:14:55 +08:00
|
|
|
|
const googleAccess = await this.getAccess(this.googleAccessId);
|
2024-10-10 13:29:08 +08:00
|
|
|
|
const googleClient = new GoogleClient({
|
|
|
|
|
|
access: googleAccess,
|
|
|
|
|
|
logger: this.logger,
|
|
|
|
|
|
});
|
|
|
|
|
|
eab = await googleClient.getEab();
|
2024-10-14 10:27:11 +08:00
|
|
|
|
} else if (this.eabAccessId) {
|
|
|
|
|
|
this.logger.info("当前正在使用 google EAB授权");
|
2025-04-12 00:14:55 +08:00
|
|
|
|
eab = await this.getAccess(this.eabAccessId);
|
2024-10-14 10:27:11 +08:00
|
|
|
|
} else if (this.googleCommonEabAccessId) {
|
|
|
|
|
|
this.logger.info("当前正在使用 google公共EAB授权");
|
2025-04-12 00:14:55 +08:00
|
|
|
|
eab = await this.getAccess(this.googleCommonEabAccessId, true);
|
2024-10-10 13:29:08 +08:00
|
|
|
|
} else {
|
2024-12-11 09:30:21 +08:00
|
|
|
|
throw new Error("google需要配置EAB授权或服务账号授权");
|
2024-10-10 13:29:08 +08:00
|
|
|
|
}
|
|
|
|
|
|
} else if (this.sslProvider === "zerossl") {
|
|
|
|
|
|
if (this.eabAccessId) {
|
2024-10-15 12:59:40 +08:00
|
|
|
|
this.logger.info("当前正在使用 zerossl EAB授权");
|
2025-04-12 00:14:55 +08:00
|
|
|
|
eab = await this.getAccess(this.eabAccessId);
|
2024-10-15 12:59:40 +08:00
|
|
|
|
} else if (this.zerosslCommonEabAccessId) {
|
|
|
|
|
|
this.logger.info("当前正在使用 zerossl 公共EAB授权");
|
2025-04-12 00:14:55 +08:00
|
|
|
|
eab = await this.getAccess(this.zerosslCommonEabAccessId, true);
|
2024-10-10 15:12:39 +08:00
|
|
|
|
} else {
|
2024-12-11 09:30:21 +08:00
|
|
|
|
throw new Error("zerossl需要配置EAB授权");
|
2024-10-10 13:29:08 +08:00
|
|
|
|
}
|
2024-07-04 01:14:09 +08:00
|
|
|
|
}
|
2024-10-14 03:17:10 +08:00
|
|
|
|
this.eab = eab;
|
2025-04-11 12:13:57 +08:00
|
|
|
|
const subDomainsGetter = await this.ctx.serviceGetter.get<ISubDomainsGetter>("subDomainsGetter");
|
2025-05-06 00:14:17 +08:00
|
|
|
|
const domainParser = new DomainParser(subDomainsGetter, this.logger);
|
2024-07-08 15:35:58 +08:00
|
|
|
|
this.acme = new AcmeService({
|
2025-04-11 12:13:57 +08:00
|
|
|
|
userId: this.ctx.user.id,
|
2024-07-08 15:35:58 +08:00
|
|
|
|
userContext: this.userContext,
|
|
|
|
|
|
logger: this.logger,
|
|
|
|
|
|
sslProvider: this.sslProvider,
|
|
|
|
|
|
eab,
|
|
|
|
|
|
skipLocalVerify: this.skipLocalVerify,
|
2024-07-25 10:38:45 +08:00
|
|
|
|
useMappingProxy: this.useProxy,
|
2024-10-10 15:32:25 +08:00
|
|
|
|
reverseProxy: this.reverseProxy,
|
2024-08-23 17:41:02 +08:00
|
|
|
|
privateKeyType: this.privateKeyType,
|
2024-11-01 02:13:34 +08:00
|
|
|
|
signal: this.ctx.signal,
|
2025-03-21 11:07:15 +08:00
|
|
|
|
maxCheckRetryCount: this.maxCheckRetryCount,
|
2025-04-11 12:13:57 +08:00
|
|
|
|
domainParser,
|
2024-07-08 15:35:58 +08:00
|
|
|
|
});
|
2022-11-08 22:10:42 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2022-12-29 23:52:51 +08:00
|
|
|
|
async doCertApply() {
|
2024-10-14 03:17:10 +08:00
|
|
|
|
let email = this.email;
|
|
|
|
|
|
if (this.eab && this.eab.email) {
|
|
|
|
|
|
email = this.eab.email;
|
|
|
|
|
|
}
|
2022-12-29 23:52:51 +08:00
|
|
|
|
const domains = this["domains"];
|
2024-10-07 03:21:16 +08:00
|
|
|
|
|
2022-11-08 22:10:42 +08:00
|
|
|
|
const csrInfo = _.merge(
|
|
|
|
|
|
{
|
|
|
|
|
|
country: "CN",
|
|
|
|
|
|
state: "GuangDong",
|
|
|
|
|
|
locality: "ShengZhen",
|
|
|
|
|
|
organization: "CertD Org.",
|
|
|
|
|
|
organizationUnit: "IT Department",
|
|
|
|
|
|
emailAddress: email,
|
|
|
|
|
|
},
|
2024-06-15 02:17:34 +08:00
|
|
|
|
this.csrInfo ? JSON.parse(this.csrInfo) : {}
|
2022-11-08 22:10:42 +08:00
|
|
|
|
);
|
|
|
|
|
|
this.logger.info("开始申请证书,", email, domains);
|
|
|
|
|
|
|
2025-01-02 00:28:13 +08:00
|
|
|
|
let dnsProvider: IDnsProvider = null;
|
2024-10-07 03:21:16 +08:00
|
|
|
|
let domainsVerifyPlan: DomainsVerifyPlan = null;
|
2025-01-03 00:12:15 +08:00
|
|
|
|
if (this.challengeType === "cname" || this.challengeType === "http") {
|
2024-10-07 03:21:16 +08:00
|
|
|
|
domainsVerifyPlan = await this.createDomainsVerifyPlan();
|
|
|
|
|
|
} else {
|
|
|
|
|
|
const dnsProviderType = this.dnsProviderType;
|
2025-04-12 00:14:55 +08:00
|
|
|
|
const access = await this.getAccess(this.dnsProviderAccess);
|
2024-10-20 11:47:35 +08:00
|
|
|
|
dnsProvider = await this.createDnsProvider(dnsProviderType, access);
|
2024-09-29 14:57:20 +08:00
|
|
|
|
}
|
2022-11-08 22:10:42 +08:00
|
|
|
|
|
2024-07-18 11:17:13 +08:00
|
|
|
|
try {
|
|
|
|
|
|
const cert = await this.acme.order({
|
|
|
|
|
|
email,
|
|
|
|
|
|
domains,
|
|
|
|
|
|
dnsProvider,
|
2024-10-07 03:21:16 +08:00
|
|
|
|
domainsVerifyPlan,
|
2024-07-18 11:17:13 +08:00
|
|
|
|
csrInfo,
|
|
|
|
|
|
isTest: false,
|
2024-08-23 17:41:02 +08:00
|
|
|
|
privateKeyType: this.privateKeyType,
|
2024-07-18 11:17:13 +08:00
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
const certInfo = this.formatCerts(cert);
|
|
|
|
|
|
return new CertReader(certInfo);
|
|
|
|
|
|
} catch (e: any) {
|
2024-09-25 02:31:45 +08:00
|
|
|
|
const message: string = e?.message;
|
|
|
|
|
|
if (message != null && message.indexOf("redundant with a wildcard domain in the same request") >= 0) {
|
2024-07-18 11:17:13 +08:00
|
|
|
|
this.logger.error(e);
|
|
|
|
|
|
throw new Error(`通配符域名已经包含了普通域名,请删除其中一个(${message})`);
|
|
|
|
|
|
}
|
2024-11-01 02:13:34 +08:00
|
|
|
|
if (e.name === "CancelError") {
|
|
|
|
|
|
throw new CancelError(e.message);
|
|
|
|
|
|
}
|
2024-07-18 11:17:13 +08:00
|
|
|
|
throw e;
|
|
|
|
|
|
}
|
2022-11-08 22:10:42 +08:00
|
|
|
|
}
|
2024-10-07 03:21:16 +08:00
|
|
|
|
|
2024-10-20 11:47:35 +08:00
|
|
|
|
async createDnsProvider(dnsProviderType: string, dnsProviderAccess: any): Promise<IDnsProvider> {
|
2025-04-11 12:13:57 +08:00
|
|
|
|
const domainParser = this.acme.options.domainParser;
|
|
|
|
|
|
const context: DnsProviderContext = { access: dnsProviderAccess, logger: this.logger, http: this.ctx.http, utils, domainParser };
|
2024-10-09 02:34:28 +08:00
|
|
|
|
return await createDnsProvider({
|
|
|
|
|
|
dnsProviderType,
|
|
|
|
|
|
context,
|
|
|
|
|
|
});
|
2024-10-07 03:21:16 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
async createDomainsVerifyPlan(): Promise<DomainsVerifyPlan> {
|
|
|
|
|
|
const plan: DomainsVerifyPlan = {};
|
|
|
|
|
|
for (const domain in this.domainsVerifyPlan) {
|
|
|
|
|
|
const domainVerifyPlan = this.domainsVerifyPlan[domain];
|
|
|
|
|
|
let dnsProvider = null;
|
|
|
|
|
|
const cnameVerifyPlan: Record<string, CnameVerifyPlan> = {};
|
2025-01-03 00:12:15 +08:00
|
|
|
|
const httpVerifyPlan: Record<string, HttpVerifyPlan> = {};
|
2024-10-07 03:21:16 +08:00
|
|
|
|
if (domainVerifyPlan.type === "dns") {
|
2025-04-12 00:14:55 +08:00
|
|
|
|
const access = await this.getAccess(domainVerifyPlan.dnsProviderAccessId);
|
2024-10-20 11:47:35 +08:00
|
|
|
|
dnsProvider = await this.createDnsProvider(domainVerifyPlan.dnsProviderType, access);
|
2025-01-03 00:12:15 +08:00
|
|
|
|
} else if (domainVerifyPlan.type === "cname") {
|
2024-10-07 03:21:16 +08:00
|
|
|
|
for (const key in domainVerifyPlan.cnameVerifyPlan) {
|
|
|
|
|
|
const cnameRecord = await this.ctx.cnameProxyService.getByDomain(key);
|
2024-11-08 01:31:20 +08:00
|
|
|
|
let dnsProvider = cnameRecord.commonDnsProvider;
|
|
|
|
|
|
if (cnameRecord.cnameProvider.id > 0) {
|
|
|
|
|
|
dnsProvider = await this.createDnsProvider(cnameRecord.cnameProvider.dnsProviderType, cnameRecord.cnameProvider.access);
|
|
|
|
|
|
}
|
2024-10-07 03:21:16 +08:00
|
|
|
|
cnameVerifyPlan[key] = {
|
2025-01-03 00:12:15 +08:00
|
|
|
|
type: "cname",
|
2024-10-07 03:21:16 +08:00
|
|
|
|
domain: cnameRecord.cnameProvider.domain,
|
|
|
|
|
|
fullRecord: cnameRecord.recordValue,
|
2024-11-08 01:31:20 +08:00
|
|
|
|
dnsProvider,
|
2024-10-07 03:21:16 +08:00
|
|
|
|
};
|
|
|
|
|
|
}
|
2025-01-03 00:12:15 +08:00
|
|
|
|
} else if (domainVerifyPlan.type === "http") {
|
|
|
|
|
|
const httpUploaderContext = {
|
|
|
|
|
|
accessService: this.ctx.accessService,
|
|
|
|
|
|
logger: this.logger,
|
|
|
|
|
|
utils,
|
|
|
|
|
|
};
|
|
|
|
|
|
for (const key in domainVerifyPlan.httpVerifyPlan) {
|
|
|
|
|
|
const httpRecord = domainVerifyPlan.httpVerifyPlan[key];
|
2025-04-12 00:14:55 +08:00
|
|
|
|
const access = await this.getAccess(httpRecord.httpUploaderAccess);
|
2025-01-04 01:45:24 +08:00
|
|
|
|
let rootDir = httpRecord.httpUploadRootDir;
|
|
|
|
|
|
if (!rootDir.endsWith("/") && !rootDir.endsWith("\\")) {
|
|
|
|
|
|
rootDir = rootDir + "/";
|
|
|
|
|
|
}
|
|
|
|
|
|
this.logger.info("上传方式", httpRecord.httpUploaderType);
|
2025-04-25 01:26:04 +08:00
|
|
|
|
const httpUploader = await ossClientFactory.createOssClientByType(httpRecord.httpUploaderType, {
|
2025-01-03 00:12:15 +08:00
|
|
|
|
access,
|
2025-01-04 01:45:24 +08:00
|
|
|
|
rootDir: rootDir,
|
2025-01-03 00:12:15 +08:00
|
|
|
|
ctx: httpUploaderContext,
|
|
|
|
|
|
});
|
|
|
|
|
|
httpVerifyPlan[key] = {
|
|
|
|
|
|
type: "http",
|
|
|
|
|
|
domain: key,
|
|
|
|
|
|
httpUploader,
|
|
|
|
|
|
};
|
|
|
|
|
|
}
|
2024-10-07 03:21:16 +08:00
|
|
|
|
}
|
|
|
|
|
|
plan[domain] = {
|
|
|
|
|
|
domain,
|
|
|
|
|
|
type: domainVerifyPlan.type,
|
|
|
|
|
|
dnsProvider,
|
|
|
|
|
|
cnameVerifyPlan,
|
2025-01-03 00:12:15 +08:00
|
|
|
|
httpVerifyPlan,
|
2024-10-07 03:21:16 +08:00
|
|
|
|
};
|
|
|
|
|
|
}
|
|
|
|
|
|
return plan;
|
|
|
|
|
|
}
|
2022-11-08 22:10:42 +08:00
|
|
|
|
}
|
2023-05-09 09:56:31 +08:00
|
|
|
|
|
|
|
|
|
|
new CertApplyPlugin();
|