Wheelhouse

Security#

The summary, for the person who has to sign off on putting this at an edge. The full treatment is in the project's own security documentation (docs/security.md) and the wiki mirrors it.

Nothing here says "secure". It describes mechanisms and lets you judge them.

What is listening, on a router that just finished installing#

PortServiceBound toReachable from
8443/tcpthe Wheelhouse agent: web interface and API0.0.0.0every interface, including the WANanything the firewall lets through
443/tcpthe platform's own HTTPS API127.0.0.1 onlythe agent, and root on the box
53/udp, tcpDNS forwarderthe LAN addressthe LAN subnet only
67/udpDHCP serverthe LAN interfacethe LAN
123/udpNTP client and serverloopback, link-local and RFC 1918 ranges
22/tcpSSHnot configurednothing

Four of those deserve saying out loud.

The agent binds every interface. The firewall is the only thing keeping the management interface off the internet, and there is no listen-address setting short of editing the systemd unit — which an image upgrade replaces.

The platform's own API is pinned to loopback on first boot. The first-boot unit mints a random API key for the agent, enables the REST endpoints and binds them to 127.0.0.1. The router's API is not a network service on a Wheelhouse box; the agent is the management surface.

SSH is off. Neither the image's default configuration nor the installer writes service ssh, so a freshly installed router has no SSH at all and the console is the only shell. Turn it on deliberately, restrict it, and prefer keys.

NTP is a client and a server, answering only loopback, link-local and RFC 1918. It does not answer the WAN.

Five routes answer without authentication#

GET /health (which returns {"status":"ok"} and deliberately not the version), GET /api/auth/status, GET /api/oidc/status, the two OIDC redirect legs, and POST /api/auth/login behind all three rate limiters. Everything else — every read, every write, and /metrics unless you pass --metrics-public — requires a principal.

What the installer's firewall checkbox writes#

Whenever the box is left ticked and there is a WAN — it does not need a LAN, because a router with a WAN and no LAN is the machine that most needs a closed front door:

The shape of it
state policy   established accept, related accept, invalid drop
ipv4 input     default drop; accept from the LAN, from lo, ICMP,
               the DHCP client's return traffic on the WAN,
               and a management source if you named one
ipv4 forward   default drop; accept inbound from the LAN
ipv6 input     default drop; the same, plus ICMPv6 and DHCPv6
ipv6 forward   default drop; accept inbound from the LAN, ICMPv6 through

Both address families are filtered. A box filtered on one and open on the other is not filtered, and a provider can bring IPv6 up without being asked. If the image has no firewall ipv6 node the installer says so at the end rather than silently committing a v4-only ruleset.

ICMP is accepted on every interface, WAN included. On IPv4 that is a choice you can narrow. On IPv6 it is not a choice: neighbour discovery and path-MTU discovery are ICMPv6.

Nothing in it opens 8443 from the WAN.

Turning it off takes a deliberate answer. Unticking the box with a WAN present raises a screen saying the router will accept everything arriving from the internet, and its default answer is Turn it on.

Check what you actually have on any box you did not install yourself:

show configuration commands | match firewall

An empty answer on a router with a WAN is an emergency, not a preference.

Accounts#

Argon2id at 64 MiB and three passes, TOTP verified with a live code before it is switched on, three roles enforced server-side on every route, API tokens carrying a role and an expiry and stored only as a hash, opaque HttpOnly SameSite=Strict session cookies with a CSRF token echoed on every mutation, three separate login rate limiters, and OIDC single sign-on linked by the provider's subject claim rather than by email.

The whole of it, including what it does not do — no recovery codes, no administrator reset of a second factor, no per-object permissions — is on the access-control page.

What is not defended against#

Stated as plainly as the rest.

  • Root on the router. Root reads every secret, edits the state file and rewrites the audit log with a text editor. There is no tamper-evidence and no off-box copy.
  • Physical access. No disk encryption. Secrets are files on an unencrypted partition, and the console is a login prompt in front of a bootloader you can edit.
  • A compromised browser or administrator workstation. A live session is a live session.
  • Supply chain. Checksums are published; no release is signed, because no signing key exists, and there is nowhere yet to fetch a public key that would make a signature mean anything.
  • Denial of service at line rate. The agent caps request bodies and rate limits logins. It is a management plane, not a scrubbing centre.

Assumed trustworthy: your identity provider if you configure single sign-on, your container registry when you install an app, and the licence server for the one thing it does.

The licence is not a security control#

An unlicensed agent refuses to change configuration. It still authenticates and still serves every read. It is a commercial gate on a self-hosted binary — do not reason about it as if it protected anything.

Getting fixes#

Where the problem livesHow the fix reaches you
The agent or the interfaceA new agent package, or the next image
The kernel, the base, FRR, Kea, nftables, strongSwan, OpenVPN, PodmanA new image built from a newer vyos-build. There is no in-place package update path
A container you installedThe app's own update action; the image comes from its registry, not from us

Reporting a vulnerability#

The policy is in SECURITY.md: a three-business-day acknowledgement, a severity judgement within ten, a fix or a dated plan within ninety, credit in the changelog if you want it, and safe harbour for good-faith research against your own installation.

There is no bug bounty. There is credit, a reply from a person who read the report, and a fix.

Read on#

Updated 2026-09-02