Skip to main content

Router

The OpenWrt router is the internet gateway for the blr-home network, its only DHCP/DNS server, and the sole ingress point for every port-forwarded service. It is not deployed by any repo in this platform (it is manually configured), but every host and service in application-deployer/inventory.yml depends on the addressing, DNS, and forwarding rules it provides.

This document is a snapshot of the current device's configuration (/etc/config/*, read over SSH), kept for disaster recovery. It is not a restore script: secrets are never reproduced here, and hardware-specific identifiers (MAC addresses) are omitted because they don't carry over to a replacement device anyway. Use it to rebuild an equivalent router, not to restore this exact one.

Hardware / firmware​

ItemValue
ModelTP-Link Archer A9 v6 (tplink,archer-a9-v6)
SoCQualcomm Atheros QCA550X
Targetath79/generic, mips_24kc
FirmwareOpenWrt 25.12.5 (r33051-f5dae5ece4)
Storage6.3 MB squashfs /rom + overlay; see below

A USB drive is attached and mounted at /mnt/pd-03 (/etc/config/fstab, FAT32, matched by UUID). It holds the daily config backups written by backup.ash (see Provisioning and backup scripts). hd-idle is also installed (spins down idle USB HDDs) but its config is empty/disabled.

This device enumerates on the SoC's EHCI (USB 2.0) controller and identifies as a plain mass-storage device, so the kernel's classic usb-storage (BOT) driver claims it, not uas. The kmod-usb-storage-uas and kmod-usb3 packages are installed for USB3/UAS-capable hardware (e.g. a spinning HDD) but sit unused for this particular drive. block-mount/fstools auto-mount by filesystem UUID rather than a fixed /dev/sdaN path, so the same /etc/config/fstab entry survives the drive being plugged into a different USB port or a replacement router.

On a replacement device, pick any target OpenWrt supports and adjust the VLAN/switch config below to that hardware's port layout. The model itself isn't a requirement.

Non-default packages installed​

