networking
guide
networking · fortinet · guide

FortiGate im Homelab —
hinter Mesh-Router & Double-NAT

Die meisten FortiGate-Guides gehen davon aus, dass die Firewall direkt am Modem hängt. In der Praxis sieht es oft anders aus: ISP-Router → Mesh-System → FortiGate, mit Double-NAT, eingeschränktem WAN-Zugriff und einem Pi-hole der DNS-Anfragen abfangen soll. Dieser Guide beschreibt genau diese Situation — inklusive aller Fallstricke die dabei aufgetaucht sind.

FortiGate 30E FortiOS 6.4 Pi-hole Nginx Reverse Proxy ⚠ Double-NAT ⚠ wan ≠ wan1
01 Topologie 02 Interfaces 03 DHCP 04 VIPs 05 Firewall Policies 06 DNS-Fluss 07 Nginx & Dienste 08 Fallstricke
01 — Topologie

Die Ausgangssituation

Das Setup entstand aus einer typischen Heimnetz-Realität: Der ISP-Router (DSL-Modem mit integriertem Router) war bereits vorhanden und sollte nicht ersetzt werden. Ein Mesh-System übernimmt WLAN und AP-Funktion im Haus. Die FortiGate kommt dahinter — als echte Firewall für den Server-Bereich.

Das Ergebnis ist eine dreistufige NAT-Kette mit zwei verschiedenen Subnetzen, die voneinander getrennt sind. Geräte im Mesh-Netz können Server im FortiGate-LAN nicht direkt erreichen — das ist gleichzeitig ein Feature und die größte Quelle für Verwirrung beim Setup.

Internet
    │  DSL / PPPoE
    ▼
ISP-Router          NAT 1 · DHCP 192.168.178.0/24
    │  Kabel
    ▼
Mesh-Master         Subnetz 192.168.68.0/22 · DHCP-Server aktiv
    │  WLAN-Backhaul
    ▼
Mesh-Node (Büro)    kein eigenes Subnetz · nur AP
    │  Kabel
    ▼
FortiGate 30E       WAN: 203.0.113.10 (DHCP vom Mesh)
                    LAN: 10.0.1.99/24
    │  Kabel · LAN-Port
    ▼
Unmanaged Switch
    ├── Server1  10.0.1.71  Always-on · Nginx · Pi-hole
    ├── Server2  10.0.1.82  On-demand · Nextcloud · Paperless
    └── Server3  10.0.1.83  Always-on · Joplin · AliasVault
        
⚠ Double-NAT

Der ISP-Router und der Mesh-Master betreiben beide NAT. Die FortiGate bekommt daher keine öffentliche IP auf dem WAN-Interface, sondern eine private aus dem Mesh-Netz. Das schränkt manche VPN-Szenarien ein, ist für den internen Homelab-Betrieb aber problemlos handhabbar.

Warum Mesh im AP-Mode?

Das Mesh-System läuft nicht als reiner AP — es betreibt weiterhin seinen eigenen DHCP und sein eigenes Subnetz (192.168.68.0/22). Smartphones, Laptops und Smart-Home-Geräte hängen im Mesh-Netz. Die FortiGate ist nur für den Server-Bereich zuständig. Die Trennung ist bewusst: Server-Traffic geht durch die Firewall, Consumer-Traffic nicht.

02 — Interfaces

WAN & LAN konfigurieren

Der FortiGate 30E hat zwei relevante Interfaces: wan (Uplink zum Mesh) und lan (Hard-Switch, alle LAN-Ports gebridged). Das WAN-Interface bezieht seine IP per DHCP vom Mesh-Master.

Kritischer Fallstrick — Interface heißt "wan" nicht "wan1"

Beim FortiGate 30E heißt das WAN-Interface wan — nicht wan1 wie bei anderen FortiGate-Modellen. In VIP-Konfigurationen muss extintf "wan" stehen. Mit "wan1" greift die VIP nicht und der Traffic kommt nie an. Klingt trivial, kostet aber leicht eine Stunde Debugging.

