homelab-blueprint/docs/network/allow-caddy-to-apps.md
Brian Pooe fef521909b feat(forgejo): expose publicly at git.brianpooe.com with SSO-free access
Public repos are now browsable/clonable anonymously (HTTPS) and over SSH,
proxied through the Hetzner VPS. Unlike other public apps this is
deliberately not behind the oauth2-proxy/Zitadel gate, since Forgejo
already enforces its own per-repo visibility. Root redirects to the
.profile landing page. Documents the REQUIRE_SIGNIN_VIEW gotcha and the
Zitadel OIDC redirect_uri fix needed whenever FORGEJO_DOMAIN changes.
2026-07-19 18:34:13 +02:00

10 KiB

Caddy Firewall Policies — Missing and Incorrect Rules

Note

For the complete list of custom firewall rules, see firewall-rules.md.

Audit findings from the zone policy review. Apply these in UniFi Settings → Policy Engine → Policy Table in the order shown. All custom policies must be above the built-in Block All Traffic terminal rule.


0. Immediate Cleanup — Redundant and Incorrect Rules

Sort these out before adding anything new.

Delete — completely redundant

Policy Why
Allow CADDY to Gateway DNS The Caddy Pi's resolv.conf points to Technitium at 10.0.10.5 (TRUSTED). Allow CADDY to DNS already covers that. The UDM Gateway DNS forwarder is not in use.

Keep — intra-zone rules that are actually required

UniFi's documentation implies intra-zone traffic is allowed by default, but in practice this does not apply to custom zones — removing these breaks intra-zone traffic.

Policy Why to keep
Allow TRUSTED to TRUSTED Any Without this rule, devices within TRUSTED cannot reach each other (confirmed: removing it broke SSH to Technitium).
Allow IoT to IoT Any Same — IoT intra-zone traffic requires an explicit allow for custom zones.

Fix — wrong action

Policy Problem Fix
Allow FAMILY to CADDY Action is set to Block instead of Allow. FAMILY devices cannot reach Caddy at all. Edit the policy and change Action to Allow.

Review — direct pinholes that bypass Caddy

These work but contradict the network design (§7.3/7.4), which says MEDIA and FAMILY should only reach internal services through Caddy. Once the ALLOW CADDY to arr stack backend policy (§2 below) is in place, both zones can reach Emby via emby.yourdomain.com through Caddy instead.

Policy Impact of keeping Impact of removing
Allow MEDIA to Emby Apple TV can connect directly to LXC — wider attack surface, bypasses Caddy logging and auth. Apple TV must use emby.yourdomain.com via Caddy. Requires Caddy backend policy in place first.
Allow FAMILY to EMBY Family devices bypass Caddy for Emby. Family devices must use emby.yourdomain.com via Caddy. Requires Caddy backend policy in place first.

Remove these only after the ALLOW CADDY to arr stack policy is created and confirmed working.


1. Fix: Allow FAMILY to CADDY (currently set to Block)

The existing Allow FAMILY to CADDY policy has Action = Block. Edit it and change the action to Allow.

Field Value
Name Allow FAMILY to CADDY
Action Allow (currently wrong — set to Block)
Source zone FAMILY
Source IP Any
Destination zone CADDY
Destination IP Any
Destination port 443
Protocol TCP

2. CADDY → TRUSTED Backend App Policies

Caddy at 10.0.15.5 cannot proxy to any backend apps in the TRUSTED zone until these policies exist. Create one policy per row.

Every policy in this table uses:

  • Action: Allow
  • Source zone: CADDY
  • Source IP: 10.0.15.5
  • Destination zone: TRUSTED
  • Protocol: TCP
Policy name Destination IP Destination port(s) Purpose
ALLOW CADDY to arr stack 10.0.10.20 5055,6767,7878,8000,8080,8096,8191,8282,8989,9696 Arr-stack LXC (bazarr, radarr, gluetun, qbittorrent, emby, flaresolverr, sabnzbd, sonarr, prowlarr, seerr)
ALLOW CADDY to Technitium UI 10.0.10.5 5380 Technitium admin UI
ALLOW CADDY to Proxmox 10.0.10.10 8006 Proxmox VE
ALLOW CADDY to PBS 10.0.10.8 8007 Proxmox Backup Server
ALLOW CADDY to LAN web devices 10.0.1.6 80 Lite-8 PoE switch UI
ALLOW CADDY to Beszel 10.0.10.13 8090 Beszel monitoring
ALLOW CADDY to Gramps 10.0.10.17 80 Gramps Web
ALLOW CADDY to Immich 10.0.10.15 2283 Immich
ALLOW CADDY to IT Tools 10.0.10.7 80 IT Tools
ALLOW CADDY to Paperless 10.0.10.11 8000 Paperless-ngx
ALLOW CADDY to YTD 10.0.10.14 3087 Youtarr
ALLOW CADDY to Bento PDF 10.0.10.6 8080 Bento PDF
ALLOW CADDY to Koffan 10.0.10.12 8080 Koffan (LXC 111)
ALLOW CADDY to Hermes 10.0.10.19 9119 Hermes Agent dashboard (LXC 113)
ALLOW CADDY to Forgejo 10.0.10.18 3000,22 Forgejo web UI (LXC 114) and git-over-SSH for hosts pulling from raspberrypi's zone

3. CADDY → IoT Backend Policies

Caddy cannot proxy to Home Assistant or Zigbee until these exist.

Every policy in this table uses:

  • Action: Allow
  • Source zone: CADDY
  • Source IP: 10.0.15.5
  • Destination zone: IoT
  • Protocol: TCP