Beyond the stock ath79/generic image, these are installed (via apk, OpenWrt's current package manager). setup.v3.sh (see Provisioning and backup scripts) installs SSH/SFTP support, rsync, the USB-storage stack, and luci-app-hd-idle; the LuCI web UI itself (luci, luci-ssl, luci-light, the luci-mod-* modules), luci-app-attendedsysupgrade, owut, luci-app-package-manager, rpcd-mod-rrdns, and cgi-io are present as separately, explicitly requested apk packages (per /etc/apk/world) rather than being installed by that script. Re-run setup.v3.sh for the packages it covers, and apk add the rest by hand:

  • luci (full collection) + luci-ssl, luci-light, luci-mod-{admin-full,network,status,system}, luci-compat, luci-lib-ipkg, themes (bootstrap, bootstrap-dark is the active one, material, openwrt, openwrt2020): the web admin UI.
  • luci-app-attendedsysupgrade + owut: GUI and CLI sysupgrade-server-based firmware upgrades (/etc/config/attendedsysupgrade points at sysupgrade.openwrt.org).
  • luci-app-hd-idle + hd-idle: USB HDD spin-down (installed, unconfigured; see above).
  • luci-app-package-manager: manage apk packages from LuCI.
  • openssh-sftp-server: SFTP subsystem for dropbear (dropbear has no built-in SFTP server).
  • rsync: file sync utility, used for the /mnt/pd-03 backups.
  • block-mount, e2fsprogs, kmod-usb-storage-uas, kmod-usb3, kmod-fs-vfat, lsblk, blkid, mount-utils: USB storage support for the /mnt/pd-03 drive (auto-mount via block-mount/fstools, FAT32 filesystem driver, and inspection tools).
  • rpcd-mod-rrdns, cgi-io, announce: LuCI/ubus support packages.

WireGuard is not installed on the router itself. Port 51820 is forwarded to an internal host that terminates the tunnel (see Port forwarding).

Provisioning and backup scripts​

/etc/scripts.d/ holds this router's own setup/backup automation. It is not part of the stock OpenWrt image, and is itself preserved across firmware upgrades via /etc/sysupgrade.conf (see below), so it survives a sysupgrade even though it lives outside /mnt/pd-03.

  • setup.v3.sh: the current provisioning script (apk-based, matches this OpenWrt 25.12 install). Installs SSH/SFTP support, announce, rsync, the USB-storage stack, and luci-app-hd-idle plus a subset of LuCI theme packages (luci-compat, luci-lib-ipkg, luci-theme-material, luci-theme-openwrt, luci-theme-openwrt-2020); writes the homepage rpcd ACL (/usr/share/rpcd/acl.d/homepage.json, scoping that account to network.interface.{wan,lan}, network.device, and system status reads); restarts dropbear and uhttpd; and runs apk update && apk upgrade first. It does not install the LuCI web UI itself (luci, luci-ssl, luci-light, the luci-mod-* modules), luci-app-attendedsysupgrade, owut, luci-app-package-manager, rpcd-mod-rrdns, or cgi-io; those are present on this device as separately, explicitly requested apk packages (per /etc/apk/world), not reproduced by this script. Run it on a fresh OpenWrt install for the packages it covers, and apk add the rest by hand.
  • setup.v1.ash / setup.v2.ash: opkg-based scripts for OpenWrt releases before the switch to apk. They are not used on this OpenWrt version.
  • backup.ash: runs daily via cron (0 3 * * * /bin/ash /etc/scripts.d/backup.ash, in /etc/crontabs/root). Each run rewrites /etc/sysupgrade.conf to preserve /etc/scripts.d/ across upgrades, then calls sysupgrade -b to write a full config-backup tarball to /mnt/pd-03/openwrt-r1-tpla9v6-backups/sysupgrade-config-backup/. Depends on $HOSTNAME being set (ash exports it by default; nothing in this repo sets it explicitly) and on /mnt/pd-03 being mounted. It exits with an error if the backup directory is missing rather than silently skipping. No retention/pruning exists. Backups accumulate indefinitely, which is something to watch if /mnt/pd-03 is small.

These sysupgrade -b tarballs are a same-hardware restore path (sysupgrade -r backup-*.tar.gz-style restore, effectively uci state + /etc/scripts.d/) distinct from the Rebuild checklist below, which is for provisioning a replacement router from scratch.

Network topology​

There is no Guest → LAN edge: the firewall has no forwarding rule between those zones, which is what isolates guest devices from the rest of the network (see Firewall and port forwarding). The guest zone's firewall rules allow only DNS (53) and DHCP (67) inbound from guest clients, which is why GuestDevices only reach DNS, never LAN. LuCI is likewise unreachable from Guest/WAN (both zones reject input; only lan's input ACCEPT lets it through), and its /ubus HTTP endpoint is what the dashboard's openwrt Homepage widget on rb5-m3 calls back into using the scoped homepage rpcd login (see dashboard/stack/homepage/config/services.yaml and ubus and rpcd API access). See DHCP and DNS for how DNS resolves a query, and Firewall and port forwarding for the DNAT rules into Backends.

  • Switch (switch0): VLAN-enabled. VLAN 1 (LAN) on ports 2 3 4 5 + tagged on CPU port 0; VLAN 2 (WAN) on port 1 + tagged on CPU port 0. Adjust port numbers to match the replacement hardware's physical switch. This mapping is specific to the Archer A9 v6's port layout.
  • WAN (eth0.2): PPPoE. Requires an ISP-issued PPPoE username/password from the ISP account portal, not reproduced here (see Secrets excluded from this document). IPv6 is disabled on the PPPoE session itself; a separate wan6 interface does DHCPv6-PD (disabled/auto 0 by default).
  • LAN (br-lan): static 10.8.33.1/24, domain blr-home.easyiac.com.
  • Guest (br-guest): isolated bridge, static 10.8.34.1/24, no LAN routing (see firewall zones). Not attached to any switch VLAN. Client isolation is provided by binding only the guest Wi-Fi SSID to it.
  • IPv6 ULA prefix: locally generated (fdd0:ce94:cf21::/48 on this device). OpenWrt autogenerates a new random ULA prefix on a fresh install, so there's no need to reuse this value.

DHCP and DNS​

