mirror of https://github.com/certd/certd
perf: 优化acme sdk
parent
03b751fa13
commit
54db744282
|
@ -99,31 +99,14 @@ export default async (client, userOpts) => {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
const keyAuthorizationGetter = async (challenge) => {
|
||||||
/* Select challenge based on priority */
|
return await client.getChallengeKeyAuthorization(challenge);
|
||||||
const challenge = authz.challenges.sort((a, b) => {
|
|
||||||
const aidx = opts.challengePriority.indexOf(a.type);
|
|
||||||
const bidx = opts.challengePriority.indexOf(b.type);
|
|
||||||
|
|
||||||
if (aidx === -1) return 1;
|
|
||||||
if (bidx === -1) return -1;
|
|
||||||
return aidx - bidx;
|
|
||||||
}).slice(0, 1)[0];
|
|
||||||
|
|
||||||
if (!challenge) {
|
|
||||||
throw new Error(`Unable to select challenge for ${d}, no challenge found`);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
log(`[auto] [${d}] Found ${authz.challenges.length} challenges, selected type: ${challenge.type}`);
|
|
||||||
|
|
||||||
/* Trigger challengeCreateFn() */
|
|
||||||
log(`[auto] [${d}] Trigger challengeCreateFn()`);
|
|
||||||
const keyAuthorization = await client.getChallengeKeyAuthorization(challenge);
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const { recordReq, recordRes, dnsProvider } = await opts.challengeCreateFn(authz, challenge, keyAuthorization);
|
log(`[auto] [${d}] Trigger challengeCreateFn()`);
|
||||||
log(`[auto] [${d}] challengeCreateFn success`);
|
try {
|
||||||
log(`[auto] [${d}] add challengeRemoveFn()`);
|
const { recordReq, recordRes, dnsProvider,challenge ,keyAuthorization} = await opts.challengeCreateFn(authz, keyAuthorizationGetter);
|
||||||
clearTasks.push(async () => {
|
clearTasks.push(async () => {
|
||||||
/* Trigger challengeRemoveFn(), suppress errors */
|
/* Trigger challengeRemoveFn(), suppress errors */
|
||||||
log(`[auto] [${d}] Trigger challengeRemoveFn()`);
|
log(`[auto] [${d}] Trigger challengeRemoveFn()`);
|
||||||
|
@ -141,7 +124,7 @@ export default async (client, userOpts) => {
|
||||||
await wait(60 * 1000);
|
await wait(60 * 1000);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
log(`[auto] [${d}] Running challenge verification`);
|
log(`[auto] [${d}] Running challenge verification, type = ${challenge.type}`);
|
||||||
try {
|
try {
|
||||||
await client.verifyChallenge(authz, challenge);
|
await client.verifyChallenge(authz, challenge);
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,3 +5,5 @@ export class CancelError extends Error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -4,6 +4,8 @@
|
||||||
|
|
||||||
import { AxiosInstance } from 'axios';
|
import { AxiosInstance } from 'axios';
|
||||||
import * as rfc8555 from './rfc8555';
|
import * as rfc8555 from './rfc8555';
|
||||||
|
import {CancelError} from '../src/error.js'
|
||||||
|
export * from '../src/error.js'
|
||||||
|
|
||||||
export type PrivateKeyBuffer = Buffer;
|
export type PrivateKeyBuffer = Buffer;
|
||||||
export type PublicKeyBuffer = Buffer;
|
export type PublicKeyBuffer = Buffer;
|
||||||
|
@ -56,7 +58,7 @@ export interface ClientExternalAccountBindingOptions {
|
||||||
|
|
||||||
export interface ClientAutoOptions {
|
export interface ClientAutoOptions {
|
||||||
csr: CsrBuffer | CsrString;
|
csr: CsrBuffer | CsrString;
|
||||||
challengeCreateFn: (authz: Authorization, challenge: rfc8555.Challenge, keyAuthorization: string) => Promise<{recordReq:any,recordRes:any,dnsProvider:any}>;
|
challengeCreateFn: (authz: Authorization, keyAuthorization: (challenge:rfc8555.Challenge)=>Promise<string>) => Promise<{recordReq?:any,recordRes?:any,dnsProvider?:any,challenge: rfc8555.Challenge,keyAuthorization:string}>;
|
||||||
challengeRemoveFn: (authz: Authorization, challenge: rfc8555.Challenge, keyAuthorization: string,recordReq:any, recordRes:any,dnsProvider:any) => Promise<any>;
|
challengeRemoveFn: (authz: Authorization, challenge: rfc8555.Challenge, keyAuthorization: string,recordReq:any, recordRes:any,dnsProvider:any) => Promise<any>;
|
||||||
email?: string;
|
email?: string;
|
||||||
termsOfServiceAgreed?: boolean;
|
termsOfServiceAgreed?: boolean;
|
||||||
|
@ -202,4 +204,4 @@ export function setLogger(fn: (message: any, ...args: any[]) => void): void;
|
||||||
|
|
||||||
export function walkTxtRecord(record: any): Promise<string[]>;
|
export function walkTxtRecord(record: any): Promise<string[]>;
|
||||||
|
|
||||||
export const CancelError: Error;
|
export const CancelError: typeof CancelError;
|
|
@ -7,7 +7,6 @@ import { IContext } from "@certd/pipeline";
|
||||||
import { ILogger, utils } from "@certd/basic";
|
import { ILogger, utils } from "@certd/basic";
|
||||||
import { IDnsProvider, parseDomain } from "../../dns-provider/index.js";
|
import { IDnsProvider, parseDomain } from "../../dns-provider/index.js";
|
||||||
import { HttpChallengeUploader } from "./uploads/api.js";
|
import { HttpChallengeUploader } from "./uploads/api.js";
|
||||||
|
|
||||||
export type CnameVerifyPlan = {
|
export type CnameVerifyPlan = {
|
||||||
type?: string;
|
type?: string;
|
||||||
domain: string;
|
domain: string;
|
||||||
|
@ -170,75 +169,36 @@ export class AcmeService {
|
||||||
return key.toString();
|
return key.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
async challengeCreateFn(authz: any, challenge: any, keyAuthorization: string, providers: Providers) {
|
async challengeCreateFn(authz: any, keyAuthorizationGetter: (challenge: Challenge) => Promise<string>, providers: Providers) {
|
||||||
this.logger.info("Triggered challengeCreateFn()");
|
this.logger.info("Triggered challengeCreateFn()");
|
||||||
|
|
||||||
/* http-01 */
|
|
||||||
const fullDomain = authz.identifier.value;
|
const fullDomain = authz.identifier.value;
|
||||||
if (challenge.type === "http-01") {
|
let domain = parseDomain(fullDomain);
|
||||||
|
this.logger.info("主域名为:" + domain);
|
||||||
|
|
||||||
|
const getChallenge = (type: string) => {
|
||||||
|
return authz.challenges.find((c: any) => c.type === type);
|
||||||
|
};
|
||||||
|
|
||||||
|
const doHttpVerify = async (challenge: any, httpUploader: HttpChallengeUploader) => {
|
||||||
|
const keyAuthorization = await keyAuthorizationGetter(challenge);
|
||||||
|
this.logger.info("http校验");
|
||||||
const filePath = `.well-known/acme-challenge/${challenge.token}`;
|
const filePath = `.well-known/acme-challenge/${challenge.token}`;
|
||||||
const fileContents = keyAuthorization;
|
const fileContents = keyAuthorization;
|
||||||
this.logger.info(`校验 ${fullDomain} ,准备上传文件:${filePath}`);
|
this.logger.info(`校验 ${fullDomain} ,准备上传文件:${filePath}`);
|
||||||
|
await httpUploader.upload(filePath, fileContents);
|
||||||
let httpUploaderPlan: HttpVerifyPlan = null;
|
|
||||||
if (providers.domainsVerifyPlan) {
|
|
||||||
//查找文件上传配置
|
|
||||||
for (const mainDomain in providers.domainsVerifyPlan) {
|
|
||||||
const domainVerifyPlan = providers.domainsVerifyPlan[mainDomain];
|
|
||||||
if (domainVerifyPlan && domainVerifyPlan.type === "http" && domainVerifyPlan.httpVerifyPlan[fullDomain]) {
|
|
||||||
httpUploaderPlan = domainVerifyPlan.httpVerifyPlan[fullDomain];
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (httpUploaderPlan == null) {
|
|
||||||
throw new Error(`未找到域名【${fullDomain}】的http校验计划`);
|
|
||||||
}
|
|
||||||
|
|
||||||
await httpUploaderPlan.httpUploader.upload(filePath, fileContents);
|
|
||||||
this.logger.info(`上传文件【${filePath}】成功`);
|
this.logger.info(`上传文件【${filePath}】成功`);
|
||||||
} else if (challenge.type === "dns-01") {
|
return {
|
||||||
/* dns-01 */
|
challenge,
|
||||||
let fullRecord = `_acme-challenge.${fullDomain}`;
|
keyAuthorization,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const doDnsVerify = async (challenge: any, fullRecord: string, dnsProvider: IDnsProvider) => {
|
||||||
|
this.logger.info("dns校验");
|
||||||
|
const keyAuthorization = await keyAuthorizationGetter(challenge);
|
||||||
|
|
||||||
const recordValue = keyAuthorization;
|
const recordValue = keyAuthorization;
|
||||||
|
|
||||||
this.logger.info(`Creating TXT record for ${fullDomain}: ${fullRecord}`);
|
|
||||||
/* Replace this */
|
|
||||||
this.logger.info(`Would create TXT record "${fullRecord}" with value "${recordValue}"`);
|
|
||||||
|
|
||||||
let domain = parseDomain(fullDomain);
|
|
||||||
this.logger.info("解析到域名domain=" + domain);
|
|
||||||
|
|
||||||
let dnsProvider = providers.dnsProvider;
|
|
||||||
if (providers.domainsVerifyPlan) {
|
|
||||||
//按照计划执行
|
|
||||||
const domainVerifyPlan = providers.domainsVerifyPlan[domain];
|
|
||||||
if (domainVerifyPlan) {
|
|
||||||
if (domainVerifyPlan.type === "dns") {
|
|
||||||
dnsProvider = domainVerifyPlan.dnsProvider;
|
|
||||||
} else if (domainVerifyPlan.type === "cname") {
|
|
||||||
const cnameVerifyPlan = domainVerifyPlan.cnameVerifyPlan;
|
|
||||||
if (cnameVerifyPlan) {
|
|
||||||
const cname = cnameVerifyPlan[fullDomain];
|
|
||||||
if (cname) {
|
|
||||||
dnsProvider = cname.dnsProvider;
|
|
||||||
domain = parseDomain(cname.domain);
|
|
||||||
fullRecord = cname.fullRecord;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
this.logger.error("未找到域名Cname校验计划,使用默认的dnsProvider");
|
|
||||||
}
|
|
||||||
} else if (domainVerifyPlan.type === "http") {
|
|
||||||
throw new Error("切换为http校验");
|
|
||||||
} else {
|
|
||||||
// this.logger.error("不支持的校验类型", domainVerifyPlan.type);
|
|
||||||
throw new Error("不支持的校验类型", domainVerifyPlan.type);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
this.logger.info("未找到域名校验计划,使用默认的dnsProvider");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let hostRecord = fullRecord.replace(`${domain}`, "");
|
let hostRecord = fullRecord.replace(`${domain}`, "");
|
||||||
if (hostRecord.endsWith(".")) {
|
if (hostRecord.endsWith(".")) {
|
||||||
hostRecord = hostRecord.substring(0, hostRecord.length - 1);
|
hostRecord = hostRecord.substring(0, hostRecord.length - 1);
|
||||||
|
@ -258,8 +218,50 @@ export class AcmeService {
|
||||||
recordReq,
|
recordReq,
|
||||||
recordRes,
|
recordRes,
|
||||||
dnsProvider,
|
dnsProvider,
|
||||||
|
challenge,
|
||||||
|
keyAuthorization,
|
||||||
};
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
let dnsProvider = providers.dnsProvider;
|
||||||
|
let fullRecord = `_acme-challenge.${fullDomain}`;
|
||||||
|
|
||||||
|
if (providers.domainsVerifyPlan) {
|
||||||
|
//按照计划执行
|
||||||
|
const domainVerifyPlan = providers.domainsVerifyPlan[domain];
|
||||||
|
if (domainVerifyPlan) {
|
||||||
|
if (domainVerifyPlan.type === "dns") {
|
||||||
|
dnsProvider = domainVerifyPlan.dnsProvider;
|
||||||
|
} else if (domainVerifyPlan.type === "cname") {
|
||||||
|
const cnameVerifyPlan = domainVerifyPlan.cnameVerifyPlan;
|
||||||
|
if (cnameVerifyPlan) {
|
||||||
|
const cname = cnameVerifyPlan[fullDomain];
|
||||||
|
if (cname) {
|
||||||
|
dnsProvider = cname.dnsProvider;
|
||||||
|
domain = parseDomain(cname.domain);
|
||||||
|
fullRecord = cname.fullRecord;
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
this.logger.error("未找到域名Cname校验计划,使用默认的dnsProvider");
|
||||||
|
}
|
||||||
|
} else if (domainVerifyPlan.type === "http") {
|
||||||
|
const httpVerifyPlan = domainVerifyPlan.httpVerifyPlan;
|
||||||
|
if (httpVerifyPlan) {
|
||||||
|
const httpChallenge = getChallenge("http-01");
|
||||||
|
return await doHttpVerify(httpChallenge, httpVerifyPlan[fullDomain].httpUploader);
|
||||||
|
} else {
|
||||||
|
throw new Error("未找到域名【" + fullDomain + "】的http校验配置");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
throw new Error("不支持的校验类型", domainVerifyPlan.type);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.logger.info("未找到域名校验计划,使用默认的dnsProvider");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const dnsChallenge = getChallenge("dns-01");
|
||||||
|
return await doDnsVerify(dnsChallenge, fullRecord, dnsProvider);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -376,10 +378,9 @@ export class AcmeService {
|
||||||
challengePriority: ["dns-01", "http-01"],
|
challengePriority: ["dns-01", "http-01"],
|
||||||
challengeCreateFn: async (
|
challengeCreateFn: async (
|
||||||
authz: acme.Authorization,
|
authz: acme.Authorization,
|
||||||
challenge: Challenge,
|
keyAuthorizationGetter: (challenge: Challenge) => Promise<string>
|
||||||
keyAuthorization: string
|
): Promise<{ recordReq?: any; recordRes?: any; dnsProvider?: any; challenge: Challenge; keyAuthorization: string }> => {
|
||||||
): Promise<{ recordReq: any; recordRes: any; dnsProvider: any }> => {
|
return await this.challengeCreateFn(authz, keyAuthorizationGetter, providers);
|
||||||
return await this.challengeCreateFn(authz, challenge, keyAuthorization, providers);
|
|
||||||
},
|
},
|
||||||
challengeRemoveFn: async (
|
challengeRemoveFn: async (
|
||||||
authz: acme.Authorization,
|
authz: acme.Authorization,
|
||||||
|
|
|
@ -19,6 +19,7 @@
|
||||||
"@certd/basic": "^1.29.2",
|
"@certd/basic": "^1.29.2",
|
||||||
"@certd/pipeline": "^1.29.2",
|
"@certd/pipeline": "^1.29.2",
|
||||||
"@kubernetes/client-node": "0.21.0",
|
"@kubernetes/client-node": "0.21.0",
|
||||||
|
"ali-oss": "^6.21.0",
|
||||||
"basic-ftp": "^5.0.5",
|
"basic-ftp": "^5.0.5",
|
||||||
"dayjs": "^1.11.7",
|
"dayjs": "^1.11.7",
|
||||||
"iconv-lite": "^0.6.3",
|
"iconv-lite": "^0.6.3",
|
||||||
|
|
|
@ -13,9 +13,12 @@ export class AliossClient {
|
||||||
}
|
}
|
||||||
|
|
||||||
async init() {
|
async init() {
|
||||||
|
if (this.client) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
const OSS = await import("ali-oss");
|
const OSS = await import("ali-oss");
|
||||||
this.client = new OSS.default({
|
const ossClient = new OSS.default({
|
||||||
accessKeyId: this.access.accessKeyId,
|
accessKeyId: this.access.accessKeyId,
|
||||||
accessKeySecret: this.access.accessKeySecret,
|
accessKeySecret: this.access.accessKeySecret,
|
||||||
// yourRegion填写Bucket所在地域。以华东1(杭州)为例,Region填写为oss-cn-hangzhou。
|
// yourRegion填写Bucket所在地域。以华东1(杭州)为例,Region填写为oss-cn-hangzhou。
|
||||||
|
@ -25,16 +28,20 @@ export class AliossClient {
|
||||||
// yourBucketName填写Bucket名称。
|
// yourBucketName填写Bucket名称。
|
||||||
bucket: this.bucket,
|
bucket: this.bucket,
|
||||||
});
|
});
|
||||||
|
// oss
|
||||||
|
|
||||||
|
this.client = ossClient;
|
||||||
}
|
}
|
||||||
|
|
||||||
async doRequest(client: any, bucket: string, xml: string, params: any) {
|
async doRequest(bucket: string, xml: string, params: any) {
|
||||||
params = client._bucketRequestParams("POST", bucket, {
|
await this.init();
|
||||||
|
params = this.client._bucketRequestParams("POST", bucket, {
|
||||||
...params,
|
...params,
|
||||||
});
|
});
|
||||||
params.content = xml;
|
params.content = xml;
|
||||||
params.mime = "xml";
|
params.mime = "xml";
|
||||||
params.successStatuses = [200];
|
params.successStatuses = [200];
|
||||||
const res = await client.request(params);
|
const res = await this.client.request(params);
|
||||||
this.checkRet(res);
|
this.checkRet(res);
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
@ -46,11 +53,12 @@ export class AliossClient {
|
||||||
}
|
}
|
||||||
|
|
||||||
async uploadFile(filePath: string, content: Buffer) {
|
async uploadFile(filePath: string, content: Buffer) {
|
||||||
const memFile = new File([content], filePath);
|
await this.init();
|
||||||
return await this.client.put(filePath, memFile);
|
return await this.client.put(filePath, content);
|
||||||
}
|
}
|
||||||
|
|
||||||
async removeFile(filePath: string) {
|
async removeFile(filePath: string) {
|
||||||
|
await this.init();
|
||||||
return await this.client.delete(filePath);
|
return await this.client.delete(filePath);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue