diff options
| -rw-r--r-- | docs/PACKAGING-GUIDE.md | 30 | ||||
| -rwxr-xr-x | packaging/build/build-node.sh | 16 | ||||
| -rw-r--r-- | packaging/firewall/firewalld/meshbay-node.xml | 6 | ||||
| -rw-r--r-- | packaging/firewall/ufw/meshbay | 5 | ||||
| -rw-r--r-- | packaging/rpm/meshbay-node.spec | 1 |
5 files changed, 54 insertions, 4 deletions
diff --git a/docs/PACKAGING-GUIDE.md b/docs/PACKAGING-GUIDE.md index 5a5c388..56504d4 100644 --- a/docs/PACKAGING-GUIDE.md +++ b/docs/PACKAGING-GUIDE.md @@ -202,6 +202,36 @@ sudo firewall-cmd --reload sudo ufw allow "MeshBay Cast" ``` +### Peer connections (node) + +Opens inbound UDP 1024-65535. **Scope it to the LAN** — apply the firewalld +service to the zone holding the LAN interface, and give the ufw rule a +`from`. It does not belong in an internet-facing zone. + +```bash +# Fedora (firewalld) — replace FedoraWorkstation with your LAN zone +sudo firewall-cmd --permanent --zone=FedoraWorkstation --add-service=meshbay-node +sudo firewall-cmd --reload + +# Ubuntu (ufw) +sudo ufw allow from 192.168.1.0/24 app "MeshBay Node" + +# a libvirt guest reaching the node on its own hypervisor: scope to the guest +# subnet, since traffic to the host's own address is not masqueraded +sudo ufw allow in on virbr0 from 192.168.200.0/24 app "MeshBay Node" +``` + +**Why a node needs this.** WebRTC binds an ephemeral UDP port per connection, +so there is no fixed port to open. A connection succeeds if *either* side can +initiate. Browsers publish their host candidate as an mDNS `<uuid>.local` +name, which `aioice` cannot resolve on any platform and discards — so the node +can never call a browser back, and the browser must call the node. A node that +refuses unsolicited inbound UDP is unreachable from every browser on its own +LAN, and falls back to reflexive candidates, which fail whenever both peers +share one public IP and the router will not hairpin. + +The node's administration surface is unaffected: loopback only, see below. + The node's own administration surface is a loopback API (127.0.0.1 only, per-run token) reached by the CLI and the desktop client's Node page. It is never network-exposed and ships no firewall profile. diff --git a/packaging/build/build-node.sh b/packaging/build/build-node.sh index 9ad4968..bd81096 100755 --- a/packaging/build/build-node.sh +++ b/packaging/build/build-node.sh @@ -75,12 +75,20 @@ cp "$REPO/packaging/systemd/meshbay-node.service" \ cp "$REPO/packaging/systemd/meshbay-node-user.service" \ "$ROOT/usr/lib/systemd/user/meshbay-node.service" -# --- Firewall profile --------------------------------------------------------- -# UFW (Ubuntu/Debian) — carries the "MeshBay Cast" LAN-casting profile only. -# The node's own admin surface is a loopback API (127.0.0.1, token-gated) and -# is never firewall-exposed, so it ships no profile. +# --- Firewall profiles -------------------------------------------------------- +# The node's admin surface is a loopback API (127.0.0.1, token-gated) and is +# never firewall-exposed. Its *peer* traffic is: WebRTC binds an ephemeral UDP +# port per connection, and a peer that publishes an unroutable address — every +# browser does, as an mDNS .local name aioice cannot resolve — can only be +# reached if it calls the node. A node refusing unsolicited inbound UDP is +# therefore unreachable from browsers on its own LAN. Both profiles are passive: +# packaged, not activated, and meant to be scoped to a LAN zone/source. mkdir -p "$ROOT/etc/ufw/applications.d" cp "$REPO/packaging/firewall/ufw/meshbay" \ "$ROOT/etc/ufw/applications.d/" +mkdir -p "$ROOT/usr/lib/firewalld/services" +cp "$REPO/packaging/firewall/firewalld/meshbay-node.xml" \ + "$ROOT/usr/lib/firewalld/services/" + echo "==> meshbay-node staging ready at $ROOT" diff --git a/packaging/firewall/firewalld/meshbay-node.xml b/packaging/firewall/firewalld/meshbay-node.xml new file mode 100644 index 0000000..2f863ec --- /dev/null +++ b/packaging/firewall/firewalld/meshbay-node.xml @@ -0,0 +1,6 @@ +<?xml version="1.0" encoding="utf-8"?> +<service> + <short>MeshBay Node</short> + <description>Inbound WebRTC/ICE for a MeshBay node. Peers connect over a DataChannel negotiated through the hub; there is no fixed listening port, because aiortc binds an ephemeral UDP port per connection. A peer that cannot reach the node directly can only be reached if the node initiates, which requires the peer to publish a routable address — browsers do not, they publish an mDNS .local name the node cannot resolve. So a node that refuses unsolicited inbound UDP is unreachable from any browser it cannot itself call back. Apply this to the zone holding the LAN interface only; it does not belong in an internet-facing zone. The node's administration surface is unaffected: it is a loopback API on 127.0.0.1 and is never exposed here.</description> + <port protocol="udp" port="1024-65535"/> +</service> diff --git a/packaging/firewall/ufw/meshbay b/packaging/firewall/ufw/meshbay index 732e5d8..b87afdb 100644 --- a/packaging/firewall/ufw/meshbay +++ b/packaging/firewall/ufw/meshbay @@ -2,3 +2,8 @@ title=MeshBay Chromecast relay description=HTTP relay for casting decrypted video to LAN devices ports=19550:19553/tcp|5353/udp + +[MeshBay Node] +title=MeshBay node WebRTC/ICE +description=Inbound UDP for peer connections; scope it to your LAN with "from" +ports=1024:65535/udp diff --git a/packaging/rpm/meshbay-node.spec b/packaging/rpm/meshbay-node.spec index 2bebb26..7910a83 100644 --- a/packaging/rpm/meshbay-node.spec +++ b/packaging/rpm/meshbay-node.spec @@ -43,6 +43,7 @@ fi %{_unitdir}/meshbay-node@.service %{_userunitdir}/meshbay-node.service /etc/ufw/applications.d/meshbay +/usr/lib/firewalld/services/meshbay-node.xml %changelog * Sun Aug 31 2026 MeshBay Team <team@meshbay.org> - %{version}-1 |