DHCP and DNS are split across two daemons: dnsmasq and odhcpd.

  • dnsmasq is authoritative for blr-home.easyiac.com and is the LAN/guest DNS resolver; it also reads Ethernet leases (readethers) so wired clients resolve without a DHCP handshake.

  • Resolution flow: a client's query lands on dnsmasq first (it's the only resolver handed out by DHCP on both lan and guest). A name under blr-home.easyiac.com, or any bare hostname, is answered locally from a DHCP lease, a static config host/config domain entry, or a readethers ARP-table lookup; domainneeded/local '/blr-home.easyiac.com/' keep these from ever leaving the router. Everything else is forwarded upstream to the nameservers in /tmp/resolv.conf.d/resolv.conf.auto (currently 8.8.8.8 / 8.8.4.4 on this device), which netifd regenerates from whatever the ISP hands out over the PPPoE session (peerdns defaults to enabled on wan; nothing in /etc/config/network overrides it, and dnsmasq's own resolvfile option points at that same auto-generated file).

  • LAN pool: 10.8.33.100 to 10.8.33.249, 12h lease.

  • Guest pool: same range convention (100 to 249 in 10.8.34.0/24), 12h lease.

  • WAN: dnsmasq is explicitly told to ignore the wan interface (no accidental DHCP server facing the ISP).

  • odhcpd handles only IPv6 RA/prefix delegation (maindhcp '0'), while dnsmasq owns IPv4.

  • Static hosts (config host): every managed host gets two DHCP entries tagged known (one for its wired MAC, one tagged wifi for its wireless MAC), both pinned to the same hostname with leasetime infinite and dns '1', so the host gets a fixed IP and a forward DNS name regardless of which interface it's on. Actual MAC/IP pairs are host-specific and not reproduced here; recreate this pattern per host from uci show dhcp on the live device when rebuilding.

  • Static aliases (config domain): a name → IP mapping with no DHCP entry of its own, used to give a service its own DNS name on a host that already has a config host entry, so internal clients can resolve nextcloud.blr-home.easyiac.com-style names without a public round-trip. On this device they all point at two hosts from application-deployer/inventory.yml:

    • rb5-m3 (reverse_proxy/wireguard role): fronts minio-s3, minio-console, emby, qbittorrent-nox, qbn, jellyfin, nextcloud, devops-server, code-server, dashboard, navidrome, vikunja.
    • sash-m1: devops-server-ssh.

    Recreate one config domain entry per internet-facing/internally-resolved service, pointed at whichever host actually runs it in the new setup.

Firewall and port forwarding​

Zones: lan (accept all), wan (reject input/forward, masquerade, MTU clamp), guest (reject input/forward, isolated, allowing only DNS 53 and DHCP 67 inbound from guest clients). Forwarding is lan → wan and guest → wan; there is no guest → lan rule, which is what isolates guest devices from the rest of the network. The default WAN-facing accept rules (DHCP renew, ping, IGMP, DHCPv6, MLD, ICMPv6, IPSec ESP/ISAKMP) are stock OpenWrt firewall defaults, not custom additions.

Port forwards (all DNAT, wan → lan). The application-facing rows (80, 443, 51820, 3478) match the services in application-deployer/docs/prerequisites.md#router-port-forwarding; the SSH rows (22, 2228) are host-access forwards outside that deployer's scope:

WAN portProtocolForwarded toService
22TCP+UDPrb5-m3:22SSH (reverse proxy host)
80TCP+UDPrb5-m3:80Reverse proxy HTTP
443TCP+UDPrb5-m3:443Reverse proxy HTTPS
51820TCP+UDPrb5-m3:51820WireGuard VPN
2228TCP+UDPsash-m1:2228DevOps Server SSH (Git-over-SSH)
3478TCP+UDPs1-home:3478Nextcloud Talk coturn TURN relay

None of these config redirect blocks sets an explicit option proto in /etc/config/firewall, so firewall4 forwards both TCP and UDP for every rule (confirmed via fw4 print/nft list) regardless of which protocol the service itself uses.

When rebuilding, recreate one DNAT redirect per internet-facing service, pointed at wherever that service actually lands on the new network. The port numbers are the contract with the outside world, not the destination IPs.

Wireless​

  • Radio: 5 GHz only (radio0, mac80211), channel 36, VHT80 (80 MHz width). No regulatory country code is set on this device. Set one on rebuild if the AP hardware requires it for the correct channel/power table.
  • Main SSID: bound to lan, WPA3/WPA2-mixed (sae-mixed). Passphrase excluded, see below.
  • Guest SSID: bound to guest, disabled '1' (off by default) with encryption none. Turn this on deliberately and set a passphrase before relying on it; open guest Wi-Fi is not the intended steady state.

System​

  • Hostname r1-tpla9v6, timezone Asia/Kolkata (IST-5:30).
  • NTP: client synced against the *.openwrt.pool.ntp.org pool, and also serves NTP to the LAN (enable_server '1' on the lan interface) so internal hosts can sync from the router instead of reaching out individually.
  • WAN activity LED wired to switch0 port-2 traffic.

SSH​

  • The SSH server (dropbear) is bound to the lan interface only, not reachable from wan directly. (The port-22 DNAT above forwards to an internal host's SSH, not the router's own.)
  • Password auth and root password auth are both off. Key-only login is required.
  • openssh-sftp-server is installed to give dropbear SFTP support (see Non-default packages).

Web UI​

  • Served by uhttpd, running LuCI. Listens on 80/443 (v4 and v6), redirect_https '0'. HTTP does not redirect to HTTPS on this device.
  • Uses a self-signed cert (/etc/uhttpd.crt/.key) regenerated locally; config cert 'defaults' sets EC P-256, 397-day validity.
  • http_keepalive '20' + max_requests '20': connections stay open for up to 20s and serve up to 20 requests each, so the LuCI UI's background XHR calls reuse one TLS connection instead of paying a full handshake per call (this MIPS SoC has no hardware crypto/RNG, so each handshake is slow; without keep-alive, back-to-back XHR calls could exceed the browser's XHR timeout).
  • LuCI theme: bootstrap-dark.

