fix: 根据SOA记录判断子域名托管有缺陷,改回手动配置子域名托管记录的方式

pull/409/head
xiaojunnuo 2025-05-05 21:43:39 +08:00
parent 424890a1e1
commit 1b280a2940
8 changed files with 85 additions and 58 deletions

View File

@ -0,0 +1,11 @@
import {assert} from 'chai'
import {resolveDomainBySoaRecord} from "../src/util.js"
describe('dns', () => {
it('resolveDomainBySoaRecord', async () => {
const resp = await resolveDomainBySoaRecord("a.corp.smartdeer.com")
assert.equal(resp, "smartdeer.com")
});
})

View File

@ -1,7 +1,6 @@
import { IDomainParser, ISubDomainsGetter } from "./api"; import { IDomainParser, ISubDomainsGetter } from "./api";
//@ts-ignore //@ts-ignore
import psl from "psl"; import psl from "psl";
import { resolveDomainBySoaRecord } from "@certd/acme-client";
import { logger, utils } from "@certd/basic"; import { logger, utils } from "@certd/basic";
export class DomainParser implements IDomainParser { export class DomainParser implements IDomainParser {
@ -10,7 +9,7 @@ export class DomainParser implements IDomainParser {
this.subDomainsGetter = subDomainsGetter; this.subDomainsGetter = subDomainsGetter;
} }
parseDomain(fullDomain: string) { parseDomainByPsl(fullDomain: string) {
const parsed = psl.parse(fullDomain) as psl.ParsedDomain; const parsed = psl.parse(fullDomain) as psl.ParsedDomain;
if (parsed.error) { if (parsed.error) {
throw new Error(`解析${fullDomain}域名失败:` + JSON.stringify(parsed.error)); throw new Error(`解析${fullDomain}域名失败:` + JSON.stringify(parsed.error));
@ -26,30 +25,34 @@ export class DomainParser implements IDomainParser {
logger.info(`从缓存获取到主域名:${fullDomain}->${value}`); logger.info(`从缓存获取到主域名:${fullDomain}->${value}`);
return value; return value;
} }
try { // try {
const mainDomain = await resolveDomainBySoaRecord(fullDomain); // const mainDomain = await resolveDomainBySoaRecord(fullDomain);
if (mainDomain) { // if (mainDomain) {
utils.cache.set(cacheKey, mainDomain, { // utils.cache.set(cacheKey, mainDomain, {
ttl: 2 * 60 * 1000, // ttl: 2 * 60 * 1000,
}); // });
logger.info(`获取到主域名:${fullDomain}->${mainDomain}`); // logger.info(`获取到主域名:${fullDomain}->${mainDomain}`);
return mainDomain; // return mainDomain;
}
} catch (e) {
logger.error("从SOA获取主域名失败", e.message);
}
// const subDomains = await this.subDomainsGetter.getSubDomains();
// if (subDomains && subDomains.length > 0) {
// for (const subDomain of subDomains) {
// if (fullDomain.endsWith(subDomain)) {
// //找到子域名托管
// return subDomain;
// }
// } // }
// } catch (e) {
// logger.error("从SOA获取主域名失败", e.message);
// } // }
const res = this.parseDomain(fullDomain); const subDomains = await this.subDomainsGetter.getSubDomains();
if (subDomains && subDomains.length > 0) {
for (const subDomain of subDomains) {
if (fullDomain.endsWith(subDomain)) {
//找到子域名托管
utils.cache.set(cacheKey, subDomain, {
ttl: 2 * 60 * 1000,
});
logger.info(`获取到子域名托管域名:${fullDomain}->${subDomain}`);
return subDomain;
}
}
}
const res = this.parseDomainByPsl(fullDomain);
logger.info(`从psl获取主域名:${fullDomain}->${res}`); logger.info(`从psl获取主域名:${fullDomain}->${res}`);
return res; return res;
} }

View File

@ -248,7 +248,10 @@ export class AcmeService {
fullRecord = cname.fullRecord; fullRecord = cname.fullRecord;
} }
} else { } else {
this.logger.error("未找到域名Cname校验计划使用默认的dnsProvider"); this.logger.error(`未找到域名${fullDomain}的CNAME校验计划请修改证书申请配置`);
}
if (dnsProvider == null) {
throw new Error(`未找到域名${fullDomain}CNAME校验计划的DnsProvider请修改证书申请配置`);
} }
} else if (domainVerifyPlan.type === "http") { } else if (domainVerifyPlan.type === "http") {
const httpVerifyPlan = domainVerifyPlan.httpVerifyPlan; const httpVerifyPlan = domainVerifyPlan.httpVerifyPlan;

View File

@ -8,7 +8,7 @@
<fs-copyable v-model="cnameRecord.hostRecord"></fs-copyable> <fs-copyable v-model="cnameRecord.hostRecord"></fs-copyable>
</td> </td>
<td style="text-align: center">CNAME</td> <td style="text-align: center">CNAME</td>
<td class="record-value"> <td class="record-value" :title="cnameRecord.recordValue">
<fs-copyable v-model="cnameRecord.recordValue"></fs-copyable> <fs-copyable v-model="cnameRecord.recordValue"></fs-copyable>
</td> </td>
<td class="status center flex-center"> <td class="status center flex-center">
@ -38,12 +38,12 @@ const statusDict = dict({
{ label: "验证中", value: "validating", color: "blue" }, { label: "验证中", value: "validating", color: "blue" },
{ label: "验证成功", value: "valid", color: "green" }, { label: "验证成功", value: "valid", color: "green" },
{ label: "验证失败", value: "failed", color: "red" }, { label: "验证失败", value: "failed", color: "red" },
{ label: "验证超时", value: "timeout", color: "red" } { label: "验证超时", value: "timeout", color: "red" },
] ],
}); });
defineOptions({ defineOptions({
name: "CnameRecordInfo" name: "CnameRecordInfo",
}); });
const props = defineProps<{ const props = defineProps<{
@ -55,7 +55,7 @@ const emit = defineEmits<{
{ {
id: number | null; id: number | null;
status: string | null; status: string | null;
} },
]; ];
}>(); }>();
@ -64,7 +64,7 @@ const cnameRecord = ref<CnameRecord | null>(null);
function onRecordChange() { function onRecordChange() {
emit("change", { emit("change", {
id: cnameRecord.value?.id, id: cnameRecord.value?.id,
status: cnameRecord.value?.status status: cnameRecord.value?.status,
}); });
} }
@ -90,11 +90,11 @@ async function doRefresh() {
watch( watch(
() => props.domain, () => props.domain,
async (value) => { async value => {
await doRefresh(); await doRefresh();
}, },
{ {
immediate: true immediate: true,
} }
); );

View File

@ -16,9 +16,9 @@ export const aboutResource = [
show: () => { show: () => {
const settingStore = useSettingStore(); const settingStore = useSettingStore();
return !settingStore.isComm; return !settingStore.isComm;
} },
} },
} },
]; ];
export default aboutResource; export default aboutResource;

