diff --git a/src/pages/manage/networks/use-cases/site-to-vpn.mdx b/src/pages/manage/networks/use-cases/site-to-vpn.mdx index 6eb41ab1..d05c39d9 100644 --- a/src/pages/manage/networks/use-cases/site-to-vpn.mdx +++ b/src/pages/manage/networks/use-cases/site-to-vpn.mdx @@ -24,19 +24,19 @@ Clientless Device ──► Routing Peer ──► NetBird Overlay ──► Net The routing peer must perform **outbound source NAT** for site traffic -entering the NetBird overlay. NetBird performs this automatically only when -the routing peer is **Linux running in kernel mode**. On any other platform -(pfSense, OPNsense, MikroTik, Windows, macOS, or Linux in userspace mode), -you must configure the outbound NAT yourself on the routing peer or on its -upstream firewall. Without this, the overlay peer drops the traffic at its -access control. See [Outbound SNAT requirement](#outbound-snat-requirement). +entering the NetBird overlay. The dashboard **Masquerade** flag does not +cover this direction on any route — you must install the SNAT rule +manually on the routing peer (or on its upstream firewall) on every +platform. Without this, the overlay peer drops the traffic at its access +control. ## Prerequisites -- A device on the local network to serve as the routing peer. **Linux is - strongly recommended** for the routing peer because it can install the - required outbound SNAT automatically (see the warning above). +- A device on the local network to serve as the routing peer. Linux is + assumed throughout this guide; on other platforms the steps are the + same but the [Step 3](#step-3-configure-the-outbound-snat) SNAT rule + has to be installed via the platform's native NAT mechanism. - A separate device running the NetBird client that the clientless device needs to reach - The ability to either add a static route on the clientless device (or its @@ -83,43 +83,35 @@ sudo netbird up --setup-key YOUR_SITE_SETUP_KEY Confirm the peer appears in the dashboard and shows the `site-routing-peers` group. -## Step 3: Configure the Outbound SNAT (If applicable) +## Step 3: Configure the Outbound SNAT The routing peer must SNAT site traffic onto its NetBird interface so the -overlay peer's access control sees a NetBird IP it recognises. See -[Outbound SNAT requirement](#outbound-snat-requirement) for the reasoning. +overlay peer's access control sees a NetBird IP it recognises — the +overlay peer's per-policy ipset only contains the NetBird IPs of source +peers, so unrewritten packets sourced from a routed CIDR are dropped. -**On Linux:** no manual SNAT configuration is needed. NetBird enables IP -forwarding and installs the SNAT itself when masquerade is enabled on the -routing peer (Step 4). +This step is **required on every routing peer**, regardless of OS or +WireGuard mode. The dashboard **Masquerade** flag (Step 4) does not +install a SNAT for the Site-to-VPN direction. -The only Linux-side caveat is if you run a host firewall (UFW, firewalld) -with the `FORWARD` chain default set to `DROP` — in that case, allow -forwarding between the site-facing interface and `wt0`: +### Linux + +Install the SNAT rule via `iptables-persistent` so it survives reboot. +This works on systems using either `iptables-legacy` or `iptables-nft` +underneath: ```bash -sudo iptables -I FORWARD 1 -i -o wt0 -j ACCEPT +sudo apt-get install -y iptables-persistent +sudo iptables -t nat -A POSTROUTING -s 192.168.50.0/24 -o wt0 -j MASQUERADE +sudo netfilter-persistent save ``` -**On any other platform** — pfSense, OPNsense, MikroTik, Windows, macOS, or -Linux running in userspace mode — NetBird does not install the SNAT for -you. Configure it manually on the routing peer or its upstream firewall. +### Other platforms -For pfSense / OPNsense, add an outbound NAT rule on the `wt0` (or -equivalent) interface that translates traffic sourced from `192.168.50.0/24` -to the interface address. Switch outbound NAT mode to **Manual** (or -**Hybrid**) so this rule is honoured. - -For MikroTik (RouterOS): - -``` -/ip firewall nat add chain=srcnat src-address=192.168.50.0/24 \ - out-interface=wt0 action=masquerade -``` - -Any outbound source NAT mechanism that rewrites the site-CIDR source to the -routing peer's NetBird IP (or to the `wt0` interface address) on the -NetBird egress path is sufficient. +On non-Linux routing peers, install the equivalent rule via the +platform's native NAT mechanism. Any outbound source NAT that rewrites +the site-CIDR source to the routing peer's NetBird IP (or to the `wt0` +interface address) on egress from `wt0` is sufficient. ## Step 4: Create the Network @@ -144,10 +136,12 @@ Attach the routing peer: 1. In the network, click **Add Routing Peer** 2. Select `site-router` (or the `site-routing-peers` group) -3. **Masquerade:** Enabled is fine, but does not replace - [Step 3](#step-3-configure-the-outbound-snat) — this dashboard flag does - not install a working SNAT on non-Linux routing peers and is unreliable in - userspace mode +3. **Masquerade:** Leave at the default. This flag controls SNAT for + traffic flowing **outbound from NetBird peers through the routing + peer** (the VPN-to-Site direction). It has no effect on Site-to-VPN + traffic, which is what this guide configures — the manual SNAT from + [Step 3](#step-3-configure-the-outbound-snat) is what makes Site-to-VPN + work. 4. Click **Save** ## Step 5: Create the Access Policy @@ -192,14 +186,34 @@ In the examples below, replace `100.121.0.0/16` with your own block. ### Install the route -**On a Linux clientless device:** +**On a Linux clientless device with netplan** (Ubuntu Server default): -```bash -sudo ip route add 100.121.0.0/16 via 192.168.50.10 -# Persist via /etc/network/interfaces, netplan, or NetworkManager +```yaml +# /etc/netplan/99-netbird-route.yaml +network: + version: 2 + ethernets: + eth0: # the interface holding the device's site IP + routes: + - to: 100.121.0.0/16 + via: 192.168.50.10 ``` -**On Windows:** +```bash +sudo chmod 600 /etc/netplan/99-netbird-route.yaml +sudo netplan apply +``` + +**On a Linux clientless device with NetworkManager** (RHEL / Fedora / +desktop distros): + +```bash +sudo nmcli connection modify "" \ + +ipv4.routes "100.121.0.0/16 192.168.50.10" +sudo nmcli connection up "" +``` + +**On Windows** (the `-p` flag persists the route across reboots): ```powershell route -p add 100.121.0.0 mask 255.255.0.0 192.168.50.10 @@ -234,11 +248,12 @@ curl -v http://:8080/ Verify on the target peer that the request arrived: ```bash -sudo ss -tnp | grep :8080 +sudo ss -tan | grep :8080 ``` -The connection's remote address on the target peer will be the **routing -peer's NetBird IP**, not the clientless device's local IP. +The connection appears as a `TIME-WAIT` entry for about a minute after +curl closes; its remote address is the **routing peer's NetBird IP**, +not the clientless device's local IP. ## Resolving NetBird DNS Names @@ -279,27 +294,6 @@ the `server=/netbird.cloud/...` line — you can find it with `netbird status` on the routing peer. -## Outbound SNAT requirement - -NetBird's per-peer access control on the destination peer matches inbound -traffic against an ipset of allowed source IPs. The ipset is populated from -the **NetBird IPs of peers in the policy's source group** — it cannot -contain a routed CIDR like `192.168.50.0/24`. So when a packet from -`192.168.50.20` arrives at the overlay peer, the access control has no -matching entry and the packet is dropped. - -The fix is to rewrite the source IP at the routing peer before the packet -enters the overlay, replacing the site IP with the routing peer's NetBird -IP. That NetBird IP **is** in the policy's source group, so the access -control matches and the packet is accepted. - -On a Linux routing peer running in kernel mode, NetBird installs the SNAT -itself via the kernel netfilter hooks when masquerade is enabled on the -routing peer. On any other platform — pfSense, OPNsense, MikroTik, -Windows, macOS, or Linux in userspace mode — that hook isn't available, so -the SNAT must be configured manually on the routing peer or on its -upstream firewall, as shown in [Step 3](#step-3-configure-the-outbound-snat). - ## Troubleshooting **Connection times out from the clientless device.** @@ -335,31 +329,43 @@ allows `site-routing-peers` → target peer's group on the required port. **Target peer receives packets but drops them.** -The outbound SNAT in [Step 3](#step-3-configure-the-outbound-snat) is -missing or not effective. On the routing peer, packets going out `wt0` must -have their source IP rewritten to the routing peer's NetBird IP: +The outbound SNAT from +[Step 3](#step-3-configure-the-outbound-snat) is missing or not effective. +On the routing peer, packets going out `wt0` must have their source IP +rewritten to the routing peer's NetBird IP: ```bash -# On the routing peer, verify packet counters on the MASQUERADE rule: +# Verify packet counters on the MASQUERADE rule installed in Step 3: sudo iptables -t nat -L POSTROUTING -n -v # Or watch overlay traffic on the way out: sudo tcpdump -ni wt0 'src net 192.168.50.0/24' # Seeing site IPs here means SNAT is NOT firing; the target peer will drop. ``` -If the routing peer is Linux in kernel mode and masquerade is enabled but -the SNAT counters stay at zero, drop in an explicit rule as a fallback: +If the Step 3 rule is missing or its counters stay at zero, re-install it +(and add it to your persistence layer so it survives a reboot): ```bash sudo iptables -t nat -A POSTROUTING -s 192.168.50.0/24 -o wt0 -j MASQUERADE ``` -**DNS resolution returns NXDOMAIN.** +**DNS resolution returns NXDOMAIN or times out.** -Confirm the routing peer's NetBird IP is correct in `dnsmasq.conf` (it -changes if the peer is re-enrolled), and that `dnsmasq` is not bound to -`lo` — binding loopback causes it to refuse forwarding to its own -`127.0.0.1`-co-located NetBird resolver. +Confirm the `server=/netbird.cloud/...` line in +`/etc/dnsmasq.d/netbird.conf` still matches the routing peer's current +NetBird IP — re-enrolling the peer changes it. Isolate from the +clientless device: + +```bash +dig @ .netbird.cloud +``` + +A timeout means `dnsmasq` isn't reachable on the routing peer's site IP +(check `listen-address` in the config). `SERVFAIL` means `dnsmasq` +received the query but its forward to the NetBird resolver failed. +`NXDOMAIN` means the NetBird resolver answered but doesn't know that +hostname — verify the target peer's actual FQDN with `netbird status` +on the peer itself. ## Appendix: Per-Service Port Forwarding @@ -391,8 +397,9 @@ local IP and the forwarded port: curl http://192.168.50.10:18080/ ``` -The outbound SNAT configured in [Step 3](#step-3-configure-the-outbound-snat) -applies to this traffic as well — the target peer still observes the +The outbound SNAT configured in +[Step 3](#step-3-configure-the-outbound-snat) applies to this +traffic as well — the target peer still observes the routing peer's NetBird IP as the source. Each forwarded service needs its own DNAT rule. This pattern is a good fit