ubus and rpcd API access​

Two rpcd logins beyond the implicit root/console access:

  • root: full read/write ubus access, auth delegated to the system password ($p$root is OpenWrt's syntax for "check against the Unix root password", not a stored secret).
  • homepage: read-only, limited to the homepage rpcd ACL that setup.v3.sh writes (network.interface.{wan,lan}, network.device, and system status reads). This is what the dashboard repo's Homepage openwrt widget for r1-tpla9v6 authenticates as (see dashboard/stack/homepage/config/services.yaml), reaching it over uhttpd's /ubus HTTP endpoint rather than local ubus, so it gets router status without full admin rights. Recreate a similarly scoped, read-only login for any such integration rather than reusing the root account.

Secrets excluded from this document​

Per this platform's rule that nothing secret is ever committed, the following exist in the live config but are deliberately not reproduced here. Source fresh values from the ISP/a password manager when rebuilding, not from this file:

  • PPPoE username + password (ISP account credentials)
  • Wi-Fi WPA passphrase (main SSID)
  • rpcd login password hash for the homepage account
  • Router's own root password / SSH host keys

Rebuild checklist​

  • Flash OpenWrt for the replacement hardware's target.
  • Set hostname, timezone (Asia/Kolkata or as desired), NTP pool + enable_server.
  • Configure the switch/VLANs for the new hardware's port layout: one VLAN for LAN ports, one for the WAN port, both tagged on the CPU port.
  • Configure WAN as PPPoE with fresh ISP credentials.
  • Configure LAN as static 10.8.33.1/24 (or a chosen equivalent CIDR) with domain blr-home.easyiac.com (or equivalent).
  • Configure the guest bridge/interface (10.8.34.1/24 or equivalent), left unattached to any switch VLAN.
  • Configure dnsmasq: authoritative domain, readethers, LAN + guest DHCP pools, wan DHCP ignored, odhcpd set to maindhcp '0'.
  • Recreate static DHCP reservations (wired + wifi-tagged pair per host, known tag, leasetime infinite) for each managed host, and the config domain records mapping service names to their host IPs.
  • Recreate firewall zones (lan/wan/guest) and forwarding rules exactly as described above, then the DNAT port forwards for each internet-facing service, pointed at the new host IPs.
  • Configure the 5 GHz radio (channel/width per new AP capability), set a fresh WPA3/WPA2-mixed passphrase on the main SSID; leave the guest SSID disabled until deliberately turned on with its own passphrase.
  • Lock down dropbear: LAN-only, password auth off, root password auth off.
  • Configure uhttpd/LuCI: HTTPS redirect on, regenerate the self-signed cert.
  • Attach and mount any external storage needed for backups.
  • Copy /etc/scripts.d/setup.v3.sh and backup.ash onto the device (pull them from a recent sysupgrade -b backup tarball, or from this device directly, before it's decommissioned) and run setup.v3.sh: it installs the scoped homepage rpcd login, SFTP support, USB storage support, and the packages it covers in Provisioning and backup scripts, in one pass. Separately apk add the LuCI web UI (luci, luci-ssl, luci-light, the luci-mod-* modules), luci-app-attendedsysupgrade, owut, luci-app-package-manager, rpcd-mod-rrdns, and cgi-io, which the script doesn't cover. Add backup.ash to /etc/crontabs/root (0 3 * * * /bin/ash /etc/scripts.d/backup.ash) and confirm its backup directory path matches the new hostname before relying on it.