[GH-ISSUE #5394] Missing nftables and MASQUERADE rules on Ubuntu Exit Node hosts #10486

Open
opened 2026-08-05 01:26:05 -04:00 by saavagebueno · 3 comments
Owner

Originally created by @braydensh on GitHub (Feb 19, 2026).
Original GitHub issue: https://github.com/netbirdio/netbird/issues/5394

Describe the problem

In collaboration with Claude (AI), I discovered the reason why my Exit Nodes across 4 separate machines (3 separate physical networks) were dropping client traffic. Because I'm not an expert in networking by any means, I'll leave the information Claude summarized from our work together.

  • NetBird fails to add a forwarding accept rule for new connections in its own netbird-acl-forward-filter nftables chain on exit node machines
  • The chain only accepts established,related traffic and then drops everything else from wt0
  • Also fails to add a MASQUERADE rule in POSTROUTING for the NetBird subnet
  • Both failures are silent — no errors in logs, no warnings
  • Workaround is a systemd service that adds the missing rules after wt0 comes up
  • Reproduced across 4 Ubuntu machines (24.04.4 LTS & 25.10) running native (Linux install script, not Docker) versions 0.65.1/0.65.2
  • Also running Tailscale on the same machines, which may or may not be a factor

To Reproduce

Steps to reproduce the behavior:

  1. Create an Exit Node pointing to a Debian/Ubuntu machine.
  2. Connect to the Exit Node (0.0.0.0/0) on a client and attempt any traffic -- I used ping 8.8.8.8.
  3. See no response from pings.

Expected behavior

Traffic should be accepted and flow through the Exit Node host, but the rule to allow them to reach the internet isn't being added (from my understanding).

Are you using NetBird Cloud?

Yes.

NetBird version

0.65.1 / 0.65.2 (varying across 4 machines)

Is any other VPN software installed?

Yes, Tailscale is also installed on all 4 machines tested.
However, because I use 10.147.0.0/16 with NetBird, I don't believe any traffic was being caught by Tailscale's 100.64.0.0/10. I can't say for sure, though.

Debug output

  • netbird status -dA (run on a machine with the attached script fix):
    netbird_status_-dA.txt

  • netbird debug for 1m -AS -U (run on a machine with the attached script fix):
    f79e391890ab27fb37c88b3b4be7011e22aa2e5ca6f38ffa9c4481884941f726/c5a5dd08-8aef-4f6c-bba9-e72150dcedd0

Additional context

Using a systemd script to add the missing rule was an idea inspired by a reply from @crazy-matt to a similar closed issue #1976 (though I'm not using Synology). The script that Claude and I put together to fix the issue contains these two files, which tie to wt0 to add the missing rule.

Originally created by @braydensh on GitHub (Feb 19, 2026). Original GitHub issue: https://github.com/netbirdio/netbird/issues/5394 **Describe the problem** In collaboration with Claude (AI), I discovered the reason why my Exit Nodes across 4 separate machines (3 separate physical networks) were dropping client traffic. Because I'm not an expert in networking by any means, I'll leave the information Claude summarized from our work together. - NetBird fails to add a forwarding accept rule for new connections in its own `netbird-acl-forward-filter` nftables chain on exit node machines - The chain only accepts `established,related` traffic and then drops everything else from `wt0` - Also fails to add a `MASQUERADE` rule in `POSTROUTING` for the NetBird subnet - Both failures are silent — no errors in logs, no warnings - Workaround is a systemd service that adds the missing rules after `wt0` comes up - Reproduced across 4 Ubuntu machines (`24.04.4 LTS` & `25.10`) running native (Linux install script, not Docker) versions `0.65.1`/`0.65.2` - Also running Tailscale on the same machines, which may or may not be a factor **To Reproduce** Steps to reproduce the behavior: 1. Create an Exit Node pointing to a Debian/Ubuntu machine. 2. Connect to the Exit Node (`0.0.0.0/0`) on a client and attempt any traffic -- I used `ping 8.8.8.8`. 3. See no response from pings. **Expected behavior** Traffic should be accepted and flow through the Exit Node host, but the rule to allow them to reach the internet isn't being added (from my understanding). **Are you using NetBird Cloud?** Yes. **NetBird version** `0.65.1` / `0.65.2` (varying across 4 machines) **Is any other VPN software installed?** Yes, Tailscale is also installed on all 4 machines tested. However, because I use `10.147.0.0/16` with NetBird, I don't believe any traffic was being caught by Tailscale's `100.64.0.0/10`. I can't say for sure, though. **Debug output** - `netbird status -dA` (run on a machine with the attached script fix): [netbird_status_-dA.txt](https://github.com/user-attachments/files/25418554/netbird_status_-dA.txt) - `netbird debug for 1m -AS -U` (run on a machine with the attached script fix): `f79e391890ab27fb37c88b3b4be7011e22aa2e5ca6f38ffa9c4481884941f726/c5a5dd08-8aef-4f6c-bba9-e72150dcedd0` **Additional context** Using a systemd script to add the missing rule was an idea inspired by a reply from @crazy-matt to a similar closed issue #1976 (though I'm not using Synology). The script that Claude and I put together to fix the issue contains these two files, which tie to `wt0` to add the missing rule. - /etc/netbird-masquerade.sh: [netbird-masquerade.sh](https://github.com/user-attachments/files/25418924/netbird-masquerade.sh) - /etc/systemd/system/netbird-masquerade.service: [netbird-masquerade.service.txt](https://github.com/user-attachments/files/25418947/netbird-masquerade.service.txt) (extension changed to txt for GitHub)
saavagebueno added the triage-needed label 2026-08-05 01:26:05 -04:00
Author
Owner

@braydensh commented on GitHub (Feb 19, 2026):

If anyone seeing this issue needs the fix scripts, I can add them in text format (rather than file format) here.

  • /etc/netbird-masquerade.sh: (don't forget to sudo chmod +x /etc/netbird-masquerade.sh)
#!/bin/bash

INTERFACE=$(ip route show default | awk '/default/ {print $5}' | head -n1)
TIMEOUT=120
ELAPSED=0
INTERVAL=5

logger -t netbird-masquerade "Starting, waiting for wt0, detected interface: $INTERFACE"

while [ $ELAPSED -lt $TIMEOUT ]; do
    if ip link show wt0 >/dev/null 2>&1; then
        # Fix missing MASQUERADE rule
        RESULT1=$(iptables -t nat -A POSTROUTING -s 10.147.0.0/16 -o $INTERFACE -j MASQUERADE 2>&1)
        if [ $? -ne 0 ]; then
            logger -t netbird-masquerade "ERROR: iptables masquerade failed on $INTERFACE: $RESULT1"
            exit 1
        fi

        # Fix missing nftables forward accept rule
        RESULT2=$(nft insert rule ip netbird netbird-acl-forward-filter iifname "wt0" oifname "$INTERFACE" accept 2>&1)
        if [ $? -ne 0 ]; then
            logger -t netbird-masquerade "ERROR: nft forward rule failed: $RESULT2"
            exit 1
        fi

        logger -t netbird-masquerade "Rules added successfully on $INTERFACE after ${ELAPSED}s"
        exit 0
    fi
    sleep $INTERVAL
    ELAPSED=$((ELAPSED + INTERVAL))
done

logger -t netbird-masquerade "ERROR: wt0 did not appear after ${TIMEOUT}s"
exit 1
  • /etc/systemd/system/netbird-masquerade.service: (don't forget to sudo systemctl daemon-reload and sudo systemctl enable --now netbird-masquerade.service)
[Unit]
Description=NetBird MASQUERADE rule
After=netbird.service
Requires=netbird.service

[Service]
Type=oneshot
ExecStart=/etc/netbird-masquerade.sh
RemainAfterExit=yes

[Install]
WantedBy=multi-user.target
<!-- gh-comment-id:3927954335 --> @braydensh commented on GitHub (Feb 19, 2026): If anyone seeing this issue needs the fix scripts, I can add them in text format (rather than file format) here. - /etc/netbird-masquerade.sh: (don't forget to `sudo chmod +x /etc/netbird-masquerade.sh`) ``` #!/bin/bash INTERFACE=$(ip route show default | awk '/default/ {print $5}' | head -n1) TIMEOUT=120 ELAPSED=0 INTERVAL=5 logger -t netbird-masquerade "Starting, waiting for wt0, detected interface: $INTERFACE" while [ $ELAPSED -lt $TIMEOUT ]; do if ip link show wt0 >/dev/null 2>&1; then # Fix missing MASQUERADE rule RESULT1=$(iptables -t nat -A POSTROUTING -s 10.147.0.0/16 -o $INTERFACE -j MASQUERADE 2>&1) if [ $? -ne 0 ]; then logger -t netbird-masquerade "ERROR: iptables masquerade failed on $INTERFACE: $RESULT1" exit 1 fi # Fix missing nftables forward accept rule RESULT2=$(nft insert rule ip netbird netbird-acl-forward-filter iifname "wt0" oifname "$INTERFACE" accept 2>&1) if [ $? -ne 0 ]; then logger -t netbird-masquerade "ERROR: nft forward rule failed: $RESULT2" exit 1 fi logger -t netbird-masquerade "Rules added successfully on $INTERFACE after ${ELAPSED}s" exit 0 fi sleep $INTERVAL ELAPSED=$((ELAPSED + INTERVAL)) done logger -t netbird-masquerade "ERROR: wt0 did not appear after ${TIMEOUT}s" exit 1 ``` - /etc/systemd/system/netbird-masquerade.service: (don't forget to `sudo systemctl daemon-reload` and `sudo systemctl enable --now netbird-masquerade.service`) ``` [Unit] Description=NetBird MASQUERADE rule After=netbird.service Requires=netbird.service [Service] Type=oneshot ExecStart=/etc/netbird-masquerade.sh RemainAfterExit=yes [Install] WantedBy=multi-user.target ```
Author
Owner

@sjansen1 commented on GitHub (Jun 20, 2026):

Looks like exit node still does not work at all. I can see that traffic comes in on wt0 and send out on eth0 but traffic never finds its way back, only outbound traffic is visible.

IPv4/IPv6 forwarding is active. The script from BTheEPIC does not work. This node is a cleanly fresh installed Hetzner Cloud Server.

Error: Could not process rule: No such file or directory
insert rule ip netbird netbird-acl-forward-filter iifname wt0 oifname eth0 accept
^^^^^

<!-- gh-comment-id:4758997417 --> @sjansen1 commented on GitHub (Jun 20, 2026): Looks like exit node still does not work at all. I can see that traffic comes in on wt0 and send out on eth0 but traffic never finds its way back, only outbound traffic is visible. IPv4/IPv6 forwarding is active. The script from BTheEPIC does not work. This node is a cleanly fresh installed Hetzner Cloud Server. Error: Could not process rule: No such file or directory insert rule ip netbird netbird-acl-forward-filter iifname wt0 oifname eth0 accept ^^^^^
Author
Owner

@dct-leni commented on GitHub (Jun 30, 2026):

entrypoint: >
      /bin/sh -c "
      DEFAULT_IFACE=$$(ip route | grep '^default' | awk '{print $$5}' | head -n1);
      iptables -I FORWARD 1 -i wt0 -j ACCEPT;
      iptables -I FORWARD 2 -m state --state RELATED,ESTABLISHED -j ACCEPT;
      if [ -n \"$$DEFAULT_IFACE\" ]; then
        iptables -t nat -A POSTROUTING -o \"$$DEFAULT_IFACE\" -j MASQUERADE;
      fi;
      exec /usr/local/bin/netbird-entrypoint.sh
      "

help to make exit node working and route all traffic, doesn't seems like right solution... i expected netbird to manage iptabels by it own.

<!-- gh-comment-id:4848573410 --> @dct-leni commented on GitHub (Jun 30, 2026): ``` entrypoint: > /bin/sh -c " DEFAULT_IFACE=$$(ip route | grep '^default' | awk '{print $$5}' | head -n1); iptables -I FORWARD 1 -i wt0 -j ACCEPT; iptables -I FORWARD 2 -m state --state RELATED,ESTABLISHED -j ACCEPT; if [ -n \"$$DEFAULT_IFACE\" ]; then iptables -t nat -A POSTROUTING -o \"$$DEFAULT_IFACE\" -j MASQUERADE; fi; exec /usr/local/bin/netbird-entrypoint.sh " ``` help to make exit node working and route all traffic, doesn't seems like right solution... i expected netbird to manage iptabels by it own.
Sign in to join this conversation.
No Label triage-needed
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: DYNR/netbird#10486