FortiOS CLI Interface-Konfiguration
# WAN Interface — DHCP vom Mesh-Router
config system interface
    edit "wan"
        set mode dhcp
        set allowaccess ping https
        set role wan
    next

# LAN Interface — statisch, Gateway für alle Server
    edit "lan"
        set ip 10.0.1.99 255.255.255.0
        set allowaccess ping https ssh
        set role lan
    next
end
InterfaceIPModusAllowaccess
wan 203.0.113.10 (DHCP) dhcp ping, https
lan 10.0.1.99/24 statisch ping, https, ssh
03 — DHCP

DHCP-Server & Reservierungen

Die FortiGate verteilt IP-Adressen im LAN-Segment. Server bekommen feste Reservierungen per MAC — so bleiben die IPs stabil ohne statische Konfiguration auf jedem Server. Als primärer DNS wird die FortiGate selbst eingetragen, die dann per VIP an Pi-hole weiterleitet.

FortiOS CLI DHCP Server + Reservierungen
config system dhcp server
    edit 1
        set interface "lan"
        set default-gateway 10.0.1.99
        set netmask 255.255.255.0
        set dns-server1 10.0.1.99   # → Pi-hole via VIP
        set dns-server2 9.9.9.9
        set ip-range
            start-ip 10.0.1.10
            end-ip   10.0.1.210

        # DHCP Reservierungen
        config reserved-address
            edit 1
                set mac aa:bb:cc:dd:ee:01
                set ip  10.0.1.71
                set description "Server1"
            next
            edit 2
                set mac aa:bb:cc:dd:ee:02
                set ip  10.0.1.83
                set description "Server3"
            next
        end
    next
end
DNS-Server auf FortiGate selbst zeigen lassen

Als DNS-Server wird 10.0.1.99 eingetragen — also die FortiGate selbst. Per VIP leitet sie DNS-Anfragen (Port 53) intern an Pi-hole weiter. So muss Pi-hole nicht direkt als DNS-Server in jedem Client eingetragen werden, und es gibt einen zentralen Punkt für DNS-Änderungen.

04 — Virtual IPs

Port-Forwarding via VIPs

Da Geräte im Mesh-Netz (192.168.68.0/22) Server im FortiGate-LAN (10.0.1.0/24) nicht direkt erreichen können, laufen alle Zugriffe über die FortiGate WAN-IP. Virtual IPs (VIPs) leiten eingehende Verbindungen auf bestimmten Ports an interne Dienste weiter.

Der wichtigste VIP-Trick dabei: alle lokalen Domains im Pi-hole zeigen auf die FortiGate WAN-IP — nicht auf die Server direkt. Nginx auf Server1 nimmt dann den HTTPS-Traffic entgegen und verteilt ihn intern.

VIP-NameExt. PortInterne IPInt. PortDienst
VIP-PiHole-DNS53 UDP10.0.1.7153Pi-hole DNS
VIP-PiHole-Web8888 TCP10.0.1.718888Pi-hole Web-UI
VIP-HTTP80 TCP10.0.1.7180Nginx → HTTPS-Redirect
VIP-HTTPS443 TCP10.0.1.71443Nginx → alle .local-Domains
VIP-SSH22 TCP10.0.1.7122SSH Server1
VIP-SSH-S22222 TCP10.0.1.8222SSH Server2
VIP-Joplin22300 TCP10.0.1.8322300Joplin Sync (HTTP)
FortiOS CLI Beispiel: VIP-HTTPS anlegen
config firewall vip
    edit "VIP-HTTPS"
        set extip      203.0.113.10   # FortiGate WAN-IP
        set extintf    "wan"          # ← NICHT "wan1" !
        set portforward enable
        set protocol   tcp
        set extport    443
        set mappedip   10.0.1.71      # Nginx auf Server1
        set mappedport 443
    next
