images / pi3-silt-gateway

pi3-silt-gateway

Silt Gateway on a Raspberry Pi 3 Model B: join a tailnet from a browser, on Tailscale or your own control server, and share this network or offer an exit node with nothing installed on the machines behind it.

What it does

  • gatewaySilt Gateway: join a tailnet and choose what this board shares, from a browser
  • tailscaletailscaled, joined at boot, optionally routing its LAN

Raspberry Pi 3 (Model B and B+): flashing and first boot

One image boots both boards: the firmware picks the device tree for the board it finds.

You need: a microSD card (1 GB or larger), a card reader, Ethernet, and a 2.5 A micro-USB power supply. Optional: a USB-to-serial adapter (3.3 V) for the serial console.

1. Check the download. Compare its SHA-256 with the one on this page:

shasum -a 256 sdcard.img        # macOS
sha256sum sdcard.img            # Linux

2. Write it to the card. This erases the card. Find the card's device first and make sure it is the card, not a disk you need.

# macOS: find the card (e.g. /dev/disk6), then
diskutil list external
diskutil unmountDisk /dev/diskN
sudo dd if=sdcard.img of=/dev/rdiskN bs=4m && sync
diskutil eject /dev/diskN

# Linux: find the card (e.g. /dev/sdX), then
lsblk
sudo dd if=sdcard.img of=/dev/sdX bs=4M conv=fsync status=progress

Raspberry Pi Imager works too: choose Use custom and pick sdcard.img.

3. Boot. Insert the card, connect Ethernet, then power. The board asks your network for an address (DHCP) as it starts.

4. Find it. Your router's client list shows it, or scan your network:

sudo nmap -sn 192.168.1.0/24 | grep -B2 -i 'raspberry'

Serial console. On the 40-pin header: pin 6 is ground, pin 8 is the board's TX and pin 10 its RX. Connect them to a 3.3 V adapter (its RX to pin 8, its TX to pin 10) and open it at 115200 baud.

# gateway: Silt Gateway

A board that joins a tailnet and shares its network, set up from a browser. Plug it in, open http://<board>/, choose a password, and join: with an auth key, or by following a sign-in link. On Tailscale's coordination server, or on your own (Headscale), by giving its URL. Then choose what the board offers:

  • subnet routes: the networks it is on, reachable from anywhere on the tailnet with nothing installed on the machines in them;
  • the exit node: devices on the tailnet send their internet traffic out through this board;
  • other routers' routes: whether this board reaches what other gateways share.

Each route shows whether the control server has approved it yet.

How it fits

image:pi*-tailscale  ── feature:tailscale  tailscaled, its init script, CA certs,
                                           a clock, TUN and NAT in the kernel
feature:gateway      ── silt-gatewayd      this page, on :80

silt-gatewayd keeps no Tailscale state. tailscaled's state file holds the node key, the control server and the routes; the page reads them with tailscale status --json and tailscale debug prefs on every request and changes them with tailscale login and tailscale set, so nothing can drift. The only thing it keeps is the owner's password, as a PBKDF2 hash in /var/lib/silt-gateway/password.

The kernel's forwarding is switched on whenever tailscaled holds a route or the exit node (and after a reboot, from what tailscaled holds), never off.

Images

| image | what | |---|---| | pi5-silt-gateway | image:pi5-tailscale + feature:gateway | | pi3-silt-gateway | image:pi3-tailscale + feature:gateway: a Raspberry Pi 3 Model B | | qemu-silt-gateway | image:qemu-aarch64 + feature:tailscale + feature:gateway: the same gateway, no board needed |

Trying it in QEMU

Build it like any image; the output has Image, rootfs.ext4 and Buildroot's own start-qemu.sh:

# on the build machine (once: sudo apt install qemu-system-arm)
kconf build qemu-silt-gateway -o out/qemu-silt-gateway
cd out/qemu-silt-gateway/images
./start-qemu.sh --use-system-qemu -m 1024 -monitor unix:/tmp/gw-qemu.sock,server,nowait

The guest's network is QEMU's user network (eth0), which reaches the internet but takes no connections. Forward the page to the build machine from another shell, through QEMU's monitor:

echo "hostfwd_add eth0 tcp:127.0.0.1:8080-:80" | nc -NU /tmp/gw-qemu.sock

and from your own machine, ssh -L 8080:127.0.0.1:8080 to the build machine and open http://localhost:8080/. Joining works as on a board; routes to the QEMU user network (10.0.2.0/24) are suggested, which is enough to see approval work.

Security

  • The first visitor chooses the password, as with most home appliances: set it before putting the board on a network you don't trust.
  • Sessions are an HttpOnly, SameSite=Strict cookie; every change is a JSON request, which a page on another site can't send with it.
  • Five wrong passwords in a minute lock sign-in for the rest of that minute.
  • To reset a forgotten password, delete /var/lib/silt-gateway/password on the board (serial console) and reload the page.

Name

"Silt Gateway" is a working name. Tailscale is a trademark of Tailscale Inc.; this product uses the open-source Tailscale client and is not affiliated with it. Check its terms before selling a device built on it.

Development

cd br2-external/package/silt-gateway/src
go test ./...
go run ./cmd/silt-gatewayd -http 127.0.0.1:8080 -data /tmp/gw \
  -tailscale "$(command -v tailscale)" -socket /var/run/tailscale/tailscaled.sock

The tests read internal/tailnet/testdata, captured from tailscale 1.78.1 (the version Buildroot 2025.02.16 ships).

On the board

Every file below is checked in the built image before it is released.

  • /etc/default/tailscale
  • /etc/init.d/S95tailscale
  • /etc/init.d/S96silt-gateway
  • /usr/bin/silt-gatewayd
  • /usr/bin/tailscale
  • /usr/sbin/tailscaled

Kernel

Stated by this image and checked in the kernel that was built.

CONFIG_EXT4_FS=y
CONFIG_IP_NF_IPTABLES=y
CONFIG_IP_NF_NAT=y
CONFIG_NETFILTER=y
CONFIG_NF_NAT=y
CONFIG_TUN=m

Hardware it can use

Read from the board's own device tree.

csi-cameradevicetree:bcm2710-rpi-3-b at /soc/csi@7e800000
mmusoc:bcm2837
sdcarddevicetree:bcm2710-rpi-3-b at /soc/mmc@7e202000
serial-consoledevicetree:bcm2710-rpi-3-b at /soc/serial@7e201000
wifi-sdiodevicetree:bcm2710-rpi-3-b at /soc/mmcnr@7e300000/wifi@1