mirror of https://github.com/certd/certd
				
				
				
			perf: 优化acme sdk
							parent
							
								
									03b751fa13
								
							
						
					
					
						commit
						54db744282
					
				| 
						 | 
				
			
			@ -99,31 +99,14 @@ export default  async (client, userOpts) => {
 | 
			
		|||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        const keyAuthorizationGetter = async (challenge) => {
 | 
			
		||||
            return await client.getChallengeKeyAuthorization(challenge);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        try {
 | 
			
		||||
            /* Select challenge based on priority */
 | 
			
		||||
            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 {
 | 
			
		||||
                const { recordReq, recordRes, dnsProvider } = await opts.challengeCreateFn(authz, challenge, keyAuthorization);
 | 
			
		||||
                log(`[auto] [${d}] challengeCreateFn success`);
 | 
			
		||||
                log(`[auto] [${d}] add challengeRemoveFn()`);
 | 
			
		||||
                const { recordReq, recordRes, dnsProvider,challenge ,keyAuthorization} = await opts.challengeCreateFn(authz, keyAuthorizationGetter);
 | 
			
		||||
                clearTasks.push(async () => {
 | 
			
		||||
                    /* Trigger challengeRemoveFn(), suppress errors */
 | 
			
		||||
                    log(`[auto] [${d}] Trigger challengeRemoveFn()`);
 | 
			
		||||
| 
						 | 
				
			
			@ -141,7 +124,7 @@ export default  async (client, userOpts) => {
 | 
			
		|||
                    await wait(60 * 1000);
 | 
			
		||||
                }
 | 
			
		||||
                else {
 | 
			
		||||
                    log(`[auto] [${d}] Running challenge verification`);
 | 
			
		||||
                    log(`[auto] [${d}] Running challenge verification, type = ${challenge.type}`);
 | 
			
		||||
                    try {
 | 
			
		||||
                        await client.verifyChallenge(authz, challenge);
 | 
			
		||||
                    }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -5,3 +5,5 @@ export class CancelError extends Error {
 | 
			
		|||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -4,6 +4,8 @@
 | 
			
		|||
 | 
			
		||||
import { AxiosInstance } from 'axios';
 | 
			
		||||
import * as rfc8555 from './rfc8555';
 | 
			
		||||
import {CancelError} from '../src/error.js'
 | 
			
		||||
export * from '../src/error.js'
 | 
			
		||||
 | 
			
		||||
export type PrivateKeyBuffer = Buffer;
 | 
			
		||||
export type PublicKeyBuffer = Buffer;
 | 
			
		||||
| 
						 | 
				
			
			@ -56,7 +58,7 @@ export interface ClientExternalAccountBindingOptions {
 | 
			
		|||
 | 
			
		||||
export interface ClientAutoOptions {
 | 
			
		||||
    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>;
 | 
			
		||||
    email?: string;
 | 
			
		||||
    termsOfServiceAgreed?: boolean;
 | 
			
		||||
| 
						 | 
				
			
			@ -202,4 +204,4 @@ export function setLogger(fn: (message: any, ...args: any[]) => void): void;
 | 
			
		|||
 | 
			
		||||
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 { IDnsProvider, parseDomain } from "../../dns-provider/index.js";
 | 
			
		||||
import { HttpChallengeUploader } from "./uploads/api.js";
 | 
			
		||||
 | 
			
		||||
export type CnameVerifyPlan = {
 | 
			
		||||
  type?: string;
 | 
			
		||||
  domain: string;
 | 
			
		||||
| 
						 | 
				
			
			@ -170,75 +169,36 @@ export class AcmeService {
 | 
			
		|||
    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()");
 | 
			
		||||
 | 
			
		||||
    /* http-01 */
 | 
			
		||||
    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 fileContents = keyAuthorization;
 | 
			
		||||
      this.logger.info(`校验 ${fullDomain} ,准备上传文件:${filePath}`);
 | 
			
		||||
 | 
			
		||||
      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);
 | 
			
		||||
      await httpUploader.upload(filePath, fileContents);
 | 
			
		||||
      this.logger.info(`上传文件【${filePath}】成功`);
 | 
			
		||||
    } else if (challenge.type === "dns-01") {
 | 
			
		||||
      /* dns-01 */
 | 
			
		||||
      let fullRecord = `_acme-challenge.${fullDomain}`;
 | 
			
		||||
      return {
 | 
			
		||||
        challenge,
 | 
			
		||||
        keyAuthorization,
 | 
			
		||||
      };
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    const doDnsVerify = async (challenge: any, fullRecord: string, dnsProvider: IDnsProvider) => {
 | 
			
		||||
      this.logger.info("dns校验");
 | 
			
		||||
      const keyAuthorization = await keyAuthorizationGetter(challenge);
 | 
			
		||||
 | 
			
		||||
      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}`, "");
 | 
			
		||||
      if (hostRecord.endsWith(".")) {
 | 
			
		||||
        hostRecord = hostRecord.substring(0, hostRecord.length - 1);
 | 
			
		||||
| 
						 | 
				
			
			@ -258,8 +218,50 @@ export class AcmeService {
 | 
			
		|||
        recordReq,
 | 
			
		||||
        recordRes,
 | 
			
		||||
        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"],
 | 
			
		||||
      challengeCreateFn: async (
 | 
			
		||||
        authz: acme.Authorization,
 | 
			
		||||
        challenge: Challenge,
 | 
			
		||||
        keyAuthorization: string
 | 
			
		||||
      ): Promise<{ recordReq: any; recordRes: any; dnsProvider: any }> => {
 | 
			
		||||
        return await this.challengeCreateFn(authz, challenge, keyAuthorization, providers);
 | 
			
		||||
        keyAuthorizationGetter: (challenge: Challenge) => Promise<string>
 | 
			
		||||
      ): Promise<{ recordReq?: any; recordRes?: any; dnsProvider?: any; challenge: Challenge; keyAuthorization: string }> => {
 | 
			
		||||
        return await this.challengeCreateFn(authz, keyAuthorizationGetter, providers);
 | 
			
		||||
      },
 | 
			
		||||
      challengeRemoveFn: async (
 | 
			
		||||
        authz: acme.Authorization,
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -19,6 +19,7 @@
 | 
			
		|||
    "@certd/basic": "^1.29.2",
 | 
			
		||||
    "@certd/pipeline": "^1.29.2",
 | 
			
		||||
    "@kubernetes/client-node": "0.21.0",
 | 
			
		||||
    "ali-oss": "^6.21.0",
 | 
			
		||||
    "basic-ftp": "^5.0.5",
 | 
			
		||||
    "dayjs": "^1.11.7",
 | 
			
		||||
    "iconv-lite": "^0.6.3",
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -13,9 +13,12 @@ export class AliossClient {
 | 
			
		|||
  }
 | 
			
		||||
 | 
			
		||||
  async init() {
 | 
			
		||||
    if (this.client) {
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
    // @ts-ignore
 | 
			
		||||
    const OSS = await import("ali-oss");
 | 
			
		||||
    this.client = new OSS.default({
 | 
			
		||||
    const ossClient = new OSS.default({
 | 
			
		||||
      accessKeyId: this.access.accessKeyId,
 | 
			
		||||
      accessKeySecret: this.access.accessKeySecret,
 | 
			
		||||
      // yourRegion填写Bucket所在地域。以华东1(杭州)为例,Region填写为oss-cn-hangzhou。
 | 
			
		||||
| 
						 | 
				
			
			@ -25,16 +28,20 @@ export class AliossClient {
 | 
			
		|||
      // yourBucketName填写Bucket名称。
 | 
			
		||||
      bucket: this.bucket,
 | 
			
		||||
    });
 | 
			
		||||
    // oss
 | 
			
		||||
 | 
			
		||||
    this.client = ossClient;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  async doRequest(client: any, bucket: string, xml: string, params: any) {
 | 
			
		||||
    params = client._bucketRequestParams("POST", bucket, {
 | 
			
		||||
  async doRequest(bucket: string, xml: string, params: any) {
 | 
			
		||||
    await this.init();
 | 
			
		||||
    params = this.client._bucketRequestParams("POST", bucket, {
 | 
			
		||||
      ...params,
 | 
			
		||||
    });
 | 
			
		||||
    params.content = xml;
 | 
			
		||||
    params.mime = "xml";
 | 
			
		||||
    params.successStatuses = [200];
 | 
			
		||||
    const res = await client.request(params);
 | 
			
		||||
    const res = await this.client.request(params);
 | 
			
		||||
    this.checkRet(res);
 | 
			
		||||
    return res;
 | 
			
		||||
  }
 | 
			
		||||
| 
						 | 
				
			
			@ -46,11 +53,12 @@ export class AliossClient {
 | 
			
		|||
  }
 | 
			
		||||
 | 
			
		||||
  async uploadFile(filePath: string, content: Buffer) {
 | 
			
		||||
    const memFile = new File([content], filePath);
 | 
			
		||||
    return await this.client.put(filePath, memFile);
 | 
			
		||||
    await this.init();
 | 
			
		||||
    return await this.client.put(filePath, content);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  async removeFile(filePath: string) {
 | 
			
		||||
    await this.init();
 | 
			
		||||
    return await this.client.delete(filePath);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue