Files
avaya_switch_management/README.md
T
Claude 30d67ebd4f Overhaul README to reflect unified switch + OPNsense management
The tool now actively manages OPNsense alongside the switch. Updated:

- Title: "Switch + OPNsense Network Manager"
- Description: mentions VLANs, firewall, DHCP, DNS, VPN, backups
- Tabs: updated from 8 to 17 with descriptions for all new tabs
  (Dashboard, Network, Firewall, Services, Port Fwd, PoE, Topology,
  Backups, Alerts)
- OPNsense: changed from "optional/read-only" to core integration
- VPN: OPNsense WireGuard as recommended, ControlD DNS auto-applied
- "Does Not Do": removed "does not manage OPNsense" (it does now)
- Files table: added 15 new config files (policies, services,
  backups, schedules, NAT rules, etc.)
- Backup note: back up entire /etc/switch-manager/ directory
- Endpoint count: 34 → ~60
- Component count: 8 tabs → 17 tabs, 38 components

https://claude.ai/code/session_01Do9bsN39MTuy2GVv7yzSrE
2026-03-28 16:57:08 +00:00

916 lines
41 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Avaya / Extreme ERS Switch + OPNsense Network Manager
A browser-based unified management interface for Avaya / Extreme Networks ERS switches and OPNsense routers. Manages VLANs, firewall policies, DHCP, DNS filtering, WireGuard VPN, port forwarding, service proxying, backups, and scheduling — across both devices from one place. You click buttons. The software figures out the CLI and API calls. You never type a switch command or log into the OPNsense GUI separately.
Compatible with: ERS 5928, ERS 5948, ERS 5952, ERS 5952-PWR+, ERS 59100GTS-PWR+
---
## Requirements
**Management computer OS: Linux** (Ubuntu / Debian recommended — Raspberry Pi OS works perfectly)
The setup script uses `apt`, `systemd`, `picocom`, and Docker. It will not run on Mac or Windows. The management computer does not need a monitor — a headless Raspberry Pi or thin client is ideal. Once running, the web UI is accessible only from devices on VLAN 99 (management network) or via WireGuard VPN with VLAN 99 access.
The switch UI itself (the browser interface) works from any OS — phone, tablet, Mac, Windows, Linux — as long as you're on VLAN 99 or connected via VPN.
**Switch OS: BOSS (Baystack Operating System Software) v7.9.6**
BOSS is the firmware that runs on Avaya / Extreme ERS switches. This project is written and tested against BOSS v7.9.6 on the ERS 59100GTS-PWR+. It is not Cisco IOS or any other vendor's CLI — commands are different. BOSS firmware is pre-installed on the switch and does not need to be downloaded or installed.
---
## Physical Setup — First Time Out of the Box
Everything here happens before you run any software. You need a console cable and a laptop.
---
### Step 1 — Find the Service Port
The **console (service) port** is a physical RJ-45 serial port on the switch. It is **not** a regular ethernet port.
**ERS 59100GTS-PWR+:**
The console port is on the **front panel**, left side. It is labelled **"Console"** and looks like a standard ethernet jack but is wired as RS-232 serial. It is located next to the USB port and the out-of-band management port.
**ERS 5952 / 5952-PWR+:**
The console port is on the **front panel**, far left, labelled **"Console"**.
---
### Step 2 — Get a Console Cable
Search **"RJ45 to USB console cable Cisco compatible"** — around $8. This is the same cable used for Cisco, Juniper, and most enterprise switches. It has an RJ-45 plug on one end and USB-A on the other.
**Do not** use a standard ethernet cable — it will not work.
---
### Step 3 — Connect and Open a Terminal
**Important:** Plug the console cable in — both the RJ-45 end into the switch and the USB end into your laptop — **before** powering on the switch. If you plug in after power-on, `screen` and `picocom` may not detect the device correctly.
**Settings:** `9600 baud · 8 data bits · No parity · 1 stop bit · No flow control`
Open a terminal session:
| OS | Command |
|---|---|
| Linux | `sudo picocom -b 9600 /dev/ttyUSB0` |
| Mac | `screen /dev/tty.usbserial-* 9600` |
| Windows | PuTTY → Serial → COM port → 9600 baud |
To find your COM/device name if unsure:
- Linux: `ls /dev/ttyUSB*` before and after plugging in
- Mac: `ls /dev/tty.usb*` before and after plugging in
- Windows: Device Manager → Ports (COM & LPT)
> **Console vs SSH:** The console cable gives you direct CLI access to type commands on the switch. It is only needed for initial setup. Once SSH is configured, the management script connects over Ethernet (port 22) and you never need the console cable again — unless you lock yourself out.
---
### Step 4 — Power On and Read the Boot Screen
Power on the switch. You will see a banner like this:
```
Enter Ctrl-Y to begin.
*************************************************************
*** Ethernet Routing Switch 59100GTS-PWR+ ***
*** Copyright (C) 1996-2024 Extreme Networks. ***
*** HW:22 FW:7.5.0.4 SW:v7.9.6.015 ***
*************************************************************
```
**Press Ctrl-Y.** The switch is waiting for this keypress to continue booting — it will not proceed without it. After pressing Ctrl-Y the switch runs POST and boots normally in about 6090 seconds.
When it is ready you will see:
```
Login:
```
---
### Step 5 — Log In with Default Credentials
```
Login: admin
Password: (press Enter — no password by default)
```
If that fails, try `admin` / `admin`. If the switch was previously configured you may need to do a factory reset (see Troubleshooting below).
You will land at the `5952#` or `59100GTS-PWR+#` prompt in privileged exec mode.
---
### Step 6 — Bootstrap VLAN 99 via Console
This is the only time you need the console cable for normal operation. Enter these commands one at a time, waiting for the prompt before each:
```
enable
configure terminal
vlan create 99 name "Management" type port
interface vlan 99
ip address 192.168.99.1 255.255.255.0
no shutdown
exit
vlan members add 99 1
vlan pvid 1 99
ip ssh
username admin password YourPassword
end
save config
```
Replace `1` with the port number your management computer will plug into. Replace `YourPassword` with a strong password (832 characters; allowed special characters: `! @ # $ % ^ & * - _ = + [ ] ; : , . /` — no spaces, no quotes).
**Port numbering:** On both the ERS 5952 and ERS 59100, use bare port numbers (`1`, `2`, `48`…). There is no slot prefix.
> **What about the switch's existing IP?**
> Out of the box the switch has `192.168.1.1` on VLAN 1. If your router is also `192.168.1.1`, they will conflict — two devices with the same IP on the same network causes ARP fights and makes both unreliable.
>
> This setup deliberately avoids that by putting management traffic on a **separate VLAN (99) with its own subnet (192.168.99.x)**. Your management computer gets a static IP on that subnet and talks to the switch there. The switch's original VLAN 1 address is irrelevant once VLAN 99 is up.
>
> If you ever need to change the switch's VLAN 1 IP (e.g. to remove the conflict before VLAN 99 is configured):
> ```
> config terminal
> interface vlan 1
> ip address 192.168.1.2 255.255.255.0
> exit
> ip default-gateway 192.168.1.1
> exit
> save config
> ```
After the last command the switch confirms with `CP1 [07/04/15 12:00:00.000:INFO]: Operation Success` or similar.
---
### Step 7 — Give the Management Computer a Static IP
On the machine that will run the switch manager software:
```bash
# Find your interface name first
ip link show
# Set a static IP on the management network interface (adjust eth0 to your interface)
sudo ip addr add 192.168.99.50/24 dev eth0
sudo ip link set eth0 up
```
Verify: `ping 192.168.99.1` — you should get replies from the switch.
---
### Step 8 — Run the Setup Script
```bash
python Avaya_5952_setup.py
```
The script handles everything from here. When it pauses and asks you to load the SSH public key onto the switch, it will show you exactly what to paste into the console.
---
## Installation
Copy `Avaya_5952_setup.py` to your always-on management computer (Raspberry Pi, HP T620 thin client, old computer or laptop) and run:
```
python Avaya_5952_setup.py
```
That is the entire installation process. The script handles everything automatically. The only step requiring human intervention is loading the SSH public key onto the switch via console cable — the script pauses, shows you exactly what to type, and waits for you to confirm before continuing.
---
## What the Setup Script Does
The script runs 21 steps automatically:
1. Writes all project files (backend, frontend source, this README)
2. Installs system packages (picocom, qrencode, wireguard-tools)
3. Adds your user to the dialout group for console cable access
4. Installs Python dependencies
5. Creates the config directory `/etc/switch-manager/`
6. Sets a static IP on your management network interface
7. Asks whether to deploy with Docker (recommended) or native Python
8. Guides you through console cable setup and software links
9. Generates an ed25519 SSH keypair
10. Shows you exactly what to paste on the switch console
11. Tests the SSH connection and retries if it fails
12. Pins the switch host key (MITM protection)
13. Generates your TOTP authenticator secret with QR code
14. Verifies your authenticator app is working before continuing
15. Patches the backend config with your switch IP and credentials
16. Builds the React frontend (requires Node.js)
17. Installs and enables a systemd service (or Docker container)
18. Starts the service
19. Optionally sets up Docker with Caddy HTTPS
20. Optionally configures WireGuard VPN for remote access
21. Optionally installs Control D DNS filtering via ctrld
Re-running is safe — completed steps are skipped.
---
## Deployment Options
The script explains both options before asking:
**Docker + Caddy (recommended default)**
Runs the switch manager in a container with all dependencies baked in. Caddy provides automatic HTTPS at a hostname you choose (default: `switch.mgmt.lan`). Caddy generates its own internal CA — the setup script installs it into your system trust store automatically so browsers show a clean padlock. Updates with one command: `docker compose pull && docker compose up -d`.
**Native Python (bare metal)**
Runs directly as a Python process managed by systemd. Simpler, lower memory, no Docker required. Access via `http://IP:8765` directly. Right choice for very low memory machines or users who prefer managing services directly.
---
## Accessing the Interface
```
http://[management computer IP]:8765
```
Or with Docker + Caddy:
```
https://switch.mgmt.lan
```
You must be on VLAN 99 (management VLAN) or connected via WireGuard VPN to reach this address. This is intentional — the switch enforces isolation at the network layer.
### Getting onto VLAN 99 from your laptop
**Linux:**
```bash
sudo ip link add link eth0 name eth0.99 type vlan id 99
sudo ip addr add 192.168.99.50/24 dev eth0.99
sudo ip link set eth0.99 up
```
**Mac:** System Settings → Network → Add VLAN interface → VLAN ID 99
**Dedicated port:** Ask the switch to put one port on VLAN 99 as an access port. Plug in when managing.
**VPN:** Connect via WireGuard (configured during setup or via the VPN tab).
---
## The Interface — 17 Tabs
### Dashboard
Health overview showing switch and OPNsense connectivity, VLAN summary, and system status at a glance.
### Network
Unified VLAN provisioning — create a VLAN across both the switch and OPNsense in one operation. Select ports, set PoE per-port, auto-configure DHCP scope and firewall rules on OPNsense. Auto-backup before every change.
### Firewall
Inter-VLAN policy matrix — visual grid showing what each VLAN can reach. Click a cell to set the policy between two VLANs. Generates both switch ACLs and OPNsense firewall rules. Presets: Printer VLAN (one-way, other VLANs can print but printers can't reach out), IoT isolated (internet only), Guest isolated, Camera NVR-only, LAN-access-all.
### Services
Reverse proxy management for Caddy (running on a separate LAN services computer). Add services by FQDN and backend URL. Deploy writes the Caddyfile, verifies NAT reflection and WAN port forward on OPNsense. Isolated VLANs reach services via public FQDNs — NAT reflection handles it internally, no VLAN isolation broken.
### Port Fwd
OPNsense NAT port forwards for non-HTTP services (game servers, SSH, mail). For HTTP services, use the Services tab (Caddy handles routing by hostname on port 443).
### Port Map
Visual 48+4 port chassis, colour-coded by VLAN. Each port shows its VLAN assignment, mode (access/trunk/disabled), and a green dot when PoE is active. Click any port to configure it in the right panel.
**Port configuration panel:**
- Description (e.g. "AP-Corridor-1", "Camera-NE")
- Mode: Access, Trunk, or Disabled
- VLAN assignment (access) or tagged/native VLANs (trunk)
- PoE on/off and wattage limit (copper ports, 1W30W)
Live status pulls from the switch every 15 seconds while the tab is active, 60 seconds when backgrounded, and pauses when nobody has the page open.
### VLANs
Create, rename, and delete VLANs. Shows port count and subnet per VLAN. VLAN 1 cannot be deleted.
### ACL Builder
Build Access Control Lists visually. Each rule specifies action (permit/deny), protocol (ip/tcp/udp/icmp), source, destination, and optional port. The ACL is assigned to a VLAN interface with a direction. The tool generates all CLI syntax — you never write it yourself.
**Templates:** Click "Use Template" to pre-fill rules for common patterns: Staff (full internet, no management), IoT (internet only, no RFC1918), Guest (internet + ctrld DNS enforcement), Camera (NVR only). All rules are editable after applying the template.
### Review & Push
Every change across all tabs is translated into the exact CLI commands the switch understands. This tab shows those commands before anything is sent.
**You never write CLI commands.** The tool generates them. The review step exists so you can inspect what will be sent.
**Push mode choice:**
- **Batch** — all commands sent in sequence, results shown when complete
- **Step by step** — one command at a time, confirm each before the next is sent
Each command is displayed with a plain-English explanation of what it does and what it affects. You can read exactly what is about to happen.
### Device Access
Manages which devices can reach the management interface from their normal VLAN without needing VPN. The switch enforces access via an ACL pinhole. TOTP still gates any changes.
**MAC address randomization warning:** Modern phones and laptops randomize MAC addresses per network. This breaks DHCP reservations. The tab shows per-platform instructions to disable it (iOS, Android, macOS, Windows) before adding a device.
**Live DHCP leases** pulled from the switch appear as unregistered devices — click Register to add them.
**Per device:**
- Name, MAC, IP, VLAN
- Reserve static IP (DHCP binding pushed to switch)
- Grant/revoke management access (ACL pinhole pushed to switch)
### DHCP
Unified view of all DHCP reservations across switch and OPNsense (if configured).
**DHCP server recommendation:**
- Use switch DHCP for VLAN 99 (management) — devices get IPs before OPNsense is reachable
- Use OPNsense for all other VLANs — integrates with DNS, firewall rules, lease history
- Never run both for the same VLAN
**OPNsense integration:**
Auto-detects OPNsense at your gateway IP. Prompts for API key and SSH key. Once connected, the tool manages both devices together — VLANs, DHCP, firewall rules, port forwards, DNS, WireGuard, and backups. DHCP tab shows reservations from both switch and OPNsense in one table, colour-coded by source.
**Conflict detection:** If the same MAC has reservations in both places, a badge appears — red for IP conflicts (same MAC, different IP), yellow for duplicates (same MAC, same IP). Each conflict has a Resolve button with four options: Switch wins, OPNsense wins, Remove from switch, Remove from OPNsense.
### DNS Filtering
Configures Control D DNS filtering per VLAN via the `ctrld` daemon.
**How it works:** `ctrld` runs as a local DNS proxy. Devices send normal DNS queries to it. `ctrld` identifies the source VLAN subnet and routes each query to the correct Control D profile via DoH3. Each VLAN gets different filtering rules. Your ISP sees encrypted HTTPS traffic, not DNS queries.
**Three deployment options presented with full explanations:**
*Option A — ctrld on the management computer (fully automated)*
Installs ctrld alongside the switch manager. One command downloads and installs it, writes the per-VLAN config, starts it as a system service. The switch DHCP points each VLAN to this machine's IP for DNS.
*Option B — ctrld on OPNsense (semi-automated)*
Generates a single SSH command to paste into OPNsense shell. ctrld installs as a service on OPNsense. OPNsense's IP becomes the DNS server for the network.
*Option C — manual / existing setup*
Generates the `ctrld.toml` config and install command. You install wherever you choose.
**Per-VLAN Resolver IDs:** Each VLAN gets its own Control D profile. Enter the Resolver ID from the Control D dashboard (controld.com → Add Device → Router → Resolver ID). VLANs without a Resolver ID use the first configured profile as fallback.
**DNS Enforcement:** After installing ctrld, use the "Enforce DNS on Switch" button to generate ACLs that block devices from bypassing ctrld by using 8.8.8.8 directly. See [DNS Enforcement ACLs](#dns-enforcement-acls) below.
**Local Hostnames:** Optionally run a dnsmasq container so `.lan` names resolve for all devices. See [Local Hostname Resolution](#local-hostname-resolution-dnsmasq) below.
**References:**
- Control D documentation: https://docs.controld.com/docs/ctrld
- Router setup guide: https://docs.controld.com/docs/routers-platform
### VPN
Manages WireGuard VPN on OPNsense (recommended) or locally on the management computer (fallback).
**OPNsense WireGuard (recommended):** Creates a WireGuard server on OPNsense via plugin API. Peers select which VLANs they can access — OPNsense routes between the tunnel and VLANs per firewall rules. VLAN isolation preserved. ControlD DNS profiles auto-applied via ctrld — when you select a VLAN for a peer, the DNS profile is auto-set to match.
**Add clients:** Enter a name, select VLAN access, and the tool generates a keypair, registers the peer on OPNsense, and displays a QR code. Private key never leaves the management computer. DNS is set to OPNsense so Unbound → ctrld → ControlD filtering applies. This also solves the Android Private DNS conflict — WireGuard's DNS setting overrides it.
**Local WireGuard (fallback):** Runs on the management computer. Peers can only reach VLAN 99. Use when OPNsense is being reconfigured.
**SSH tunnel alternative:** For power users — one command gives secure access without WireGuard installed:
```bash
ssh -L 8765:localhost:8765 user@management-computer-ip
```
### PoE
Power budget dashboard. Visual bar showing total/used/remaining watts with colour-coded thresholds. Per-port power draw grid. Warning when budget exceeds 85%.
### Topology
Auto-generated network diagram: OPNsense router → trunk link → switch → VLAN fan-out with port counts, device counts, and live status for each node.
### Backups
Automatic backup before every change (VLAN provisioning, policy push, service deploy). OPNsense full XML config export via API. Switch running-config capture via SSH. Manual backup/restore with download. Restore creates a safety backup first. Keeps last 50 per device.
### Alerts
ntfy push notifications for network events (connectivity loss, PoE budget warning). VLAN time-based schedules — disable/enable internet access per VLAN on a schedule (Guest WiFi off midnight6am, Business off after hours). Scheduled backups and connectivity checks.
---
## Making Changes — Step by Step
1. **Configure** across any tabs — nothing happens on the switch yet
2. **Go to Review & Push** — read the generated commands with explanations
3. **Check for dangers** — automatic pre-flight runs before authentication
4. **Authenticate** — enter 6-digit TOTP code to unlock a push session
5. **Choose push mode** — batch (faster) or step-by-step (full control)
6. **Push** — commands go one at a time, checked after each
7. **Review results** — each command shows success or failure with the switch's error output
8. **Auto-lock** — session closes when push completes. New changes need a new TOTP code
---
## Authentication and Sessions
**Read-only** — no authentication required. Anyone on VLAN 99 can view the dashboard.
**Push mode** — requires TOTP. One code unlocks exactly one push session. The session closes automatically when the push completes or you cancel. The next set of changes requires a new TOTP code.
**Why TOTP and not a password:** VLAN 99 isolation is the primary barrier. TOTP adds a second factor confirming it is you making a change. A code is useless after 30 seconds and requires physical access to your authenticator app.
---
## How Commands Reach the Switch
Exact sequence on every push:
1. Browser sends the command list to the management computer backend
2. Backend runs a danger check against known lethal patterns
3. Backend validates every command against an allowlist
4. Backend opens a fresh SSH connection using the ed25519 key stored on the management computer
5. Commands execute one at a time via interactive shell
6. After each command the switch's response is checked for error patterns
7. On error: push stops immediately, no further commands sent, config not saved
8. On full success: `end` then `copy running-config nvram:config.cfg` — config saved to NVRAM
---
## Blocked Commands
**Hard-blocked — refused entirely, must run at the switch console:**
| Pattern | Reason |
|---|---|
| `no vlan 99` | Deletes management VLAN |
| `vlan members remove ... 99` | Removes VLAN 99 from a port — kills management trunk |
| `no vlan tagging ... 99` | Removes VLAN 99 tagging — kills management trunk |
| `no ip ssh` | Disables SSH — permanent lockout |
| `no ip address` | Removes IP — management computer loses connectivity |
| `interface vlan 99` | Modifies management VLAN interface |
| `boot config flags factory` | Factory reset |
**Warning-level — shown for review, push proceeds with confirmation:**
| Pattern | Reason |
|---|---|
| `shutdown` | Shuts down an interface — confirm not your uplink |
| `default interface` | Resets interface to defaults |
| `no vlan [id]` | Deletes a VLAN — confirm no active ports depend on it |
| `spanning-tree ... disable` | Disables spanning tree — loop risk |
---
## SSH Key Security
The ed25519 private key lives at `/etc/switch-manager/ers5952_key` on the management computer. It never leaves that machine. Your laptop, phone, or tablet never touches it.
The switch host key is pinned after the first connection. If the switch's host key ever changes the backend refuses to connect and reports the mismatch — MITM protection even on the management VLAN.
---
## Console Cable
See [Physical Setup — First Time Out of the Box](#physical-setup--first-time-out-of-the-box) above for the complete walkthrough including where the console port is, what cable to buy, and what to do at the boot screen.
The short version: one-time only, RJ-45 to USB console cable (~$8), 9600 baud, no parity.
**Switch password rules:** 832 characters. Special characters allowed: `! @ # $ % ^ & * - _ = + [ ] ; : , . /`. No spaces. No quotes.
---
## Live Polling
The backend polls the switch using a connection pool:
- Pool lifetime: 25 seconds (shorter than switch idle timeout)
- Tab active/visible: polls every 15 seconds
- Tab backgrounded: polls every 60 seconds
- No visitors for 5 minutes: polling pauses completely
- New visitor opens page: immediate poll, resumes normal interval
The switch is never being polled when nobody is looking at the dashboard.
---
## Files Created
| File | Location | Purpose |
|---|---|---|
| `switch_backend.py` | Project folder | Python API server (~60 endpoints) |
| `ers5952-manager.jsx` | Project folder | React app source (17 tabs, 38 components) |
| `README.md` | Project folder | This file |
| `Dockerfile` | Project folder | Docker image definition |
| `docker-compose.yml` | Project folder | Caddy + switch manager services |
| `Caddyfile` | Project folder | HTTPS reverse proxy config |
| `ctrld.toml` | Project folder | Control D per-VLAN DNS config (if configured) |
| `frontend/dist/` | Project folder | Built React app |
| `ers5952_key` | `/etc/switch-manager/` | SSH private key (chmod 600) |
| `ers5952_key.pub` | `/etc/switch-manager/` | SSH public key |
| `known_hosts` | `/etc/switch-manager/` | Pinned switch host key |
| `totp_secret` | `/etc/switch-manager/` | TOTP seed (chmod 600) — back this up |
| `devices.json` | `/etc/switch-manager/` | Registered device list |
| `opnsense.json` | `/etc/switch-manager/` | OPNsense API credentials (chmod 600) |
| `ctrld.json` | `/etc/switch-manager/` | Control D config (chmod 600) |
| `wg_server_private` | `/etc/switch-manager/` | WireGuard server private key (chmod 600) |
| `wg_server_public` | `/etc/switch-manager/` | WireGuard server public key |
| `clients/` | `/etc/switch-manager/` | WireGuard client .conf files |
| `switch-manager.service` | `/etc/systemd/system/` | Systemd service (native mode) |
| `opnsense_key` | `/etc/switch-manager/` | OPNsense SSH private key (chmod 600) |
| `opnsense_known_hosts` | `/etc/switch-manager/` | Pinned OPNsense host key |
| `opnsense_wg.json` | `/etc/switch-manager/` | WireGuard server/peer state (chmod 600) |
| `vlan-if-map.json` | `/etc/switch-manager/` | VLAN ID → OPNsense interface mapping |
| `vlan-policies.json` | `/etc/switch-manager/` | Inter-VLAN firewall policies |
| `service-proxies.json` | `/etc/switch-manager/` | Caddy reverse proxy service entries |
| `services-config.json` | `/etc/switch-manager/` | Services box LAN IP config |
| `Caddyfile.services` | `/etc/switch-manager/` | Auto-generated Caddy service proxy entries |
| `service-nat-rules.json` | `/etc/switch-manager/` | Tracked OPNsense NAT rule UUIDs |
| `port-forwards.json` | `/etc/switch-manager/` | Managed port forward rules |
| `pinholes.json` | `/etc/switch-manager/` | Firewall pinhole rule UUIDs |
| `schedules.json` | `/etc/switch-manager/` | Scheduled operations (backups, VLAN toggles) |
| `ntfy.json` | `/etc/switch-manager/` | ntfy alert configuration |
| `backups/switch/` | `/etc/switch-manager/` | Switch running-config backups |
| `backups/opnsense/` | `/etc/switch-manager/` | OPNsense XML config backups |
| `local-hostnames.json` | `/etc/switch-manager/` | User-defined hostname→IP mappings (optional) |
| `dnsmasq.conf` | `/etc/switch-manager/` | Generated dnsmasq config (optional) |
**Back up the entire `/etc/switch-manager/` directory** — it contains SSH keys, TOTP secrets, OPNsense credentials, WireGuard keys, and all configuration state. The Backups tab automatically saves switch and OPNsense configs before every change, but the management computer's own config directory should also be backed up externally.
---
## Firmware Upgrade
The switch runs two software components that must both be upgraded:
- **Diagnostic image** (diag) — upgraded first
- **Agent image** (BOSS firmware) — upgraded second
**Important rules:**
- Upgrade one version at a time — cannot skip releases
- Always upgrade the diagnostic image before the agent image
- USB files must not be marked read-only or the transfer fails
- Firmware downloads require an active support contract at the [Extreme Networks portal](https://extreme-networks.my.site.com)
- Check the [ERS Announcements page](https://community.extremenetworks.com/t5/ers-announcements/bg-p/ERS_Announcements) to find the latest version
Your switch currently runs: **BOSS v7.9.6.015 / Diagnostics 7.5.0.4**
---
### Method 1 — USB (no network needed, easiest)
1. Download the diagnostic `.bin` and agent `.img` files from the Extreme portal
2. Copy both files to a USB stick — ensure they are **not read-only**
3. Insert the USB stick into the front panel USB port on the switch
4. Via console or SSH (in enable mode):
```
download usb diag ers5900diag_7x_x_x_x.bin
```
Wait for it to complete and confirm, then:
```
download usb image ers5900_7x_x_x_x.img
```
The switch will reboot automatically after the agent upgrade.
Alternatively, from the **boot menu** (option 4 — "Download Agent/Diag") you can trigger a USB download without logging in first.
---
### Method 2 — SFTP over SSH (requires network)
From enable mode on the switch, with an SFTP server running on your management computer:
```
copy sftp address 192.168.99.50 filename ers5900diag_7x_x_x_x.bin
```
Wait for completion, then:
```
copy sftp address 192.168.99.50 filename ers5900_7x_x_x_x.img
```
The switch reboots after the agent upgrade.
---
### Method 3 — TFTP from a laptop (no dedicated server needed)
Your laptop can act as a temporary TFTP server. The switch has a default IP of **`192.168.1.1/24`** on VLAN 1 out of the box (or after a factory reset) — so on a fresh switch you can skip console IP configuration entirely. Just plug in Ethernet and go.
Plug both the console cable **and** an Ethernet cable from the laptop into the switch at the same time.
On the laptop:
```bash
sudo apt install tftpd-hpa
sudo cp ers5900diag_*.bin ers5900_*.img /srv/tftp/
sudo ip addr add 192.168.1.50/24 dev eth0 # must be on same /24 as switch default (192.168.1.x)
sudo systemctl start tftpd-hpa
```
Then on the switch via console (or via SSH to `192.168.1.1` if SSH is already enabled):
```
enable
copy tftp address 192.168.1.50 filename ers5900diag_7x_x_x_x.bin
```
Then:
```
copy tftp address 192.168.1.50 filename ers5900_7x_x_x_x.img
```
Stop the server when done:
```bash
sudo systemctl stop tftpd-hpa
```
> **No console needed on a factory switch:** The switch answers at `192.168.1.1` immediately after boot. As long as your laptop is on `192.168.1.x/24`, the TFTP transfer works without touching the console at all — useful if you only have an Ethernet cable and no console cable handy.
---
### Method 4 — XMODEM over console cable (last resort, no network required)
If the USB port is broken and you have no Ethernet available at all, boot menu option 4 ("Download Agent/Diag") supports XMODEM file transfer directly over the serial console cable. No network required.
**Warning:** At 9600 baud, a 10 MB firmware file takes approximately 3 hours. Only use this if nothing else is possible.
In `screen`, after selecting option 4 from the boot menu, send the file with:
```
Ctrl-A then :exec !! sx -b /path/to/ers5900_7x_x_x_x.img
```
(`sx` is part of the `lrzsz` package: `sudo apt install lrzsz`)
---
## Troubleshooting
**Connection banner stuck on "Connecting..."**
Backend not running or device not on VLAN 99. Check: `sudo systemctl status switch-manager` or `docker compose ps`
**"Switch unreachable" in status pill**
Backend running but cannot reach switch. Check: `ping 192.168.99.1` from the management computer.
**TOTP code rejected**
Ensure time is synchronised on both the management computer and your phone. Backend allows one 30-second window of clock drift. Sync: `sudo timedatectl set-ntp true`
**Command failed — switch error shown**
Read the error text — the switch says exactly what was wrong. Fix the configuration and push again. Already-succeeded commands do not need to be resent.
**Host key rejection after switch reset**
Re-pin: `ssh-keyscan -H 192.168.99.1 > /etc/switch-manager/known_hosts`
**Factory reset — wiping a previously configured switch**
> When to wipe: if the switch has unknown previous configuration, unknown passwords, or you want a guaranteed clean slate. If the switch already responds to `admin` with no password and has no unexpected VLANs or ACLs, a wipe is not required — you can configure over the existing state.
**Method 1 — Boot menu (easiest, works even if you don't know the password)**
The console cable must be plugged in before power-on. During boot a diagnostic menu appears briefly:
```
DIAGNOSTIC BREAK MENU
59100 GTS-PWR+ Diagnostics 7.5.0.4
1 - Launch Primary Agent-1 Vers: 7.9.6.015
2 - Launch Secondary Agent-2 Vers: 7.6.2.019
3 - Toggle Primary Agent Selection
4 - Download Agent/ Diag
5 - Reinitialize Agent Configuration Files
6 - Display Error Log
7 - Display System Information
8 - Continue Boot Sequence
9 - Reset
A - Power-Cycle
B - Toggle Do-POST Selection [ ENABLED ]
C - Run POST tests
Select:
Booting Agent in 60 seconds...
```
Press **5** — "Reinitialize Agent Configuration Files". The switch wipes its config and reboots into factory defaults. You have 60 seconds before it boots automatically.
Other useful options: **1/2** switch between primary (v7.9.6) and secondary (v7.6.2) firmware. **6** shows the error log. **7** shows system info. **8** continues normal boot if you entered the menu by accident. **9** resets (reboots). **A** power-cycles. **B** toggles POST (hardware self-test) on/off. **C** runs POST tests manually.
**Method 2 — CLI (requires working login)**
```
enable
boot config flags factory
save config
boot
```
After either method the switch comes up with blank config, default credentials (`admin` / no password), and management IP `192.168.1.1` on VLAN 1. Continue from Step 5.
**ctrld not filtering DNS**
Check DHCP option 6 is set to the ctrld machine's IP on each VLAN pool. Check ctrld is running: `ctrld status`. Check the switch is handing out the right DNS: from a device, run `nslookup example.com` and verify the server IP matches.
**WireGuard not connecting**
Check port 51820 UDP is reachable from outside your network (router port forwarding may be needed for external access). Check: `sudo systemctl status wg-quick@wg0`
**Service logs**
```bash
# Native
journalctl -u switch-manager -f
# Docker
docker compose logs -f
docker compose ps
```
---
## Command Reference — Tested Status
Commands the script sends to the switch, with known test status. Use this as a guide when debugging unexpected behaviour — untested commands may behave differently on your firmware version.
### Read commands
| Command | Status | Notes |
|---|---|---|
| `show interfaces` | confirmed | |
| `show poe-main-status` | confirmed | |
| `show vlan` | confirmed | |
| `show sys-info` | confirmed | |
| `show arp` | confirmed | |
| `show config` | confirmed | Returns table output |
| `show ip helper-address` | not tested | |
| `show ip route default` | not tested | |
| `show dhcp-server leases` | invalid on this firmware | Script handles gracefully — returns empty |
| `show dhcp-server static-binding` | invalid on this firmware | Script handles gracefully — returns empty |
| `show dhcp-server` | invalid on this firmware | Script handles gracefully — returns empty |
### Config / push commands
| Command | Status | Notes |
|---|---|---|
| `enable` | confirmed | |
| `configure terminal` | confirmed | |
| `terminal length 0` | not tested | Disables pagination — sent before reads |
| `interface FastEthernet {port}` | confirmed | |
| `vlan members add {vid} {port}` | confirmed | |
| `vlan pvid {port} {vid}` | not tested | Sets native/untagged VLAN on a port |
| `vlan tagging {tagged-set} {port}` | not tested | Adds trunk tagging |
| `vlan create {vid} name "x" type port` | not tested | |
| `no vlan {vid}` | not tested | |
| `name "{description}"` (under interface) | not tested | Sets port description |
| `shutdown` (under interface) | not tested | Disables a port |
| `poe enable` / `no poe enable` | not tested | |
| `poe poe-limit {milliwatts}` | not tested | |
| `interface vlan {vid}` (in config mode) | not tested | |
| `ip access-list extended {name}` | not tested | |
| `ip access-group {name} in/out` | not tested | |
| `end` | not tested | Returns to enable mode |
| `save config` | confirmed | **Caused a reboot on first run** — watch the first time you push this |
> **`save config` reboot note:** On at least one switch, issuing `save config` triggered a reboot. This may be firmware-version-specific or a one-time behaviour after certain config states. Subsequent saves have not reproduced it. Be aware when pushing config changes in a live environment.
---
## Scrollback in `screen` (console cable sessions)
By default `screen` does not let you scroll up through output. Enable it with copy mode:
| Action | Keys |
|---|---|
| Enter scrollback mode | `Ctrl-A` then `[` |
| Scroll up / down | Arrow keys or `PgUp` / `PgDn` |
| Exit scrollback mode | `Esc` or `q` |
To increase the scrollback buffer for a session (default is only 100 lines):
```
Ctrl-A then :scrollback 10000
```
To set it permanently, add this to `~/.screenrc`:
```
defscrollback 10000
```
Useful when reviewing long `show config` or `show interfaces` output during console sessions.
---
## What This Tool Does Not Do
- Does not manage UniFi, pfSense, or non-OPNsense routers (OPNsense is the supported router platform)
- Does not provide a terminal or shell — there is no way to type arbitrary commands through the main interface (CLI mode in settings is available for advanced users but still runs through the safety pipeline)
- Does not support multiple switches simultaneously
- Does not provide deep traffic analytics or bandwidth graphs (PoE budget and port status are available)
- Does not automatically discover or adopt new network devices
---
## On the ERS Switch Family
These switches have no REST API. Everything this tool does is via SSH sessions that parse text output and send CLI commands — the same thing a human would do at a terminal, automated and wrapped in a browser interface.
There is an inherent limit to how reliably the tool can detect every possible error condition. The danger blocking system catches the known lethal patterns but cannot anticipate every possible misconfiguration. Use the CLI review step. Read what is about to be sent.
The console cable is always your fallback. Keep it accessible.
---
## DNS Filtering — Port 53 Conflict Resolution
When installing ctrld (Option A — local install), ctrld needs to bind port 53. On Ubuntu and Debian, `systemd-resolved` holds port 53 via its stub listener.
**The tool fixes this automatically** during installation. It adds `DNSStubListener=no` to `/etc/systemd/resolved.conf` and restarts `systemd-resolved`. The service itself keeps running — it still manages `/etc/resolv.conf` and local hostname caching. Only the stub listener is disabled.
If the automatic fix fails (permission issue, non-standard config), fix it manually:
```bash
echo "[Resolve]" | sudo tee -a /etc/systemd/resolved.conf
echo "DNSStubListener=no" | sudo tee -a /etc/systemd/resolved.conf
sudo systemctl restart systemd-resolved
```
**On OPNsense (Option B):** OPNsense runs Unbound DNS on port 53. The correct approach is not to uninstall Unbound — it handles `.lan` hostnames and local DNS. Instead:
1. In OPNsense UI: Services → Unbound DNS → General → set Listen Port to `5353`, Listen Interface to `Loopback (lo0)`. Save + Apply.
2. Configure ctrld to forward `*.lan` and `*.local` to `127.0.0.1:5353` (the split-horizon block shown in the DNS tab result panel).
3. ctrld handles all other queries via DoH3 to Control D.
This keeps local names working while all external DNS is filtered per-VLAN through Control D.
---
## DNS Enforcement ACLs
Without enforcement, a device can ignore DHCP-assigned DNS and use `8.8.8.8` directly, bypassing all ctrld filtering.
The DNS tab has an **"Enforce DNS on Switch"** button that generates ACLs blocking this. For each VLAN:
```
ip access-list extended DNS-ENFORCE-VLAN10
1 permit udp 192.168.10.0 0.0.0.255 host [ctrld-ip] eq 53
2 permit tcp 192.168.10.0 0.0.0.255 host [ctrld-ip] eq 53
3 deny udp 192.168.10.0 0.0.0.255 any eq 53
4 deny tcp 192.168.10.0 0.0.0.255 any eq 53
5 deny tcp 192.168.10.0 0.0.0.255 any eq 853
6 permit ip any any
interface vlan 10
ip access-group DNS-ENFORCE-VLAN10 in
```
Rules 12 permit DNS only to ctrld. Rules 34 block DNS anywhere else (8.8.8.8, Cloudflare, etc.). Rule 5 blocks DNS-over-TLS (port 853) as another bypass path. Rule 6 permits all other traffic so internet still works.
VLAN 99 (management) is automatically excluded — a broken ACL on the management VLAN would lock you out.
The generated commands are shown for review and pushed through the normal TOTP-gated push mechanism. Nothing is sent to the switch automatically.
---
## Inter-VLAN Routing ACL Templates
The **ACL Builder** tab has a **"Use Template"** button that pre-fills common policies:
**Staff VLAN — full internet, no management access**
Permits everything except access to VLAN 99 (192.168.99.0/24). Use on a staff or office VLAN where users need full internet but must not reach the management interface.
**IoT VLAN — internet only, no RFC1918**
Blocks all RFC1918 private address ranges (192.168.x.x, 10.x.x.x, 172.16-31.x.x). IoT devices get internet but cannot reach any other VLAN, internal servers, or management. Permits internet.
**Guest VLAN — internet only, DNS must work first**
Like IoT but explicitly permits DNS to ctrld first (before the deny rules), ensuring DNS filtering continues to work even after RFC1918 is blocked.
**Camera VLAN — NVR only**
Cameras may only send traffic to one NVR/DVR IP. All other traffic is dropped. Prevents cameras from phoning home, scanning the network, or accessing the internet directly.
All templates are fully editable after applying. The template fills the rule table — you adjust IPs, add rules, or delete rules before pushing.
---
## Local Hostname Resolution (dnsmasq)
The DNS tab has a **"Local Hostnames"** section. It manages an optional `dnsmasq` container that resolves `.lan` hostnames for all devices on the network.
**How it works:**
1. dnsmasq runs in Docker, listening on port 5353 on the management computer.
2. ctrld is configured to forward `*.lan` and `*.local` queries to `127.0.0.1:5353` (split-horizon rule).
3. All other queries go through Control D as normal.
4. `switch.mgmt.lan` and `management.lan` always resolve to the management computer's IP.
**Setup:**
Add hostname→IP mappings in the DNS tab → Local Hostnames section. Click **Save & Generate dnsmasq.conf**. The tab shows:
- The generated `dnsmasq.conf` content and path
- A docker-compose snippet to add the dnsmasq service
- A ctrld.toml block to enable split-horizon forwarding
Add the docker-compose snippet to `docker-compose.yml`, add the toml block to `ctrld.toml`, then:
```bash
docker compose up -d dnsmasq
ctrld restart
```
The `dnsmasq.conf` is written to `/etc/switch-manager/dnsmasq.conf` and mounted read-only into the container.
**Files added:**
| File | Location | Purpose |
|---|---|---|
| `local-hostnames.json` | `/etc/switch-manager/` | User-defined hostname→IP mappings |
| `dnsmasq.conf` | `/etc/switch-manager/` | Generated dnsmasq config |