From 6cafb7d0a67d718c394ad4c9a52e4bd87cc5d2f0 Mon Sep 17 00:00:00 2001 From: Ian Clarke Date: Tue, 14 Apr 2026 09:47:28 -0500 Subject: [PATCH 1/2] docs(manual): add remote access page for SSH tunnel + Tailscale Explains the two supported ways to reach a node's local API from off-LAN: SSH port-forward (no config change, safest default) and extending the source-IP allowlist for a private overlay such as Tailscale or WireGuard via the new --allowed-source-cidrs option in freenet/freenet-core#3875. Pairs with freenet-core PR #3875; should land at roughly the same time so the docs don't reference a flag the released binary doesn't yet understand. [AI-assisted - Claude] --- hugo-site/content/build/manual/_index.md | 1 + .../content/build/manual/remote-access.md | 97 +++++++++++++++++++ 2 files changed, 98 insertions(+) create mode 100644 hugo-site/content/build/manual/remote-access.md diff --git a/hugo-site/content/build/manual/_index.md b/hugo-site/content/build/manual/_index.md index 5e6d6662..c1074b52 100644 --- a/hugo-site/content/build/manual/_index.md +++ b/hugo-site/content/build/manual/_index.md @@ -59,6 +59,7 @@ Understand Freenet's architecture and how it works: Resources for building on Freenet: - [Publish a Website](publish-a-website): Host a static website on Freenet -- no coding required. +- [Remote Access to a Node](remote-access): Safely reach your local node's API from another device (SSH tunnel, Tailscale). - [Tutorial: Create an App](tutorial): Step-by-step guide to creating a decentralized app. - [Contract Interfaces](contract-interface): Reference for contract interfaces. - [Manifest Format](manifest): Details about the `freenet.toml` configuration format. diff --git a/hugo-site/content/build/manual/remote-access.md b/hugo-site/content/build/manual/remote-access.md new file mode 100644 index 00000000..842cdf11 --- /dev/null +++ b/hugo-site/content/build/manual/remote-access.md @@ -0,0 +1,97 @@ +--- +title: "Remote Access to a Node" +date: 2026-04-14 +draft: false +--- + +By default a Freenet node's local HTTP/WebSocket API (port `7509`) only accepts +connections from the **same machine** and from **private-LAN addresses** +(`127.0.0.1`, RFC 1918 ranges, IPv6 link-local, IPv6 ULA). Anything that can +reach that port can use your node's full client API (read and write contract +state, call delegates, and issue requests on your behalf), so the default is +intentionally restrictive. + +This page explains how to access your node from a device that is *not* on your +LAN (for example, from your phone while you're out) without giving up that +security posture. + +> ⚠️ **The 7509 API is fully privileged.** Treat it like SSH: only expose it +> over channels you fully control. Never open it to the public internet, and +> never extend the allowlist to shared address space (CGNAT ranges such as +> `100.64.0.0/10` are shared between subscribers of some ISPs and are only +> safe on a private overlay you control). + +## Option 1 (safest): SSH tunnel + +No configuration changes required. On the remote device: + +```bash +ssh -L 7509:127.0.0.1:7509 you@your-node-host +``` + +Then point your browser or client at `http://127.0.0.1:7509/`. The traffic is +authenticated and encrypted by SSH, and the node itself never exposes 7509 to +anything other than loopback. + +## Option 2: Tailscale (or another private overlay) + +If you use [Tailscale](https://tailscale.com/), [WireGuard](https://www.wireguard.com/), +Nebula, or a similar overlay, your devices get private IPs on a network only +you control. You can grant the node's API access to that overlay with two +steps: + +1. **Bind the API to the overlay interface** (or to `0.0.0.0` / `::`) so the + socket is reachable from the overlay at all: + + ```toml + # ~/.config/freenet/config.toml + [ws-api] + ws-api-address = "0.0.0.0" + ws-api-port = 7509 + ``` + +2. **Extend the source-IP allowlist** to cover the overlay's address range. + For Tailscale, that's the CGNAT range `100.64.0.0/10`, or, ideally, a + narrower CIDR matching just your tailnet's assigned subnet: + + ```toml + [ws-api] + allowed-source-cidrs = ["100.64.0.0/10"] + # Or, stricter: only addresses in your assigned tailnet subnet: + # allowed-source-cidrs = ["100.64.1.0/24"] + ``` + + You can also pass this on the command line or via environment variable: + + ```bash + freenet --allowed-source-cidrs 100.64.0.0/10 + # or + ALLOWED_SOURCE_CIDRS=100.64.0.0/10 freenet + ``` + + Multiple ranges can be supplied by repeating the flag or using a + comma-separated list. IPv6 CIDRs (e.g. Tailscale's `fd7a:115c:a1e0::/48`) + are supported. + +With both set, the node will accept API requests from any device on your +tailnet but continue to reject everything else. + +### What the allowlist does *not* do + +`allowed-source-cidrs` only **extends** the built-in private-IP check. It is +never a substitute for one of: + +- binding the API to a private interface you control, or +- running the API behind a VPN, SSH tunnel, or authenticated reverse proxy. + +In particular, **do not add a public CIDR to this list**. Doing so publishes +your node's full client API to anyone who can route packets to that address. + +## Why the default is strict + +Earlier versions of the node rejected every non-private source IP with the +message *"Only local network connections are allowed"*. Keeping that default +unchanged means the security posture for users who don't configure anything is +identical to what it has always been: loopback and LAN only. Opting in to a +wider allowlist is an explicit choice and requires naming the exact ranges you +trust. From b18218593506d0bd38854591235721eab1d9452b Mon Sep 17 00:00:00 2001 From: Ian Clarke Date: Tue, 14 Apr 2026 10:06:10 -0500 Subject: [PATCH 2/2] docs(manual): clarify loopback bind makes allowed-source-cidrs a no-op Address big-picture review finding on freenet/web#34: the filter is only installed when `ws-api-address` is non-loopback, so the TOML snippet's `allowed-source-cidrs` has no effect if the bind address is left at the default. Spell this out in the Option 2 step. [AI-assisted - Claude] --- hugo-site/content/build/manual/remote-access.md | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/hugo-site/content/build/manual/remote-access.md b/hugo-site/content/build/manual/remote-access.md index 842cdf11..52654ff5 100644 --- a/hugo-site/content/build/manual/remote-access.md +++ b/hugo-site/content/build/manual/remote-access.md @@ -40,8 +40,13 @@ Nebula, or a similar overlay, your devices get private IPs on a network only you control. You can grant the node's API access to that overlay with two steps: -1. **Bind the API to the overlay interface** (or to `0.0.0.0` / `::`) so the - socket is reachable from the overlay at all: +1. **Bind the API to a non-loopback address** so the socket is reachable from + the overlay at all. This is important: the node only installs the + source-IP filter when the API is bound to something other than loopback, + so leaving `ws-api-address = "127.0.0.1"` means the `allowed-source-cidrs` + entry below has no effect (it can't, the socket never sees overlay + traffic). Bind to the overlay interface's IP, or to the wildcard + address: ```toml # ~/.config/freenet/config.toml