feat: 支持dns-persist-01持久化验证方式申请证书,优化Acme账号的存储方式

This commit is contained in:
xiaojunnuo
2026-05-24 05:42:51 +08:00
parent 8edb6f8727
commit 67b05e2d75
51 changed files with 3352 additions and 110 deletions
+4
View File
@@ -467,6 +467,10 @@ class AcmeClient {
return createHash('sha256').update(result).digest('base64url');
}
if (challenge.type === 'dns-persist-01') {
return '';
}
/* https://datatracker.ietf.org/doc/html/rfc8737 */
if (challenge.type === 'tls-alpn-01') {
return result;
+5 -1
View File
@@ -97,7 +97,11 @@ export interface DnsChallenge extends ChallengeAbstract {
token: string;
}
export type Challenge = HttpChallenge | DnsChallenge;
export interface DnsPersistChallenge extends ChallengeAbstract {
type: "dns-persist-01";
}
export type Challenge = HttpChallenge | DnsChallenge | DnsPersistChallenge;
/**
* Certificate
+20 -1
View File
@@ -170,7 +170,7 @@ export function createChallengeFn(opts = {}) {
if (txtRecords.length === 0) {
throw new Error(`没有找到TXT解析记录(${recordName}`);
throw new Error(`没有找到TXT解析记录(${recordName},请稍后重试`);
}
return txtRecords;
}
@@ -203,6 +203,24 @@ export function createChallengeFn(opts = {}) {
return true;
}
async function verifyDnsPersistChallenge(authz, challenge, keyAuthorization, prefix = '_validation-persist.') {
const recordName = `${prefix}${authz.identifier.value.replace(/^\*\./, '')}`;
log(`本地校验DNS持久验证TXT记录: ${recordName}`);
let recordValues = await walkTxtRecord(recordName, 0, walkFromAuthoritative);
recordValues = [...new Set(recordValues)];
const expected = challenge.expectedRecordValue;
if (!expected) {
log(`未提供dns-persist-01本地校验期望值,跳过精确匹配,仅确认TXT记录存在`);
return true;
}
log(`DNS查询成功, 找到 ${recordValues.length} 条TXT记录:${recordValues}`);
if (!recordValues.length || !recordValues.includes(expected)) {
throw new Error(`没有找到需要的DNS持久验证TXT记录: ${recordName},请稍后重试,期望:${expected},结果:${recordValues}`);
}
log(`DNS持久验证记录匹配成功(${challenge.type}/${recordName}:${expected}`);
return true;
}
/**
* Verify ACME TLS ALPN challenge
*
@@ -234,6 +252,7 @@ export function createChallengeFn(opts = {}) {
challenges: {
'http-01': verifyHttpChallenge,
'dns-01': verifyDnsChallenge,
'dns-persist-01': verifyDnsPersistChallenge,
'tls-alpn-01': verifyTlsAlpnChallenge,
},
walkTxtRecord,
+1 -1
View File
@@ -57,7 +57,7 @@ export interface ClientExternalAccountBindingOptions {
export interface ClientAutoOptions {
csr: CsrBuffer | CsrString;
challengeCreateFn: (authz: Authorization, keyAuthorization: (challenge:rfc8555.Challenge)=>Promise<string>) => Promise<{recordReq?:any,recordRes?:any,dnsProvider?:any,challenge: rfc8555.Challenge,keyAuthorization:string}>;
challengeCreateFn: (authz: Authorization, keyAuthorization: (challenge:rfc8555.Challenge)=>Promise<string>) => Promise<{recordReq?:any,recordRes?:any,dnsProvider?:any,challenge: rfc8555.Challenge,keyAuthorization:string,httpUploader?:any}>;
challengeRemoveFn: (authz: Authorization, challenge: rfc8555.Challenge, keyAuthorization: string,recordReq:any, recordRes:any,dnsProvider:any,httpUploader:any) => Promise<any>;
email?: string;
termsOfServiceAgreed?: boolean;
+5 -1
View File
@@ -97,7 +97,11 @@ export interface DnsChallenge extends ChallengeAbstract {
token: string;
}
export type Challenge = HttpChallenge | DnsChallenge;
export interface DnsPersistChallenge extends ChallengeAbstract {
type: 'dns-persist-01';
}
export type Challenge = HttpChallenge | DnsChallenge | DnsPersistChallenge;
/**
* Certificate
+1
View File
@@ -19,6 +19,7 @@ export type AccessInputDefine = FormItemProps & {
};
export type AccessDefine = Registrable & {
icon?: string;
subtype?: string;
input?: {
[key: string]: AccessInputDefine;
};