diff --git a/docs/architecture/networking.md b/docs/architecture/networking.md index 7b8935e5..e9fac547 100644 --- a/docs/architecture/networking.md +++ b/docs/architecture/networking.md @@ -180,11 +180,12 @@ VMs tag traffic on vmbr1 to isolate workloads. pfSense bridges VLAN 20 to the up **Split Horizon / Hairpin NAT fix (192.168.1.0/24 → *.viktorbarzin.me)**: - TP-Link router does NOT support hairpin NAT — LAN clients can't reach the public IP (176.12.22.76) for non-proxied domains -- Technitium's Split Horizon `AddressTranslation` post-processor translates `176.12.22.76 → 10.0.20.200` (Traefik LB) in DNS responses for 192.168.1.0/24 clients +- Technitium's Split Horizon `AddressTranslation` post-processor translates `176.12.22.76 → 10.0.20.203` (Traefik LB) in DNS responses for 192.168.1.0/24 clients (was `.200` until 2026-05-30 Traefik dedicated-IP move) - DNS Rebinding Protection has `viktorbarzin.me` in `privateDomains` to allow the translated private IP - Only affects non-proxied domains (ha-sofia, immich, headscale, etc.) — Cloudflare-proxied domains resolve to Cloudflare IPs and are unaffected - Other clients (10.0.x.x, K8s pods) are NOT translated — they reach the public IP via pfSense outbound NAT - Config synced to all 3 Technitium instances by CronJob `technitium-split-horizon-sync` (every 6h) +- **Mail port carve-out**: the translation sends `mail.viktorbarzin.me` (and `imap.`/`smtp.`) to `.203` too, but Traefik does not serve mail ports. A pfSense NAT rdr rule redirects `10.0.20.203:{25,465,587,993}` → `10.0.20.1` (mail HAProxy) on any incoming interface, so LAN mail clients land on the right service unchanged. Script: `scripts/pfsense-nat-mail-lan-redirect.php` **K8s cluster DNS path**: - CoreDNS forwards `.viktorbarzin.lan` to Technitium ClusterIP (10.96.0.53) diff --git a/scripts/pfsense-nat-mail-lan-redirect.php b/scripts/pfsense-nat-mail-lan-redirect.php new file mode 100644 index 00000000..265b2c17 --- /dev/null +++ b/scripts/pfsense-nat-mail-lan-redirect.php @@ -0,0 +1,90 @@ + 10.0.20.1 +// (pfSense's mail HAProxy listener). But 192.168.1.x clients send to +// 10.0.20.203, not the WAN IP, so those rules don't match. +// +// This script adds 4 NAT rules that match dst=10.0.20.203 on mail ports +// and redirect them to 10.0.20.1 — same target as the public-Internet path. +// Roundcube traffic to :443 stays on Traefik (.203) untouched. +// +// USAGE (on pfSense host, via SSH as admin) +// scp infra/scripts/pfsense-nat-mail-lan-redirect.php admin@10.0.20.1:/tmp/ +// ssh admin@10.0.20.1 'php /tmp/pfsense-nat-mail-lan-redirect.php' +// +// IDEMPOTENT — removes prior copies of our rules (by descr prefix) before +// re-adding. Safe to re-run. + +require_once('/etc/inc/config.inc'); +require_once('/etc/inc/filter.inc'); + +global $config; +parse_config(true); + +$TRAEFIK_LB = '10.0.20.203'; +$MAIL_HAPROXY = '10.0.20.1'; +$DESCR_PREFIX = 'mail-lan-redirect-'; + +// One rule per port; protocols match the existing WAN-IP rules so we +// behave identically once the dst is rewritten. +$PORTS = [ + ['25', 'tcp', 'mail-lan-redirect-25 (SMTP)'], + ['465', 'tcp/udp', 'mail-lan-redirect-465 (SMTPS)'], + ['587', 'tcp', 'mail-lan-redirect-587 (submission)'], + ['993', 'tcp/udp', 'mail-lan-redirect-993 (IMAPS)'], +]; + +// Strip any prior copies we added (descr starts with our prefix). +$kept = []; +$removed = 0; +foreach (($config['nat']['rule'] ?? []) as $r) { + if (strpos($r['descr'] ?? '', $DESCR_PREFIX) === 0) { + $removed++; + continue; + } + $kept[] = $r; +} +printf("Removed %d prior copies\n", $removed); + +// Append new rules. +foreach ($PORTS as [$port, $proto, $descr]) { + $kept[] = [ + 'source' => ['any' => ''], + 'destination' => [ + 'address' => $TRAEFIK_LB, + 'port' => $port, + ], + 'ipprotocol' => 'inet', + 'protocol' => $proto, + 'target' => $MAIL_HAPROXY, + 'local-port' => $port, + 'interface' => 'wan', + 'descr' => $descr, + 'associated-rule-id'=> 'pass', + 'created' => [ + 'time' => (string)time(), + 'username' => 'pfsense-nat-mail-lan-redirect.php', + ], + 'updated' => [ + 'time' => (string)time(), + 'username' => 'pfsense-nat-mail-lan-redirect.php', + ], + ]; + printf("Added: %s (%s %s:%s -> %s:%s)\n", $descr, $proto, $TRAEFIK_LB, $port, $MAIL_HAPROXY, $port); +} + +$config['nat']['rule'] = $kept; + +write_config('NAT: LAN-side mail redirects — 10.0.20.203:{25,465,587,993} -> 10.0.20.1 for Barzini WiFi clients'); + +$rc = filter_configure(); +printf("filter_configure rc=%s\n", var_export($rc, true)); +echo "Done.\n";