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) {
accountPayload.contact = [`mailto:${opts.email}`];
}
if (opts.externalAccountBinding) {
accountPayload.externalAccountBinding = opts.externalAccountBinding;
}
/**
* Register account

View File

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

View File

@ -92,6 +92,7 @@ export const directory: {
production: string
},
zerossl: {
staging: 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 "./dns-provider";
export * from "./access";

View File

@ -6,18 +6,24 @@ import { Logger } from "log4js";
import { IContext } from "@certd/pipeline";
import { IDnsProvider } from "../../dns-provider";
import psl from "psl";
import { ClientExternalAccountBindingOptions } from "@certd/acme-client";
export type CertInfo = {
crt: string;
key: string;
csr: string;
};
export type SSLProvider = "letsencrypt" | "buypass" | "zerossl";
export class AcmeService {
userContext: IContext;
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.logger = options.logger;
this.sslProvider = options.sslProvider || "letsencrypt";
this.eab = options.eab;
acme.setLogger((text: string) => {
this.logger.info(text);
});
@ -28,7 +34,7 @@ export class AcmeService {
}
buildAccountKey(email: string) {
return "acme.config." + email;
return `acme.config.${this.sslProvider}.${email}`;
}
async saveAccountConfig(email: string, conf: any) {
@ -41,11 +47,18 @@ export class AcmeService {
conf.key = await this.createNewKey();
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({
directoryUrl: isTest ? acme.directory.letsencrypt.staging : acme.directory.letsencrypt.production,
directoryUrl: directoryUrl,
accountKey: conf.key,
accountUrl: conf.accountUrl,
backoffAttempts: 70,
externalAccountBinding: this.eab,
backoffAttempts: 30,
backoffMin: 5000,
backoffMax: 10000,
});
@ -54,6 +67,7 @@ export class AcmeService {
const accountPayload = {
termsOfServiceAgreed: true,
contact: [`mailto:${email}`],
externalAccountBinding: this.eab,
};
await client.createAccount(accountPayload);
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 dayjs from "dayjs";
import { AcmeService, CertInfo } from "./acme";
import { AcmeService, CertInfo, SSLProvider } from "./acme";
import _ from "lodash";
import { Logger } from "log4js";
import { DnsProviderContext, DnsProviderDefine, dnsProviderRegistry } from "../../dns-provider";
@ -56,6 +56,32 @@ export class CertApplyPlugin extends AbstractTaskPlugin {
})
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({
title: "DNS提供商",
component: {
@ -135,7 +161,11 @@ export class CertApplyPlugin extends AbstractTaskPlugin {
this.http = this.ctx.http;
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> {