Policy name Destination IP Destination port(s) Purpose
ALLOW CADDY to Home Automation 10.0.30.6 8080,8123 Zigbee2MQTT and Home Assistant
ALLOW CADDY to Zigbee 10.0.30.5 80 SLZB-06U Zigbee coordinator UI

3a. IoT → CADDY Policies (reverse direction)

The Home Assistant host itself also needs to reach Caddy directly — it pulls homelab-blueprint from Forgejo (git.brianpooe.com, proxied through Caddy) to deploy its own docker-compose stack.

Two independent layers enforce traffic into the Caddy host — both must allow it, and both were found misconfigured while wiring this up:

  1. UniFi zone-based firewall (router-level, VLAN → VLAN):

    Policy name Source zone Source IP Src. Port Destination zone Destination IP Destination port(s) Purpose
    Allow HA to Caddy IoT 10.0.30.6 (Home Assistant IP) Any CADDY 10.0.15.5 (Caddy IP) 443 git fetch/pull of homelab-blueprint from Forgejo

    Src. Port must be Any, not restricted to HA's own listening ports (8080/8123) — this is outbound traffic from HA, so the source port is a random ephemeral port, not the port HA listens on. Setting Src. Port to a "Home Assistant" port object here was the original bug: the rule never matched, so traffic fell through to Block All Traffic below it.

  2. ufw on the Caddy host itself (10.0.15.5) — a second, separate enforcement point the UniFi rule doesn't touch. It allow-lists inbound 80/443 per source subnet and did not include the IoT VLAN (10.0.30.0/24):

    sudo ufw allow from 10.0.30.0/24 to any port 80,443 proto tcp
    

3b. CADDY / IoT → Forgejo git-over-SSH (port 22)

Both raspberrypi (CADDY zone) and Home Assistant (IoT zone) clone homelab-blueprint over SSH directly against the Forgejo LXC (10.0.10.18:22), bypassing Caddy entirely — SSH can't be proxied through Caddy (HTTP/S only), so this traffic goes straight to the TRUSTED zone. 10.0.10.18 has no host-level firewall (ufw not installed, iptables INPUT policy ACCEPT), so only the UniFi zone rule matters here — no second enforcement layer to fight, unlike the Caddy host above.

Forgejo's git-SSH runs on the standard port 22 on this LXC, not the usual 2222 (see docker-compose-files/forgejo/README.md) — the LXC's own admin sshd was relocated to port 2200 (systemd ssh.socket override) to free 22 up. Direct admin SSH to this LXC now needs -p 2200; pct exec 114 from the Proxmox host is unaffected and still works normally.

Policy name Source zone Source IP Destination zone Destination IP Destination port(s) Purpose
ALLOW CADDY to Forgejo (existing rule, port added) CADDY 10.0.15.5 TRUSTED 10.0.10.18 3000,22 Forgejo web UI + git-over-SSH from raspberrypi
Allow HA to Forgejo SSH IoT 10.0.30.6 TRUSTED 10.0.10.18 22 git-over-SSH from homeassistant

ufw status on that host is the fastest way to check which subnets can already reach it before assuming a UniFi rule is the problem.

3c. Public Exposure of Forgejo (Hetzner VPS)

Forgejo is also reachable from the public internet, at git.brianpooe.com (HTTPS, public repos only — anonymous browsing/cloning, like GitHub) and ssh://git@forgejo.brianpooe.com (SSH, requires a registered account key — self-registration is disabled, so this is effectively you-only). Full detail lives in docker-compose-files/forgejo/README.md ("Public Exposure") and, on the VPS itself, ~/netbird-vps/docs/netbird-acl-playbook.md and public-app-sso.md. Summary of what changed on the LAN side of that path:

  • DNS is split-horizon, not a new firewall rule: git.brianpooe.com and forgejo.brianpooe.com are each their own single-name Technitium zone (A record direct to 10.0.15.5 and 10.0.10.18 respectively) so LAN clients resolve them locally while the public *.brianpooe.com wildcard sends everyone else to the VPS. No UniFi policy changes were needed for the HTTPS path — it already flows through the existing ALLOW CADDY to Forgejo rule above.
  • The public SSH path is a new NetBird route, not a UniFi rule: the VPS NetBird peer previously only routed to the Pi Caddy (10.0.15.5/32). A second 10.0.10.18/32 resource (routing peer pi-caddy, "Model 2" in the playbook) was added so the VPS can reach Forgejo's git-SSH directly. The VPS forwards its own (now-freed) public port 22 to 10.0.10.18:22 via a socat container (~/netbird-vps/ssh-forward/) — the VPS's own admin sshd was relocated to port 2200 to make room.

4. Public DNS Block Policies (per zone)

Without these, devices can bypass Technitium by querying public resolvers (8.8.8.8, 1.1.1.1, etc.) directly. Place each policy above the broad External allow for its zone.

Every policy in this table uses:

  • Action: Block
  • Source IP: Any
  • Destination zone: External
  • Destination IP: Any
  • Protocol: TCP/UDP
  • Destination port: 53
Policy name Source zone
BLOCK FAMILY public DNS FAMILY
BLOCK MEDIA public DNS MEDIA
BLOCK IoT public DNS IoT
BLOCK GUEST public DNS GUEST

Policy Order Reference

Within each zone's rule set, the evaluation order must be:

specific allows (DNS, DHCP, Caddy, app pinholes)
public DNS block (above the External allow)
External allow
Gateway DHCP allow
Gateway block
Block All Traffic  ← built-in terminal rule

If a broad block rule sits above a specific allow, the block wins and the intended traffic is dropped. Use Reorder in UniFi to verify positions after adding new policies.