end
VIP-HTTP nicht vergessen

Ohne VIP-HTTP (Port 80) landen Nutzer die eine URL ohne https:// eintippen auf dem FortiGate-Management-Interface statt auf Nginx. Nginx kann dann den Redirect auf HTTPS machen — aber nur wenn Port 80 auch ankommt.

05 — Firewall Policies

Traffic erlauben

VIPs alleine leiten noch keinen Traffic. Erst die passende Firewall Policy erlaubt eingehende Verbindungen. Wichtig: NAT muss bei eingehenden VIP-Policies deaktiviert sein, sonst sieht der Ziel-Server die FortiGate als Absender statt den echten Client.

PolicyVonNachZiel (VIP)NAT
LAN-to-WANlanwanallACCEPT + NAT
HTTPS-to-NginxwanlanVIP-HTTPSaus
HTTP-to-NginxwanlanVIP-HTTPaus
DNS-to-PiholewanlanVIP-PiHole-DNSaus
SSH-Server1wanlanVIP-SSHaus
Joplin-SyncwanlanVIP-Joplinaus
FortiOS CLI Policy: HTTPS zu Nginx
config firewall policy
    edit 0
        set name      "HTTPS-to-Nginx"
        set srcintf   "wan"
        set dstintf   "lan"
        set srcaddr   "all"
        set dstaddr   "VIP-HTTPS"
        set action    accept
        set service   "HTTPS"
        set nat       disable   # ← wichtig!
    next
end
06 — DNS-Fluss

Pi-hole hinter der FortiGate

Pi-hole läuft auf Server1 im FortiGate-LAN — aber Geräte im Mesh-Netz sollen es trotzdem als DNS-Server nutzen. Die Lösung: DHCP im Mesh-System (oder per FortiGate für LAN-Geräte) trägt die FortiGate WAN-IP als DNS-Server ein. Die FortiGate leitet per VIP-PiHole-DNS alle DNS-Anfragen an Port 53 auf Server1 weiter.

Gerät im Mesh-Netz
    │  DNS-Anfrage an 203.0.113.10:53FortiGate WAN
    │  VIP-PiHole-DNS → 10.0.1.71:53Pi-hole (10.0.1.71)
    │  filtert · löst lokale Domains auf · upstream: 9.9.9.9
    ▼
Antwort: lokale .local-Domain → 203.0.113.10 (FortiGate WAN)
    │  FortiGate leitet via VIP-HTTPS → Nginx → Dienst
        
Häufigster Fehler: DNS zeigt auf Server-IP statt FortiGate WAN

Pi-hole-Einträge für lokale Domains müssen auf die FortiGate WAN-IP zeigen, nicht auf die Server direkt (10.0.1.71). Geräte im Mesh-Netz können 10.0.1.x nicht routen — die Anfrage läuft ins Leere. Erst über die FortiGate WAN-IP mit VIP-HTTPS kommt der Traffic ans Ziel.

Pi-hole DNS RecordZeigt aufWarum
cloud.local203.0.113.10FortiGate WAN → VIP-HTTPS → Nginx
paperless.local203.0.113.10FortiGate WAN → VIP-HTTPS → Nginx
joplin.local203.0.113.10FortiGate WAN → VIP-HTTPS → Nginx
pihole.local203.0.113.10FortiGate WAN → VIP-HTTPS → Nginx
… alle .local203.0.113.10Nginx-Vhost entscheidet den Ziel-Dienst
07 — Nginx & Dienste

Reverse Proxy mit mkcert

Nginx läuft als Docker-Container auf Server1 und ist der zentrale Eintrittspunkt für alle HTTPS-Dienste. Ein einziges SAN-Zertifikat (erstellt mit mkcert) deckt alle lokalen Domains ab. Nginx entscheidet per server_name welcher Dienst angesprochen wird — auch wenn der Dienst auf einem anderen Server läuft.

