mirror of
https://github.com/certd/certd.git
synced 2025-11-25 09:10:11 +08:00
pref: 支持子域名托管的域名证书申请
This commit is contained in:
@@ -27,6 +27,7 @@ export type DnsProviderContext = {
|
||||
logger: ILogger;
|
||||
http: HttpClient;
|
||||
utils: typeof utils;
|
||||
domainParser: IDomainParser;
|
||||
};
|
||||
|
||||
export interface IDnsProvider<T = any> {
|
||||
@@ -35,3 +36,11 @@ export interface IDnsProvider<T = any> {
|
||||
removeRecord(options: RemoveRecordOptions<T>): Promise<void>;
|
||||
setCtx(ctx: DnsProviderContext): void;
|
||||
}
|
||||
|
||||
export interface ISubDomainsGetter {
|
||||
getSubDomains(): Promise<string[]>;
|
||||
}
|
||||
|
||||
export interface IDomainParser {
|
||||
parse(fullDomain: string): Promise<string>;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
import { CreateRecordOptions, DnsProviderContext, DnsProviderDefine, IDnsProvider, RemoveRecordOptions } from "./api.js";
|
||||
//@ts-ignore
|
||||
import psl from "psl";
|
||||
import { dnsProviderRegistry } from "./registry.js";
|
||||
import { Decorator } from "@certd/pipeline";
|
||||
import { HttpClient, ILogger } from "@certd/basic";
|
||||
@@ -16,6 +14,10 @@ export abstract class AbstractDnsProvider<T = any> implements IDnsProvider<T> {
|
||||
this.http = ctx.http;
|
||||
}
|
||||
|
||||
async parseDomain(fullDomain: string) {
|
||||
return await this.ctx.domainParser.parse(fullDomain);
|
||||
}
|
||||
|
||||
abstract createRecord(options: CreateRecordOptions): Promise<T>;
|
||||
|
||||
abstract onInstance(): Promise<void>;
|
||||
@@ -23,14 +25,6 @@ export abstract class AbstractDnsProvider<T = any> implements IDnsProvider<T> {
|
||||
abstract removeRecord(options: RemoveRecordOptions<T>): Promise<void>;
|
||||
}
|
||||
|
||||
export function 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;
|
||||
}
|
||||
|
||||
export async function createDnsProvider(opts: { dnsProviderType: string; context: DnsProviderContext }): Promise<IDnsProvider> {
|
||||
const { dnsProviderType, context } = opts;
|
||||
const dnsProviderPlugin = dnsProviderRegistry.get(dnsProviderType);
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
import { IDomainParser, ISubDomainsGetter } from "./api";
|
||||
//@ts-ignore
|
||||
import psl from "psl";
|
||||
|
||||
export class DomainParser implements IDomainParser {
|
||||
subDomainsGetter: ISubDomainsGetter;
|
||||
constructor(subDomainsGetter: ISubDomainsGetter) {
|
||||
this.subDomainsGetter = subDomainsGetter;
|
||||
}
|
||||
|
||||
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 parse(fullDomain: string) {
|
||||
const subDomains = await this.subDomainsGetter.getSubDomains();
|
||||
if (subDomains && subDomains.length > 0) {
|
||||
for (const subDomain of subDomains) {
|
||||
if (fullDomain.endsWith(subDomain)) {
|
||||
//找到子域名托管
|
||||
return subDomain;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return this.parseDomain(fullDomain);
|
||||
}
|
||||
}
|
||||
@@ -5,8 +5,9 @@ import * as _ from "lodash-es";
|
||||
import { Challenge } from "@certd/acme-client/types/rfc8555";
|
||||
import { IContext } from "@certd/pipeline";
|
||||
import { ILogger, utils } from "@certd/basic";
|
||||
import { IDnsProvider, parseDomain } from "../../dns-provider/index.js";
|
||||
import { IDnsProvider, IDomainParser } from "../../dns-provider/index.js";
|
||||
import { HttpChallengeUploader } from "./uploads/api.js";
|
||||
|
||||
export type CnameVerifyPlan = {
|
||||
type?: string;
|
||||
domain: string;
|
||||
@@ -61,6 +62,8 @@ type AcmeServiceOptions = {
|
||||
privateKeyType?: PrivateKeyType;
|
||||
signal?: AbortSignal;
|
||||
maxCheckRetryCount?: number;
|
||||
userId: number;
|
||||
domainParser: IDomainParser;
|
||||
};
|
||||
|
||||
export class AcmeService {
|
||||
@@ -174,7 +177,7 @@ export class AcmeService {
|
||||
this.logger.info("Triggered challengeCreateFn()");
|
||||
|
||||
const fullDomain = authz.identifier.value;
|
||||
let domain = parseDomain(fullDomain);
|
||||
let domain = await this.options.domainParser.parse(fullDomain);
|
||||
this.logger.info("主域名为:" + domain);
|
||||
|
||||
const getChallenge = (type: string) => {
|
||||
@@ -240,7 +243,7 @@ export class AcmeService {
|
||||
const cname = cnameVerifyPlan[fullDomain];
|
||||
if (cname) {
|
||||
dnsProvider = cname.dnsProvider;
|
||||
domain = parseDomain(cname.domain);
|
||||
domain = await this.options.domainParser.parse(cname.domain);
|
||||
fullRecord = cname.fullRecord;
|
||||
}
|
||||
} else {
|
||||
|
||||
@@ -4,12 +4,13 @@ import { utils } from "@certd/basic";
|
||||
import type { CertInfo, CnameVerifyPlan, DomainsVerifyPlan, HttpVerifyPlan, PrivateKeyType, SSLProvider } from "./acme.js";
|
||||
import { AcmeService } from "./acme.js";
|
||||
import * as _ from "lodash-es";
|
||||
import { createDnsProvider, DnsProviderContext, IDnsProvider } from "../../dns-provider/index.js";
|
||||
import { createDnsProvider, DnsProviderContext, IDnsProvider, ISubDomainsGetter } from "../../dns-provider/index.js";
|
||||
import { CertReader } from "./cert-reader.js";
|
||||
import { CertApplyBasePlugin } from "./base.js";
|
||||
import { GoogleClient } from "../../libs/google.js";
|
||||
import { EabAccess } from "../../access";
|
||||
import { httpChallengeUploaderFactory } from "./uploads/factory.js";
|
||||
import { DomainParser } from "../../dns-provider/domain-parser.js";
|
||||
export * from "./base.js";
|
||||
export type { CertInfo };
|
||||
export * from "./cert-reader.js";
|
||||
@@ -314,7 +315,10 @@ HTTP文件验证:不支持泛域名,需要配置网站文件上传`,
|
||||
}
|
||||
}
|
||||
this.eab = eab;
|
||||
const subDomainsGetter = await this.ctx.serviceGetter.get<ISubDomainsGetter>("subDomainsGetter");
|
||||
const domainParser = new DomainParser(subDomainsGetter);
|
||||
this.acme = new AcmeService({
|
||||
userId: this.ctx.user.id,
|
||||
userContext: this.userContext,
|
||||
logger: this.logger,
|
||||
sslProvider: this.sslProvider,
|
||||
@@ -325,8 +329,7 @@ HTTP文件验证:不支持泛域名,需要配置网站文件上传`,
|
||||
privateKeyType: this.privateKeyType,
|
||||
signal: this.ctx.signal,
|
||||
maxCheckRetryCount: this.maxCheckRetryCount,
|
||||
// cnameProxyService: this.ctx.cnameProxyService,
|
||||
// dnsProviderCreator: this.createDnsProvider.bind(this),
|
||||
domainParser,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -387,7 +390,8 @@ HTTP文件验证:不支持泛域名,需要配置网站文件上传`,
|
||||
}
|
||||
|
||||
async createDnsProvider(dnsProviderType: string, dnsProviderAccess: any): Promise<IDnsProvider> {
|
||||
const context: DnsProviderContext = { access: dnsProviderAccess, logger: this.logger, http: this.ctx.http, utils };
|
||||
const domainParser = this.acme.options.domainParser;
|
||||
const context: DnsProviderContext = { access: dnsProviderAccess, logger: this.logger, http: this.ctx.http, utils, domainParser };
|
||||
return await createDnsProvider({
|
||||
dnsProviderType,
|
||||
context,
|
||||
|
||||
Reference in New Issue
Block a user