fix(integrations): не считать CNAME hostname за IP при sync
JOIN dns_record клал content CNAME в target_ips и блокировал разворот до origin IP / service IPs. Co-authored-by: Cursor <[email protected]>
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
import { resolve4 } from "node:dns/promises";
|
||||
import type { CfdmBindingSyncItem, ServiceBindingView } from "@cfdm/shared";
|
||||
import { isIpLiteral } from "@cfdm/shared";
|
||||
import type { Db } from "@cfdm/db";
|
||||
import { repos, getAppSettingsSecrets, touchVpsTrackerSync } from "@cfdm/db";
|
||||
|
||||
@@ -42,8 +43,8 @@ function resolveIpsLocally(
|
||||
const binding = index.byFqdn.get(key);
|
||||
if (!binding) return [];
|
||||
|
||||
if (binding.target_ips.length > 0) {
|
||||
return [...binding.target_ips];
|
||||
if (binding.target_ips.some(isIpLiteral)) {
|
||||
return binding.target_ips.filter(isIpLiteral);
|
||||
}
|
||||
|
||||
const cname = binding.cname_target?.trim();
|
||||
@@ -66,11 +67,11 @@ async function resolveIpsViaDns(hostname: string): Promise<string[]> {
|
||||
|
||||
/**
|
||||
* IP для матчинга в VPS Tracker:
|
||||
* 1) A-записи binding
|
||||
* 2) разворот локальной CNAME-цепочки по другим bindings
|
||||
* 3) origin A/AAAA из dns_records CFDM (даже proxied — content = origin)
|
||||
* 4) публичный DNS (resolve4) — часто CF anycast, слабый сигнал
|
||||
* 5) IP сервиса
|
||||
* 1) A/AAAA binding (только литералы IP — не CNAME hostname из dns_record.content)
|
||||
* 2) локальная CNAME-цепочка по другим bindings
|
||||
* 3) origin A/AAAA из dns_records CFDM
|
||||
* 4) IP сервиса
|
||||
* 5) публичный DNS — последний и обычно вреден (CF anycast при proxied)
|
||||
*/
|
||||
export async function resolveBindingIpsForSync(
|
||||
binding: ServiceBindingView,
|
||||
@@ -78,26 +79,32 @@ export async function resolveBindingIpsForSync(
|
||||
index: BindingIpIndex,
|
||||
db?: Db,
|
||||
): Promise<string[]> {
|
||||
if (binding.target_ips.length > 0) {
|
||||
return [...binding.target_ips];
|
||||
const directIps = binding.target_ips.filter(isIpLiteral);
|
||||
if (directIps.length > 0) {
|
||||
return [...directIps];
|
||||
}
|
||||
|
||||
const cname = binding.cname_target?.trim();
|
||||
if (cname) {
|
||||
const targetFqdn = normalizeCnameHost(cname, binding.zone_name);
|
||||
const local = resolveIpsLocally(index, targetFqdn);
|
||||
const local = resolveIpsLocally(index, targetFqdn).filter(isIpLiteral);
|
||||
if (local.length > 0) return local;
|
||||
|
||||
if (db) {
|
||||
const fromTable = repos.listOriginIpsForFqdn(db, targetFqdn);
|
||||
const fromTable = repos.listOriginIpsForFqdn(db, targetFqdn).filter(isIpLiteral);
|
||||
if (fromTable.length > 0) return fromTable;
|
||||
}
|
||||
}
|
||||
|
||||
const viaDns = await resolveIpsViaDns(targetFqdn);
|
||||
const fromService = serviceIps.filter(isIpLiteral);
|
||||
if (fromService.length > 0) return [...fromService];
|
||||
|
||||
if (cname) {
|
||||
const targetFqdn = normalizeCnameHost(cname, binding.zone_name);
|
||||
const viaDns = (await resolveIpsViaDns(targetFqdn)).filter(isIpLiteral);
|
||||
if (viaDns.length > 0) return viaDns;
|
||||
}
|
||||
|
||||
if (serviceIps.length > 0) return [...serviceIps];
|
||||
return [];
|
||||
}
|
||||
|
||||
|
||||
@@ -108,4 +108,42 @@ describe("resolveBindingIpsForSync", () => {
|
||||
const ips = await resolveBindingIpsForSync(cname, ["198.51.100.7"], index);
|
||||
expect(ips).toEqual(["198.51.100.7"]);
|
||||
});
|
||||
|
||||
it("does not treat CNAME hostname in target_ips as an IP", async () => {
|
||||
const target = binding({
|
||||
id: 1,
|
||||
hostname: "ihome",
|
||||
zone_name: "rkns.top",
|
||||
target_ips: ["203.0.113.10"],
|
||||
});
|
||||
const cname = binding({
|
||||
id: 2,
|
||||
hostname: "imsk",
|
||||
zone_name: "rkns.top",
|
||||
cname_target: "ihome.rkns.top",
|
||||
// как в прод: JOIN dns_record кладёт CNAME content в target_ip → target_ips
|
||||
target_ips: ["ihome.rkns.top"],
|
||||
});
|
||||
const index = {
|
||||
byFqdn: new Map([
|
||||
["ihome.rkns.top", target],
|
||||
["imsk.rkns.top", cname],
|
||||
]),
|
||||
};
|
||||
const ips = await resolveBindingIpsForSync(cname, [], index);
|
||||
expect(ips).toEqual(["203.0.113.10"]);
|
||||
});
|
||||
|
||||
it("prefers service IPs over empty CNAME resolution chain", async () => {
|
||||
const cname = binding({
|
||||
id: 2,
|
||||
hostname: "mhome",
|
||||
zone_name: "rkns.top",
|
||||
cname_target: "macloud.rkns.top",
|
||||
target_ips: ["macloud.rkns.top"],
|
||||
});
|
||||
const index = { byFqdn: new Map([["mhome.rkns.top", cname]]) };
|
||||
const ips = await resolveBindingIpsForSync(cname, ["203.0.113.55"], index);
|
||||
expect(ips).toEqual(["203.0.113.55"]);
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user