View File

@ -98,17 +98,17 @@ export const certdResources = [
keepAlive: true, keepAlive: true,
}, },
}, },
// { {
// title: "子域名托管设置", title: "子域名托管设置",
// name: "SubDomain", name: "SubDomain",
// path: "/certd/pipeline/subDomain", path: "/certd/pipeline/subDomain",
// component: "/certd/pipeline/sub-domain/index.vue", component: "/certd/pipeline/sub-domain/index.vue",
// meta: { meta: {
// icon: "material-symbols:approval-delegation-outline", icon: "material-symbols:approval-delegation-outline",
// auth: true, auth: true,
// keepAlive: true, keepAlive: true,
// }, },
// }, },
{ {
title: "流水线分组管理", title: "流水线分组管理",
name: "PipelineGroupManager", name: "PipelineGroupManager",

View File

@ -114,4 +114,5 @@ export class CnameProviderService extends BaseService<CnameProviderEntity> {
} }
return await super.info(id, infoIgnoreProperty); return await super.info(id, infoIgnoreProperty);
} }
} }

View File

@ -20,6 +20,7 @@ type CnameCheckCacheValue = {
recordRes?: any; recordRes?: any;
startTime: number; startTime: number;
intervalId?: NodeJS.Timeout; intervalId?: NodeJS.Timeout;
dnsProvider?: IDnsProvider;
}; };
/** /**
* *
@ -235,6 +236,23 @@ export class CnameRecordService extends BaseService<CnameRecordEntity> {
return dnsProvider; return dnsProvider;
}; };
const clearVerifyRecord = async () => {
cache.delete(cacheKey);
try {
let dnsProvider =value.dnsProvider
if (!dnsProvider) {
dnsProvider = await buildDnsProvider();
}
await dnsProvider.removeRecord({
recordReq: value.recordReq,
recordRes: value.recordRes,
});
logger.info('删除CNAME的校验DNS记录成功');
} catch (e) {
logger.error(`删除CNAME的校验DNS记录失败 ${e.message}req:${JSON.stringify(value.recordReq)}recordRes:${JSON.stringify(value.recordRes)}`, e);
}
};
const checkRecordValue = async () => { const checkRecordValue = async () => {
if (value.pass) { if (value.pass) {
return true; return true;
@ -243,7 +261,7 @@ export class CnameRecordService extends BaseService<CnameRecordEntity> {
logger.warn(`cname验证超时,停止检查,${bean.domain} ${testRecordValue}`); logger.warn(`cname验证超时,停止检查,${bean.domain} ${testRecordValue}`);
clearInterval(value.intervalId); clearInterval(value.intervalId);
await this.updateStatus(bean.id, 'timeout'); await this.updateStatus(bean.id, 'timeout');
cache.delete(cacheKey); await clearVerifyRecord()
return false; return false;
} }
@ -270,17 +288,7 @@ export class CnameRecordService extends BaseService<CnameRecordEntity> {
logger.info(`检测到CNAME配置,修改状态 ${fullDomain} ${testRecordValue}`); logger.info(`检测到CNAME配置,修改状态 ${fullDomain} ${testRecordValue}`);
await this.updateStatus(bean.id, 'valid'); await this.updateStatus(bean.id, 'valid');
value.pass = true; value.pass = true;
cache.delete(cacheKey); await clearVerifyRecord()
try {
const dnsProvider = await buildDnsProvider();
await dnsProvider.removeRecord({
recordReq: value.recordReq,
recordRes: value.recordRes,
});
logger.info('删除CNAME的校验DNS记录成功');
} catch (e) {
logger.error(`删除CNAME的校验DNS记录失败 ${e.message}req:${JSON.stringify(value.recordReq)}recordRes:${JSON.stringify(value.recordRes)}`, e);
}
return success; return success;
} }
}; };
@ -306,6 +314,7 @@ export class CnameRecordService extends BaseService<CnameRecordEntity> {
}; };
const dnsProvider = await buildDnsProvider(); const dnsProvider = await buildDnsProvider();
const recordRes = await dnsProvider.createRecord(req); const recordRes = await dnsProvider.createRecord(req);
value.dnsProvider = dnsProvider;
value.validating = true; value.validating = true;
value.recordReq = req; value.recordReq = req;
value.recordRes = recordRes; value.recordRes = recordRes;