fix: 修复aliyun域名超过100个找不到域名的bug

This commit is contained in:
xiaojunnuo
2024-06-14 01:22:07 +08:00
parent ebf2a820cc
commit 5b1494b3ce
11 changed files with 183 additions and 188 deletions

View File

@@ -20,7 +20,8 @@
"@certd/acme-client": "workspace:^1.20.10",
"@certd/pipeline": "workspace:^1.20.10",
"jszip": "^3.10.1",
"node-forge": "^0.10.0"
"node-forge": "^0.10.0",
"psl": "^1.9.0"
},
"devDependencies": {
"@alicloud/cs20151215": "^3.0.3",
@@ -35,6 +36,7 @@
"@types/lodash": "^4.14.186",
"@types/mocha": "^10.0.0",
"@types/node-forge": "^1.3.0",
"@types/psl": "^1.1.3",
"@typescript-eslint/eslint-plugin": "^5.38.1",
"@typescript-eslint/parser": "^5.38.1",
"chai": "^4.3.6",

View File

@@ -1,4 +1,4 @@
import { Registrable } from "@certd/pipeline";
import { HttpClient, IAccess, ILogger, Registrable } from "@certd/pipeline";
export type DnsProviderDefine = Registrable & {
accessType: string;
@@ -11,13 +11,21 @@ export type CreateRecordOptions = {
fullRecord: string;
type: string;
value: any;
domain: string;
};
export type RemoveRecordOptions = CreateRecordOptions & {
record: any;
};
export type DnsProviderContext = {
access: IAccess;
logger: ILogger;
http: HttpClient;
};
export interface IDnsProvider {
onInstance(): Promise<void>;
createRecord(options: CreateRecordOptions): Promise<any>;
removeRecord(options: RemoveRecordOptions): Promise<any>;
setCtx(ctx: DnsProviderContext): void;
}

View File

@@ -0,0 +1,15 @@
import { CreateRecordOptions, DnsProviderContext, IDnsProvider, RemoveRecordOptions } from "./api";
export abstract class AbstractDnsProvider implements IDnsProvider {
ctx!: DnsProviderContext;
setCtx(ctx: DnsProviderContext) {
this.ctx = ctx;
}
abstract createRecord(options: CreateRecordOptions): Promise<any>;
abstract onInstance(): Promise<void>;
abstract removeRecord(options: RemoveRecordOptions): Promise<any>;
}

View File

@@ -1,3 +1,4 @@
export * from "./api";
export * from "./registry";
export * from "./decorator";
export * from "./base";

View File

@@ -5,6 +5,8 @@ import { Challenge } from "@certd/acme-client/types/rfc8555";
import { Logger } from "log4js";
import { IContext } from "@certd/pipeline";
import { IDnsProvider } from "../../dns-provider";
import psl from "psl";
export type CertInfo = {
crt: string;
key: string;
@@ -65,33 +67,43 @@ export class AcmeService {
return key.toString();
}
parseDomain(fullDomain: string) {
const parsed = psl.parse(fullDomain) as psl.ParsedDomain;
if (parsed.error) {
throw new Error(`解析${fullDomain}域名失败:` + JSON.stringify(parsed.error));
}
return parsed.domain as string;
}
async challengeCreateFn(authz: any, challenge: any, keyAuthorization: string, dnsProvider: IDnsProvider) {
this.logger.info("Triggered challengeCreateFn()");
/* http-01 */
const fullDomain = authz.identifier.value;
if (challenge.type === "http-01") {
const filePath = `/var/www/html/.well-known/acme-challenge/${challenge.token}`;
const fileContents = keyAuthorization;
this.logger.info(`Creating challenge response for ${authz.identifier.value} at path: ${filePath}`);
this.logger.info(`Creating challenge response for ${fullDomain} at path: ${filePath}`);
/* Replace this */
this.logger.info(`Would write "${fileContents}" to path "${filePath}"`);
// await fs.writeFileAsync(filePath, fileContents);
} else if (challenge.type === "dns-01") {
/* dns-01 */
const dnsRecord = `_acme-challenge.${authz.identifier.value}`;
const dnsRecord = `_acme-challenge.${fullDomain}`;
const recordValue = keyAuthorization;
this.logger.info(`Creating TXT record for ${authz.identifier.value}: ${dnsRecord}`);
this.logger.info(`Creating TXT record for ${fullDomain}: ${dnsRecord}`);
/* Replace this */
this.logger.info(`Would create TXT record "${dnsRecord}" with value "${recordValue}"`);
const domain = this.parseDomain(fullDomain);
this.logger.info("解析到域名domain=", domain);
return await dnsProvider.createRecord({
fullRecord: dnsRecord,
type: "TXT",
value: recordValue,
domain,
});
}
}
@@ -111,28 +123,33 @@ export class AcmeService {
this.logger.info("Triggered challengeRemoveFn()");
/* http-01 */
const fullDomain = authz.identifier.value;
if (challenge.type === "http-01") {
const filePath = `/var/www/html/.well-known/acme-challenge/${challenge.token}`;
this.logger.info(`Removing challenge response for ${authz.identifier.value} at path: ${filePath}`);
this.logger.info(`Removing challenge response for ${fullDomain} at path: ${filePath}`);
/* Replace this */
this.logger.info(`Would remove file on path "${filePath}"`);
// await fs.unlinkAsync(filePath);
} else if (challenge.type === "dns-01") {
const dnsRecord = `_acme-challenge.${authz.identifier.value}`;
const dnsRecord = `_acme-challenge.${fullDomain}`;
const recordValue = keyAuthorization;
this.logger.info(`Removing TXT record for ${authz.identifier.value}: ${dnsRecord}`);
this.logger.info(`Removing TXT record for ${fullDomain}: ${dnsRecord}`);
/* Replace this */
this.logger.info(`Would remove TXT record "${dnsRecord}" with value "${recordValue}"`);
const domain = this.parseDomain(fullDomain);
try {
await dnsProvider.removeRecord({
fullRecord: dnsRecord,
type: "TXT",
value: keyAuthorization,
record: recordItem,
domain,
});
} catch (e) {
this.logger.error("删除解析记录出错:", e);

View File

@@ -1,21 +1,10 @@
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 {AcmeService, CertInfo} from "./acme";
import { AcmeService, CertInfo } from "./acme";
import _ from "lodash";
import {Logger} from "log4js";
import {DnsProviderDefine, dnsProviderRegistry} from "../../dns-provider";
import {CertReader} from "./cert-reader";
import { Logger } from "log4js";
import { DnsProviderContext, DnsProviderDefine, dnsProviderRegistry } from "../../dns-provider";
import { CertReader } from "./cert-reader";
import JSZip from "jszip";
export { CertReader };
@@ -242,8 +231,9 @@ export class CertApplyPlugin extends AbstractTaskPlugin {
// @ts-ignore
const dnsProvider: IDnsProvider = new DnsProviderClass();
const context = { access, logger: this.logger, http: this.http };
const context: DnsProviderContext = { access, logger: this.logger, http: this.http };
Decorator.inject(dnsProviderDefine.autowire, dnsProvider, context);
dnsProvider.setCtx(context);
await dnsProvider.onInstance();
const cert = await this.acme.order({