mirror of https://github.com/certd/certd
fix: 根据SOA记录判断子域名托管有缺陷,改回手动配置子域名托管记录的方式
parent
424890a1e1
commit
1b280a2940
|
@ -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")
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
})
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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,
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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",
|
||||||
|
|
|
@ -114,4 +114,5 @@ export class CnameProviderService extends BaseService<CnameProviderEntity> {
|
||||||
}
|
}
|
||||||
return await super.info(id, infoIgnoreProperty);
|
return await super.info(id, infoIgnoreProperty);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
|
Loading…
Reference in New Issue