feat: 支持zero ssl

pull/101/head
xiaojunnuo 2024-07-04 01:14:09 +08:00
parent 6ec950818c
commit eade2c2b68
8 changed files with 86 additions and 6 deletions

View File

@ -36,6 +36,9 @@ module.exports = async (client, userOpts) => {
if (opts.email) { if (opts.email) {
accountPayload.contact = [`mailto:${opts.email}`]; accountPayload.contact = [`mailto:${opts.email}`];
} }
if (opts.externalAccountBinding) {
accountPayload.externalAccountBinding = opts.externalAccountBinding;
}
/** /**
* Register account * Register account

View File

@ -18,6 +18,7 @@ exports.directory = {
production: 'https://acme-v02.api.letsencrypt.org/directory', production: 'https://acme-v02.api.letsencrypt.org/directory',
}, },
zerossl: { zerossl: {
staging: 'https://acme.zerossl.com/v2/DV90',
production: 'https://acme.zerossl.com/v2/DV90', production: 'https://acme.zerossl.com/v2/DV90',
}, },
}; };

View File

@ -92,6 +92,7 @@ export const directory: {
production: string production: string
}, },
zerossl: { zerossl: {
staging: string,
production: string production: string
} }
}; };

View File

@ -0,0 +1,29 @@
import { IsAccess, AccessInput } from "@certd/pipeline";
@IsAccess({
name: "eab",
title: "EABAccess",
desc: "ZeroSSL证书申请需要EAB授权",
})
export class EabAccess {
@AccessInput({
title: "KID",
component: {
placeholder: "kid",
},
helper: "EAB KID",
required: true,
})
kid = "";
@AccessInput({
title: "HMACKey",
component: {
placeholder: "HMAC Key",
},
helper: "EAB HMAC Key",
required: true,
})
hmacKey = "";
}
new EabAccess();

View File

@ -0,0 +1 @@
export * from "./eab-access";

View File

@ -1,2 +1,3 @@
export * from "./plugin"; export * from "./plugin";
export * from "./dns-provider"; export * from "./dns-provider";
export * from "./access";

View File

@ -6,18 +6,24 @@ import { Logger } from "log4js";
import { IContext } from "@certd/pipeline"; import { IContext } from "@certd/pipeline";
import { IDnsProvider } from "../../dns-provider"; import { IDnsProvider } from "../../dns-provider";
import psl from "psl"; import psl from "psl";
import { ClientExternalAccountBindingOptions } from "@certd/acme-client";
export type CertInfo = { export type CertInfo = {
crt: string; crt: string;
key: string; key: string;
csr: string; csr: string;
}; };
export type SSLProvider = "letsencrypt" | "buypass" | "zerossl";
export class AcmeService { export class AcmeService {
userContext: IContext; userContext: IContext;
logger: Logger; logger: Logger;
constructor(options: { userContext: IContext; logger: Logger }) { sslProvider: SSLProvider;
eab?: ClientExternalAccountBindingOptions;
constructor(options: { userContext: IContext; logger: Logger; sslProvider: SSLProvider; eab?: ClientExternalAccountBindingOptions }) {
this.userContext = options.userContext; this.userContext = options.userContext;
this.logger = options.logger; this.logger = options.logger;
this.sslProvider = options.sslProvider || "letsencrypt";
this.eab = options.eab;
acme.setLogger((text: string) => { acme.setLogger((text: string) => {
this.logger.info(text); this.logger.info(text);
}); });
@ -28,7 +34,7 @@ export class AcmeService {
} }
buildAccountKey(email: string) { buildAccountKey(email: string) {
return "acme.config." + email; return `acme.config.${this.sslProvider}.${email}`;
} }
async saveAccountConfig(email: string, conf: any) { async saveAccountConfig(email: string, conf: any) {
@ -41,11 +47,18 @@ export class AcmeService {
conf.key = await this.createNewKey(); conf.key = await this.createNewKey();
await this.saveAccountConfig(email, conf); await this.saveAccountConfig(email, conf);
} }
let directoryUrl = "";
if (isTest) {
directoryUrl = acme.directory[this.sslProvider].staging;
} else {
directoryUrl = acme.directory[this.sslProvider].production;
}
const client = new acme.Client({ const client = new acme.Client({
directoryUrl: isTest ? acme.directory.letsencrypt.staging : acme.directory.letsencrypt.production, directoryUrl: directoryUrl,
accountKey: conf.key, accountKey: conf.key,
accountUrl: conf.accountUrl, accountUrl: conf.accountUrl,
backoffAttempts: 70, externalAccountBinding: this.eab,
backoffAttempts: 30,
backoffMin: 5000, backoffMin: 5000,
backoffMax: 10000, backoffMax: 10000,
}); });
@ -54,6 +67,7 @@ export class AcmeService {
const accountPayload = { const accountPayload = {
termsOfServiceAgreed: true, termsOfServiceAgreed: true,
contact: [`mailto:${email}`], contact: [`mailto:${email}`],
externalAccountBinding: this.eab,
}; };
await client.createAccount(accountPayload); await client.createAccount(accountPayload);
conf.accountUrl = client.getAccountUrl(); conf.accountUrl = client.getAccountUrl();

View File

@ -1,6 +1,6 @@
import { AbstractTaskPlugin, Decorator, HttpClient, IAccessService, IContext, IsTaskPlugin, RunStrategy, Step, TaskInput, TaskOutput } from "@certd/pipeline"; import { AbstractTaskPlugin, Decorator, HttpClient, IAccessService, IContext, IsTaskPlugin, RunStrategy, Step, TaskInput, TaskOutput } from "@certd/pipeline";
import dayjs from "dayjs"; import dayjs from "dayjs";
import { AcmeService, CertInfo } from "./acme"; import { AcmeService, CertInfo, SSLProvider } from "./acme";
import _ from "lodash"; import _ from "lodash";
import { Logger } from "log4js"; import { Logger } from "log4js";
import { DnsProviderContext, DnsProviderDefine, dnsProviderRegistry } from "../../dns-provider"; import { DnsProviderContext, DnsProviderDefine, dnsProviderRegistry } from "../../dns-provider";
@ -56,6 +56,32 @@ export class CertApplyPlugin extends AbstractTaskPlugin {
}) })
email!: string; email!: string;
@TaskInput({
title: "证书提供商",
value: "letsencrypt",
component: {
name: "a-select",
vModel: "value",
options: [
{ value: "letsencrypt", label: "Let's Encrypt" },
// { value: "buypass", label: "Buypass" },
{ value: "zerossl", label: "ZeroSSL" },
],
},
required: true,
})
sslProvider!: SSLProvider;
@TaskInput({
title: "EAB授权",
component: {
name: "pi-access-selector",
type: "eab",
},
helper: "如果使用ZeroSSL证书需要提供EAB授权 请前往 https://app.zerossl.com/developer 生成 'EAB Credentials for ACME Clients' ",
})
eabAccessId!: number;
@TaskInput({ @TaskInput({
title: "DNS提供商", title: "DNS提供商",
component: { component: {
@ -135,7 +161,11 @@ export class CertApplyPlugin extends AbstractTaskPlugin {
this.http = this.ctx.http; this.http = this.ctx.http;
this.lastStatus = this.ctx.lastStatus as Step; this.lastStatus = this.ctx.lastStatus as Step;
this.acme = new AcmeService({ userContext: this.userContext, logger: this.logger }); let eab: any = null;
if (this.eabAccessId) {
eab = await this.ctx.accessService.getById(this.eabAccessId);
}
this.acme = new AcmeService({ userContext: this.userContext, logger: this.logger, sslProvider: this.sslProvider, eab });
} }
async execute(): Promise<void> { async execute(): Promise<void> {