Get rid of your router and use your existing Linux server as a router instead (you need 2 Ethernet ports)
Find a file
2026-06-09 19:31:27 -04:00
docker Development 2026-06-09 19:31:27 -04:00
routlin Development 2026-06-09 15:51:02 -04:00
debug_leases.py Development 2026-06-01 23:55:56 -04:00
FUNDRAISING.md Development 2026-06-06 17:14:01 -04:00
GUIDE.md Development 2026-06-07 23:32:06 -04:00
PRO_FEATURES.md Development 2026-06-09 13:52:07 -04:00
README.md Development 2026-05-25 01:04:47 -04:00

Routlin

A collection of Python scripts that transform an existing Linux server (with at least two Ethernet NICs) into a fully-featured enterprise-grade router, eliminating the need for a separate router appliance.

Why Replace Your Router?

Consumer and prosumer router appliances are constrained by OEM firmware. Security patches depend entirely on the vendor's release schedule, features and functionality are often limited, and devices that reach end of life receive no vendor support at all, leaving gaping security vulnerabilities exposed on your network indefinitely. Running your router on a general-purpose Linux machine gives you:

  • Faster speeds - Utilize full fledged computer hardware (typically exceeds that of consumer appliances)
  • Full flexibility - Any configuration that Linux and its tooling support
  • Better security - Patch your own kernel and packages on your own schedule, with no dependency on a vendor who may have abandoned your hardware

Summary

These scripts do not run continuously in the background. They install and facilitate the configuration of battle-hardened software (dnsmasq for DHCP and DNS, nftables for firewall and NAT, chrony for NTP, freeradius for RADIUS, avahi for mDNS discovery, and wireguard for VPN) using JSON files that you edit. A fully-featured, easy-to-use web management dashboard is included for users who prefer not to edit JSON directly.


Capabilities

The suite is organized into independent but complementary scripts, each managing one layer of the stack:

Core: DHCP, DNS, Firewall, RADIUS, mDNS, and WireGuard VPN (core.py)

  • Configures VLAN sub-interfaces via systemd-networkd
  • Assigns static or dynamic DHCP reservations by MAC address and hostname
  • Defines dynamic IP pools per VLAN
  • Manages per-VLAN gateway, DNS, and NTP settings derived from server_identities
  • Runs one dnsmasq instance per VLAN, each bound exclusively to its gateway IP, giving true per-VLAN DNS filtering
  • Applies per-VLAN content filtering - VLANs with different blocklist sets each get their own merged blocklist (blocklists are downloaded and merged by dns-blocklists.py)
  • Supports local hostname overrides (split DNS for DDNS hostnames)
  • Installs a daily systemd timer that runs dns-blocklists.py to refresh blocklists
  • Tracks lifetime DNS metrics (queries forwarded, cache hits, authoritative, TCP peaks, pool usage)
  • Builds nftables tables atomically - safe to re-apply without service disruption
  • Handles port forwarding (DNAT/SNAT) for externally accessible services
  • Handles port wrangling - redirects DNS and NTP requests to the local resolver regardless of what the client device may have hardcoded
  • Blocks traffic from specific IPs or subnets via banned_ips - supports single IPs, CIDR notation, wildcards, and ranges for both IPv4 and IPv6
  • Enforces inter-VLAN isolation by default (forward chain policy drop); specific cross-VLAN traffic is permitted via inter_vlan_exceptions
  • Masquerades outbound traffic for all non-WireGuard VLANs automatically
  • Auto-detects active container bridge interfaces (Docker, Podman, libvirt, etc.) and adds forward rules so VLAN clients can reach containerized services
  • Auto-detects active container bridge interfaces and adds DNS listening on each bridge IP, so containers can reach the local DNS resolver during builds and at runtime
  • Installs a systemd boot service (routlin-nat.service) to re-apply firewall rules on every boot
  • Co-exists with Docker (does not touch Docker-managed nat/filter tables)
  • Generates FreeRADIUS clients.conf and users files from core.json reservations, enabling dynamic VLAN assignment via MAC Authentication Bypass (MAB) for both wired (802.1X) and wireless clients
  • Manages a .radius-secret shared secret file (generated automatically on first --apply if RADIUS is enabled)
  • Configures avahi-daemon as an mDNS reflector to forward service discovery announcements (AirPrint, AirPlay, Chromecast, etc.) across VLANs
  • Supports any number of WireGuard VPN interfaces (is_vpn: true VLANs); generates the server keypair on first apply, writes the server conf to /etc/wireguard/, and brings the interface up with wg-quick; subsequent applies sync peer changes live without restarting the interface
  • Supports per-peer split-tunnel (VPN subnet only) or full-tunnel (all traffic) routing; peer data is stored directly in core.json

Optional: DNS Blocklists (dns-blocklists.py)

  • Downloads blocklists from upstream providers you choose (e.g. OISD, Hagezi)
  • Merges them per unique VLAN combination into conf files loaded by dnsmasq
  • Runs core.py --apply after a successful download to reload all instances
  • Invoked by the daily systemd timer installed by core.py --apply

Optional: DDNS (ddns.py)

  • Detects the current public IP by rotating through multiple IP-check services
  • Updates the specified DNS providers (currently supporting Cloudflare, No-IP and DuckDNS), supporting multiple hostnames and subdomains per provider
  • Caches the last known IP per provider to avoid unnecessary API calls
  • Installs a systemd timer that runs every 5 minutes by default
  • Logs all updates and errors to ddns.log

Optional: Routlin Dashboard

  • Web UI for managing all aspects of the router (VLANs, reservations, blocklists, VPN, DDNS, firewall, and more) without editing JSON by hand
  • Runs as a Docker container alongside the existing scripts
  • Changes made in the dashboard are queued and applied to the live system automatically via a 1-minute systemd timer

Software Dependencies

These packages are required. install.py checks that they are installed and will prompt to install any that are missing.

Dependency Purpose Required By
python3 Runs all scripts All
systemd Service, timer, networkd, and timesyncd management All
dnsmasq DHCP server and DNS resolver/forwarder core.py
nftables Firewall, NAT, port forwarding, and port wrangling core.py
chrony NTP server - synchronizes system clock and serves time to VLAN clients core.py
freeradius RADIUS server for dynamic VLAN assignment via MAC auth core.py
avahi-daemon mDNS reflector for cross-VLAN service discovery core.py
wireguard-tools WireGuard VPN (wg, wg-quick) core.py (when WireGuard VLANs are configured)
docker Runs the Routlin Dashboard container install.py (dashboard only)
caddy Reverse proxy for external HTTPS access to the dashboard install.py (external access only)

Conflicting Software

The following services conflict with this suite. No manual action is required: core.py disables them automatically on --apply. core.py re-enables them on --disable.

  • systemd-resolved - DNS stub resolver that conflicts with dnsmasq on port 53. Disabled on --apply; re-enabled on --disable.
  • systemd-timesyncd - Basic SNTP client that cannot serve time to LAN clients; replaced by chrony. Disabled on --apply; re-enabled on --disable.
  • ufw - Firewall manager that conflicts with the nftables ruleset. Disabled on --apply without removal.

Hardware Requirements

  • A Linux server with at least two Ethernet NICs:
    • One NIC facing your ISP modem/ONT (WAN)
    • One NIC facing your internal switch (LAN)

For manual configuration and command-line usage without the dashboard, see USAGE.md.