Work
Projects- 3D Printing Projects
- Amateur Radio
- Automotive Projects
- Electronics Projects
- Gaming Projects
- Homelab Projects
- Horticulture Projects
- Other Projects
- Project Projects
- Software Projects
- Tumblr (photography)
-
Work
ProjectsPrerequisite: OpenWrt Tailscale guide done (zone, masq, forwarding, exit node).
All steps via SSH.
/etc/firewall.tailscale-reflect.sh:#!/bin/sh nft insert rule inet fw4 dstnat iifname tailscale0 jump dstnat_tailscale 2>/dev/null . /lib/functions/network.sh network_get_ipaddr WANIP wan [ -n "$WANIP" ] || exit 0 i=0 while uci -q get firewall.@redirect[$i] >/dev/null 2>&1; do if [ "$(uci -q get firewall.@redirect[$i].src)" = "wan" ] \ && [ "$(uci -q get firewall.@redirect[$i].target)" = "DNAT" ]; then dest_ip=$(uci -q get firewall.@redirect[$i].dest_ip) dport=$(uci -q get firewall.@redirect[$i].src_dport) dest_port=$(uci -q get firewall.@redirect[$i].dest_port) [ -n "$dest_port" ] || dest_port="$dport" protos=$(uci -q get firewall.@redirect[$i].proto) [ -n "$protos" ] || protos="tcp" for proto in $protos; do nft add rule inet fw4 dstnat_tailscale \ ip daddr "$WANIP" "$proto" dport "$dport" \ dnat ip to "${dest_ip}:${dest_port}" \ comment "tailscale-reflect-auto" 2>/dev/null nft add rule inet fw4 srcnat_lan \ ip daddr "$dest_ip" "$proto" dport "$dest_port" \ snat ip to "$WANIP" \ comment "tailscale-reflect-auto" 2>/dev/null done fi i=$((i + 1)) done
chmod 755 /etc/firewall.tailscale-reflect.sh
uci add firewall include uci set firewall.@include[-1].type='script' uci set firewall.@include[-1].path='/etc/firewall.tailscale-reflect.sh' uci set firewall.@include[-1].fw4_compatible='1' uci commit firewall
fw4 reload
nft list chain inet fw4 dstnat | grep tailscale nft list ruleset | grep -A6 'chain dstnat_tailscale'
wan-sourced port forward auto-updates on next reload – nothing else to do./etc/hotplug.d/iface/20-firewall).reflection_zone for the tailscale zone – fails silently, fw4 can't detect the interface's subnet.dstnat dispatch rule for a zone if a UCI redirect uses src = that zone. Since none do here, the script inserts it manually every reload – required, not optional.