nginx.conf Beispiel Server-Block — Dienst auf anderem Server
server {
    listen 443 ssl;
    server_name joplin.local;

    ssl_certificate     /etc/nginx/certs/homelab.pem;
    ssl_certificate_key /etc/nginx/certs/homelab-key.pem;

    location / {
        proxy_pass         http://10.0.1.83:22300;  # Server3
        proxy_http_version 1.1;
        proxy_set_header   Host              $host;
        proxy_set_header   X-Real-IP         $remote_addr;
        proxy_set_header   X-Forwarded-For   $proxy_add_x_forwarded_for;
        proxy_set_header   X-Forwarded-Proto $scheme;
        proxy_set_header   Upgrade           $http_upgrade;
        proxy_set_header   Connection        "upgrade";
    }
}
Port-Binding in Docker beachten

Dienste die Nginx cross-server proxyen soll, dürfen nicht auf 127.0.0.1:port binden. Das blockiert Zugriffe von außerhalb des Containers. Stattdessen auf die Server-IP binden: 10.0.1.83:22300:22300 in der Docker Compose ports-Sektion.

mkcert SAN-Zertifikat für alle Domains

Ein einziges Zertifikat für alle .local-Domains spart Verwaltungsaufwand. Die mkcert-Root-CA muss auf jedem Gerät installiert werden das auf die lokalen Domains zugreifen soll — unter Android unter Einstellungen → Sicherheit → CA-Zertifikat installieren.

08 — Fallstricke

Was schiefgehen kann — und wie man es fixt

VIP greift nicht, Traffic kommt nie an
Ursache fast immer: extintf "wan1" statt extintf "wan". Beim FortiGate 30E heißt das Interface wan. Mit dem falschen Namen wird die VIP still ignoriert. Im CLI prüfen mit show firewall vip und den extintf-Wert checken.
Lokale Domain nicht erreichbar, kein Zertifikatsfehler
Pi-hole DNS-Eintrag zeigt auf die Server-IP (10.0.1.71) statt auf die FortiGate WAN-IP. Geräte im Mesh-Netz können das Server-Subnetz nicht routen. Fix: alle lokalen DNS-Einträge in Pi-hole auf <FORTIGATE-WAN-IP> setzen.
Nginx-Config-Test schlägt fehl: "host not found in upstream"
Beim Config-Test ohne laufenden Container wird host.docker.internal nicht aufgelöst. Fix: in allen Nginx-Configs direkt die Server-IP verwenden statt Hostnamen. Schnell per sed -i 's/host.docker.internal/10.0.1.71/g' conf.d/*.conf erledigt.
Docker-Dienst startet, aber Nginx kann ihn nicht erreichen
Port-Binding auf 127.0.0.1:port:port statt auf die Server-IP. Nginx läuft im eigenen Container und kann localhost des Host-Systems nicht erreichen. In der Docker Compose auf die echte Server-IP binden: 10.0.1.83:8300:80.
Ohne https:// landet man auf dem FortiGate-Login
VIP-HTTP (Port 80) fehlt oder die zugehörige Firewall Policy ist nicht angelegt. Nginx kann den HTTP→HTTPS-Redirect nur machen wenn Port 80 überhaupt ankommt. VIP + Policy für Port 80 anlegen, Nginx 00-redirect.conf macht den Rest.
VPN am PC aktiv — FortiGate Flow Trace zeigt nichts
Wenn ein VPN-Client auf dem eigenen Rechner aktiv ist, geht DNS-Traffic oft über den VPN-Tunnel statt lokal. Pi-hole sieht die Anfragen nicht, der Flow Trace auf der FortiGate zeigt nichts. VPN deaktivieren und erneut testen.
proxy-Netzwerk fehlt nach docker system prune
docker system prune löscht ungenutzte Netzwerke mit. Das externe proxy-Netzwerk das Nginx mit anderen Containern verbindet muss danach neu angelegt werden: docker network create proxy. Sonst startet Nginx nicht.