From 80177bf06777ad8a24bfc6a35b82918b9425e105 Mon Sep 17 00:00:00 2001 From: fuomag9 <1580624+fuomag9@users.noreply.github.com> Date: Mon, 23 Feb 2026 00:53:53 +0100 Subject: [PATCH] feat: inject blocker handler into proxy routes for geoblocking Co-Authored-By: Claude Sonnet 4.6 --- src/lib/caddy.ts | 110 +++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 106 insertions(+), 4 deletions(-) diff --git a/src/lib/caddy.ts b/src/lib/caddy.ts index 92f60bfc..811f645f 100644 --- a/src/lib/caddy.ts +++ b/src/lib/caddy.ts @@ -12,10 +12,12 @@ import { getLoggingSettings, getDnsSettings, getUpstreamDnsResolutionSettings, + getGeoBlockSettings, setSetting, type DnsSettings, type UpstreamDnsAddressFamily, - type UpstreamDnsResolutionSettings + type UpstreamDnsResolutionSettings, + type GeoBlockSettings } from "./settings"; import { syncInstances } from "./instance-sync"; import { @@ -23,6 +25,7 @@ import { certificates, proxyHosts } from "./db/schema"; +import { type GeoBlockMode } from "./models/proxy-hosts"; const CERTS_DIR = process.env.CERTS_DIRECTORY || join(process.cwd(), "data", "certs"); mkdirSync(CERTS_DIR, { recursive: true, mode: 0o700 }); @@ -80,6 +83,8 @@ type ProxyHostMeta = { load_balancer?: LoadBalancerMeta; dns_resolver?: DnsResolverMeta; upstream_dns_resolution?: UpstreamDnsResolutionMeta; + geoblock?: GeoBlockSettings; + geoblock_mode?: GeoBlockMode; }; type ProxyHostAuthentikMeta = { @@ -705,9 +710,96 @@ function collectCertificateUsage(rows: ProxyHostRow[], certificates: Map { + const handler: Record = { + handler: "blocker", + geoip_db: "/usr/share/GeoIP/GeoLite2-Country.mmdb", + asn_db: "/usr/share/GeoIP/GeoLite2-ASN.mmdb", + }; + + if (config.block_countries?.length) handler.block_countries = config.block_countries; + if (config.block_continents?.length) handler.block_continents = config.block_continents; + if (config.block_asns?.length) handler.block_asns = config.block_asns; + if (config.block_cidrs?.length) handler.block_cidrs = config.block_cidrs; + if (config.block_ips?.length) handler.block_ips = config.block_ips; + + if (config.allow_countries?.length) handler.allow_countries = config.allow_countries; + if (config.allow_continents?.length) handler.allow_continents = config.allow_continents; + if (config.allow_asns?.length) handler.allow_asns = config.allow_asns; + if (config.allow_cidrs?.length) handler.allow_cidrs = config.allow_cidrs; + if (config.allow_ips?.length) handler.allow_ips = config.allow_ips; + + if (config.trusted_proxies?.length) handler.trusted_proxies = config.trusted_proxies; + + if (config.redirect_url) { + handler.redirect_url = config.redirect_url; + } else { + if (config.response_status) handler.response_status = config.response_status; + if (config.response_body) handler.response_body = config.response_body; + if (config.response_headers && Object.keys(config.response_headers).length) { + handler.response_headers = config.response_headers; + } + } + + return handler; +} + type BuildProxyRoutesOptions = { globalDnsSettings: DnsSettings | null; globalUpstreamDnsResolutionSettings: UpstreamDnsResolutionSettings | null; + globalGeoBlock?: GeoBlockSettings | null; }; async function buildProxyRoutes( @@ -747,6 +839,14 @@ async function buildProxyRoutes( const authentik = parseAuthentikConfig(meta.authentik); const hostRoutes: CaddyHttpRoute[] = []; + const effectiveGeoBlock = resolveEffectiveGeoBlock( + options.globalGeoBlock ?? null, + { geoblock: meta.geoblock ?? null, geoblock_mode: meta.geoblock_mode ?? "merge" } + ); + if (effectiveGeoBlock) { + handlers.unshift(buildBlockerHandler(effectiveGeoBlock)); + } + if (row.hsts_enabled) { const value = row.hsts_subdomains ? "max-age=63072000; includeSubDomains" : "max-age=63072000"; handlers.push({ @@ -1381,10 +1481,11 @@ async function buildCaddyDocument() { }, new Map()); const { usage: certificateUsage, autoManagedDomains } = collectCertificateUsage(proxyHostRows, certificateMap); - const [generalSettings, dnsSettings, upstreamDnsResolutionSettings] = await Promise.all([ + const [generalSettings, dnsSettings, upstreamDnsResolutionSettings, globalGeoBlock] = await Promise.all([ getGeneralSettings(), getDnsSettings(), - getUpstreamDnsResolutionSettings() + getUpstreamDnsResolutionSettings(), + getGeoBlockSettings() ]); const { tlsApp, managedCertificateIds } = await buildTlsAutomation(certificateUsage, autoManagedDomains, { acmeEmail: generalSettings?.acmeEmail, @@ -1402,7 +1503,8 @@ async function buildCaddyDocument() { readyCertificates, { globalDnsSettings: dnsSettings, - globalUpstreamDnsResolutionSettings: upstreamDnsResolutionSettings + globalUpstreamDnsResolutionSettings: upstreamDnsResolutionSettings, + globalGeoBlock } );