fix[domains]: Add CDN provider detection with dynamic display names

Implements generic CDN detection service supporting Cloudflare, Fastly,
and Bunny CDN. Replaces hardcoded "Behind Cloudflare" text with
dynamic provider names and adds IP range validation for comprehensive
CDN detection.
This commit is contained in:
Torsten Dittmann
2025-06-01 23:03:00 +02:00
parent 2619cb49d1
commit 88e862544b
3 changed files with 645 additions and 30 deletions

View File

@@ -119,6 +119,7 @@ export const ShowDomains = ({ id, type }: Props) => {
isValid: result.isValid,
error: result.error,
resolvedIp: result.resolvedIp,
cdnProvider: result.cdnProvider,
message: result.error && result.isValid ? result.error : undefined,
},
}));
@@ -355,7 +356,7 @@ export const ShowDomains = ({ id, type }: Props) => {
<>
<CheckCircle2 className="size-3 mr-1" />
{validationState.message
? "Behind Cloudflare"
? `Behind ${validationState.cdnProvider}`
: "DNS Valid"}
</>
) : validationState?.error ? (

View File

@@ -0,0 +1,633 @@
// CDN Provider Interface
export interface CDNProvider {
name: string;
displayName: string;
checkIp: (ip: string) => boolean;
warningMessage: string;
}
const isIPInCIDR = (ip: string, cidr: string): boolean => {
const [network, prefixLength] = cidr.split("/");
const prefix = Number.parseInt(prefixLength, 10);
// Convert IP addresses to 32-bit integers
const ipToInt = (ipStr: string): number => {
return (
ipStr
.split(".")
.reduce((acc, octet) => (acc << 8) + Number.parseInt(octet, 10), 0) >>>
0
);
};
const ipInt = ipToInt(ip);
const networkInt = ipToInt(network);
const mask = (0xffffffff << (32 - prefix)) >>> 0;
return (ipInt & mask) === (networkInt & mask);
};
// Cloudflare IP ranges
// https://www.cloudflare.com/ips-v4
const CLOUDFLARE_IP_RANGES = [
"173.245.48.0/20",
"103.21.244.0/22",
"103.22.200.0/22",
"103.31.4.0/22",
"141.101.64.0/18",
"108.162.192.0/18",
"190.93.240.0/20",
"188.114.96.0/20",
"197.234.240.0/22",
"198.41.128.0/17",
"162.158.0.0/15",
"104.16.0.0/13",
"104.24.0.0/14",
"172.64.0.0/13",
"131.0.72.0/22",
];
// Fastly IP ranges
// https://api.fastly.com/public-ip-list
const FASTLY_IP_RANGES = [
"23.235.32.0/20",
"43.249.72.0/22",
"103.244.50.0/24",
"103.245.222.0/23",
"103.245.224.0/24",
"104.156.80.0/20",
"140.248.64.0/18",
"140.248.128.0/17",
"146.75.0.0/17",
"151.101.0.0/16",
"157.52.64.0/18",
"167.82.0.0/17",
"167.82.128.0/20",
"167.82.160.0/20",
"167.82.224.0/20",
"172.111.64.0/18",
"185.31.16.0/22",
"199.27.72.0/21",
"199.232.0.0/16",
];
// Bunny CDN IP addresses
// https://bunnycdn.com/api/system/edgeserverlist
const BUNNY_CDN_IPS = new Set([
"89.187.188.227",
"89.187.188.228",
"139.180.134.196",
"89.38.96.158",
"89.187.162.249",
"89.187.162.242",
"185.102.217.65",
"185.93.1.243",
"156.146.40.49",
"185.59.220.199",
"185.59.220.198",
"195.181.166.158",
"185.180.12.68",
"138.199.24.209",
"138.199.24.211",
"79.127.216.111",
"79.127.216.112",
"89.187.169.47",
"138.199.24.218",
"185.40.106.117",
"200.25.45.4",
"200.25.57.5",
"193.162.131.1",
"200.25.11.8",
"200.25.53.5",
"200.25.13.98",
"41.242.2.18",
"200.25.62.5",
"200.25.38.69",
"200.25.42.70",
"200.25.36.166",
"195.206.229.106",
"194.242.11.186",
"185.164.35.8",
"94.20.154.22",
"185.93.1.244",
"156.59.145.154",
"143.244.49.177",
"138.199.46.66",
"138.199.37.227",
"138.199.37.231",
"138.199.37.230",
"138.199.37.229",
"138.199.46.69",
"138.199.46.68",
"138.199.46.67",
"185.93.1.246",
"138.199.37.232",
"195.181.163.196",
"107.182.163.162",
"195.181.163.195",
"84.17.46.53",
"212.102.40.114",
"84.17.46.54",
"138.199.40.58",
"143.244.38.134",
"143.244.38.136",
"185.152.64.17",
"84.17.59.115",
"89.187.165.194",
"138.199.15.193",
"89.35.237.170",
"37.19.216.130",
"185.93.1.247",
"185.93.3.244",
"143.244.49.179",
"143.244.49.180",
"138.199.9.104",
"185.152.66.243",
"143.244.49.178",
"169.150.221.147",
"200.25.18.73",
"84.17.63.178",
"200.25.32.131",
"37.19.207.34",
"192.189.65.146",
"143.244.45.177",
"185.93.1.249",
"185.93.1.250",
"169.150.215.115",
"209.177.87.197",
"156.146.56.162",
"156.146.56.161",
"185.93.2.246",
"185.93.2.245",
"212.102.40.113",
"185.93.2.244",
"143.244.50.82",
"143.244.50.83",
"156.146.56.163",
"129.227.217.178",
"129.227.217.179",
"200.25.69.94",
"128.1.52.179",
"200.25.16.103",
"15.235.54.226",
"102.67.138.155",
"156.146.43.65",
"195.181.163.203",
"195.181.163.202",
"156.146.56.169",
"156.146.56.170",
"156.146.56.166",
"156.146.56.171",
"169.150.207.210",
"156.146.56.167",
"143.244.50.84",
"143.244.50.85",
"143.244.50.86",
"143.244.50.87",
"156.146.56.168",
"169.150.207.211",
"212.102.50.59",
"146.185.248.15",
"143.244.50.90",
"143.244.50.91",
"143.244.50.88",
"143.244.50.209",
"143.244.50.213",
"143.244.50.214",
"143.244.49.183",
"143.244.50.89",
"143.244.50.210",
"143.244.50.211",
"143.244.50.212",
"5.42.206.66",
"94.46.27.186",
"169.150.207.213",
"169.150.207.214",
"169.150.207.215",
"169.150.207.212",
"169.150.219.114",
"169.150.202.210",
"169.150.242.193",
"185.93.1.251",
"169.150.207.216",
"169.150.207.217",
"169.150.238.19",
"102.219.126.20",
"156.59.66.182",
"122.10.251.130",
"185.24.11.18",
"138.199.36.7",
"138.199.36.8",
"138.199.36.9",
"138.199.36.10",
"138.199.36.11",
"138.199.37.225",
"84.17.46.49",
"84.17.37.217",
"169.150.225.35",
"169.150.225.36",
"169.150.225.37",
"169.150.225.38",
"169.150.225.39",
"169.150.225.34",
"169.150.236.97",
"169.150.236.98",
"169.150.236.99",
"169.150.236.100",
"93.189.63.149",
"143.244.56.49",
"143.244.56.50",
"143.244.56.51",
"169.150.247.40",
"169.150.247.33",
"169.150.247.34",
"169.150.247.35",
"169.150.247.36",
"169.150.247.37",
"169.150.247.38",
"169.150.247.39",
"38.142.94.218",
"87.249.137.52",
"38.104.169.186",
"66.181.163.74",
"84.17.38.227",
"84.17.38.228",
"84.17.38.229",
"84.17.38.230",
"84.17.38.231",
"84.17.38.232",
"169.150.225.41",
"169.150.225.42",
"169.150.249.162",
"169.150.249.163",
"169.150.249.164",
"169.150.249.165",
"169.150.249.166",
"169.150.249.167",
"169.150.249.168",
"169.150.249.169",
"185.131.64.124",
"103.112.0.22",
"37.236.234.2",
"169.150.252.209",
"212.102.46.118",
"192.169.120.162",
"93.180.217.214",
"37.19.203.178",
"107.155.47.146",
"193.201.190.174",
"156.59.95.218",
"213.170.143.139",
"129.227.186.154",
"195.238.127.98",
"200.25.22.6",
"204.16.244.92",
"200.25.70.101",
"200.25.66.100",
"139.180.209.182",
"103.108.231.41",
"103.108.229.5",
"103.216.220.9",
"169.150.225.40",
"212.102.50.49",
"212.102.50.52",
"109.61.83.242",
"109.61.83.243",
"212.102.50.50",
"169.150.225.43",
"45.125.247.57",
"103.235.199.170",
"128.1.35.170",
"38.32.110.58",
"169.150.220.228",
"169.150.220.229",
"169.150.220.230",
"169.150.220.231",
"138.199.4.179",
"207.211.214.145",
"109.61.86.193",
"103.214.20.95",
"178.175.134.51",
"138.199.4.178",
"172.255.253.140",
"185.24.11.19",
"109.61.83.244",
"109.61.83.245",
"84.17.38.250",
"84.17.38.251",
"146.59.69.202",
"146.70.80.218",
"200.25.80.74",
"79.127.213.214",
"79.127.213.215",
"79.127.213.216",
"79.127.213.217",
"195.69.140.112",
"109.61.83.247",
"109.61.83.246",
"185.93.2.248",
"109.61.83.249",
"109.61.83.250",
"109.61.83.251",
"46.199.75.115",
"141.164.35.160",
"109.61.83.97",
"109.61.83.98",
"109.61.83.99",
"129.227.179.18",
"185.180.14.250",
"152.89.160.26",
"5.189.202.62",
"98.98.242.142",
"156.59.92.126",
"84.17.59.117",
"79.127.216.66",
"79.127.204.113",
"79.127.237.132",
"169.150.236.104",
"169.150.236.105",
"37.27.135.61",
"158.51.123.205",
"156.146.43.70",
"156.146.43.71",
"156.146.43.72",
"180.149.231.175",
"185.93.2.243",
"143.244.56.52",
"143.244.56.53",
"143.244.56.54",
"143.244.56.55",
"143.244.56.56",
"143.244.56.57",
"143.244.56.58",
"144.76.236.44",
"88.198.57.50",
"78.46.69.199",
"136.243.16.49",
"138.201.86.122",
"136.243.42.90",
"88.99.95.221",
"178.63.2.112",
"5.9.98.45",
"136.243.42.10",
"169.150.236.106",
"169.150.236.107",
"185.93.1.242",
"185.93.1.245",
"143.244.60.193",
"195.181.163.194",
"79.127.188.193",
"79.127.188.196",
"79.127.188.194",
"79.127.188.195",
"104.166.144.106",
"156.59.126.78",
"185.135.85.154",
"38.54.5.37",
"38.54.3.92",
"185.165.170.74",
"207.121.80.118",
"207.121.46.228",
"207.121.46.236",
"207.121.46.244",
"207.121.46.252",
"216.202.235.164",
"207.121.46.220",
"207.121.75.132",
"207.121.80.12",
"207.121.80.172",
"207.121.90.60",
"207.121.90.68",
"207.121.97.204",
"207.121.90.252",
"207.121.97.236",
"207.121.99.12",
"138.199.24.219",
"185.93.2.251",
"138.199.46.65",
"207.121.41.196",
"207.121.99.20",
"207.121.99.36",
"207.121.99.44",
"207.121.99.52",
"207.121.99.60",
"207.121.23.68",
"207.121.23.124",
"207.121.23.244",
"207.121.23.180",
"207.121.23.188",
"207.121.23.196",
"207.121.23.204",
"207.121.24.52",
"207.121.24.60",
"207.121.24.68",
"207.121.24.76",
"207.121.24.92",
"207.121.24.100",
"207.121.24.108",
"207.121.24.116",
"154.95.86.76",
"5.9.99.73",
"78.46.92.118",
"144.76.65.213",
"78.46.156.89",
"88.198.9.155",
"144.76.79.22",
"103.1.215.93",
"103.137.12.33",
"103.107.196.31",
"116.90.72.155",
"103.137.14.5",
"116.90.75.65",
"37.19.207.37",
"208.83.234.224",
"116.202.155.146",
"116.202.193.178",
"116.202.224.168",
"188.40.126.227",
"88.99.26.189",
"168.119.39.238",
"88.99.26.97",
"168.119.12.188",
"176.9.139.55",
"142.132.223.79",
"142.132.223.80",
"142.132.223.81",
"46.4.116.17",
"46.4.119.81",
"167.235.114.167",
"159.69.68.171",
"178.63.21.52",
"46.4.120.152",
"116.202.80.247",
"5.9.71.119",
"195.201.11.156",
"78.46.123.17",
"46.4.113.143",
"136.243.2.236",
"195.201.81.217",
"148.251.42.123",
"94.130.68.122",
"88.198.22.103",
"46.4.102.90",
"157.90.180.205",
"162.55.135.11",
"195.201.109.59",
"148.251.41.244",
"116.202.235.16",
"128.140.70.141",
"78.46.74.86",
"78.46.74.85",
"178.63.41.242",
"178.63.41.247",
"178.63.41.234",
"104.237.53.74",
"104.237.54.154",
"104.237.51.58",
"64.185.235.90",
"64.185.234.114",
"64.185.232.194",
"64.185.232.178",
"64.185.232.82",
"103.60.15.169",
"103.60.15.170",
"103.60.15.171",
"103.60.15.172",
"103.60.15.173",
"103.60.15.162",
"103.60.15.163",
"103.60.15.164",
"103.60.15.165",
"103.60.15.166",
"103.60.15.167",
"103.60.15.168",
"109.248.43.116",
"109.248.43.117",
"109.248.43.162",
"109.248.43.163",
"109.248.43.164",
"109.248.43.165",
"49.12.71.27",
"49.12.0.158",
"78.47.94.156",
"109.248.43.159",
"109.248.43.160",
"109.248.43.208",
"109.248.43.179",
"109.248.43.232",
"109.248.43.231",
"109.248.43.241",
"109.248.43.236",
"109.248.43.240",
"109.248.43.103",
"116.202.118.194",
"116.202.80.29",
"159.69.57.80",
"139.180.129.216",
"139.99.174.7",
"89.187.169.18",
"89.187.179.7",
"143.244.62.213",
"185.93.3.246",
"195.181.163.198",
"185.152.64.19",
"84.17.37.211",
"212.102.50.54",
"212.102.46.115",
"143.244.38.135",
"169.150.238.21",
"169.150.207.51",
"169.150.207.49",
"84.17.38.226",
"84.17.38.225",
"37.19.222.248",
"37.19.222.249",
"169.150.247.139",
"169.150.247.177",
"169.150.247.178",
"169.150.213.49",
"212.102.46.119",
"84.17.38.234",
"84.17.38.233",
"169.150.247.179",
"169.150.247.180",
"169.150.247.181",
"169.150.247.182",
"169.150.247.183",
"169.150.247.138",
"169.150.247.184",
"169.150.247.185",
"156.146.58.83",
"212.102.43.88",
"89.187.169.26",
"109.61.89.57",
"109.61.89.58",
"109.61.83.241",
"84.17.38.243",
"84.17.38.244",
"84.17.38.246",
"84.17.38.247",
"84.17.38.245",
"143.244.38.129",
"84.17.38.248",
"89.187.176.34",
"185.152.64.23",
"79.127.213.209",
"79.127.213.210",
"84.17.37.209",
"156.146.43.68",
"185.93.3.243",
"79.127.219.198",
"138.199.33.57",
"79.127.242.89",
"138.199.4.136",
"169.150.220.235",
"138.199.4.129",
"138.199.4.177",
"37.19.222.34",
"46.151.193.85",
"212.104.158.17",
"212.104.158.18",
"212.104.158.19",
"212.104.158.20",
"212.104.158.21",
"212.104.158.22",
"212.104.158.24",
"212.104.158.26",
"79.127.237.134",
"89.187.184.177",
"89.187.184.179",
"89.187.184.173",
"89.187.184.178",
"89.187.184.176",
]);
const CDN_PROVIDERS: CDNProvider[] = [
{
name: "cloudflare",
displayName: "Cloudflare",
checkIp: (ip: string) =>
CLOUDFLARE_IP_RANGES.some((range) => isIPInCIDR(ip, range)),
warningMessage:
"Domain is behind Cloudflare - actual IP is masked by Cloudflare proxy",
},
{
name: "bunnycdn",
displayName: "Bunny CDN",
checkIp: (ip: string) => BUNNY_CDN_IPS.has(ip),
warningMessage:
"Domain is behind Bunny CDN - actual IP is masked by CDN proxy",
},
{
name: "fastly",
displayName: "Fastly",
checkIp: (ip: string) =>
FASTLY_IP_RANGES.some((range) => isIPInCIDR(ip, range)),
warningMessage:
"Domain is behind Fastly - actual IP is masked by CDN proxy",
},
];
export const detectCDNProvider = (ip: string): CDNProvider | null => {
return CDN_PROVIDERS.find((provider) => provider.checkIp(ip)) || null;
};

View File

@@ -8,6 +8,7 @@ import { eq } from "drizzle-orm";
import { type apiCreateDomain, domains } from "../db/schema";
import { findUserById } from "./admin";
import { findApplicationById } from "./application";
import { detectCDNProvider } from "./cdn";
import { findServerById } from "./server";
export type Domain = typeof domains.$inferSelect;
@@ -142,28 +143,6 @@ export const getDomainHost = (domain: Domain) => {
const resolveDns = promisify(dns.resolve4);
// Cloudflare IP ranges (simplified - these are some common ones)
const CLOUDFLARE_IPS = [
"172.67.",
"104.21.",
"104.16.",
"104.17.",
"104.18.",
"104.19.",
"104.20.",
"104.22.",
"104.23.",
"104.24.",
"104.25.",
"104.26.",
"104.27.",
"104.28.",
];
const isCloudflareIp = (ip: string) => {
return CLOUDFLARE_IPS.some((range) => ip.startsWith(range));
};
export const validateDomain = async (
domain: string,
expectedIp?: string,
@@ -172,6 +151,7 @@ export const validateDomain = async (
resolvedIp?: string;
error?: string;
isCloudflare?: boolean;
cdnProvider?: string;
}> => {
try {
// Remove protocol and path if present
@@ -182,17 +162,18 @@ export const validateDomain = async (
const resolvedIps = ips.map((ip) => ip.toString());
// Check if it's a Cloudflare IP
const behindCloudflare = ips.some((ip) => isCloudflareIp(ip));
// Check if any IP belongs to a CDN provider
const cdnProvider = ips
.map((ip) => detectCDNProvider(ip))
.find((provider) => provider !== null);
// If behind Cloudflare, we consider it valid but inform the user
if (behindCloudflare) {
// If behind a CDN, we consider it valid but inform the user
if (cdnProvider) {
return {
isValid: true,
resolvedIp: resolvedIps.join(", "),
isCloudflare: true,
error:
"Domain is behind Cloudflare - actual IP is masked by Cloudflare proxy",
cdnProvider: cdnProvider.displayName,
error: cdnProvider.warningMessage,
};
}