diff --git a/apps/dokploy/components/dashboard/application/domains/show-domains.tsx b/apps/dokploy/components/dashboard/application/domains/show-domains.tsx index fe637353..97ac6ac4 100644 --- a/apps/dokploy/components/dashboard/application/domains/show-domains.tsx +++ b/apps/dokploy/components/dashboard/application/domains/show-domains.tsx @@ -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) => { <> {validationState.message - ? "Behind Cloudflare" + ? `Behind ${validationState.cdnProvider}` : "DNS Valid"} ) : validationState?.error ? ( diff --git a/packages/server/src/services/cdn.ts b/packages/server/src/services/cdn.ts new file mode 100644 index 00000000..66549ce9 --- /dev/null +++ b/packages/server/src/services/cdn.ts @@ -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; +}; diff --git a/packages/server/src/services/domain.ts b/packages/server/src/services/domain.ts index edbf3cca..f81fffb9 100644 --- a/packages/server/src/services/domain.ts +++ b/packages/server/src/services/domain.ts @@ -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, }; }