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#
| Port | Service | Bound to | Reachable from |
|---|---|---|---|
| 8443/tcp | the Wheelhouse agent: web interface and API | 0.0.0.0 — every interface, including the WAN | anything the firewall lets through |
| 443/tcp | the platform's own HTTPS API | 127.0.0.1 only | the agent, and root on the box |
| 53/udp, tcp | DNS forwarder | the LAN address | the LAN subnet only |
| 67/udp | DHCP server | the LAN interface | the LAN |
| 123/udp | NTP client and server | — | loopback, link-local and RFC 1918 ranges |
| 22/tcp | SSH | not configured | nothing |
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:
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 throughBoth 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 firewallAn 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 lives | How the fix reaches you |
|---|---|
| The agent or the interface | A new agent package, or the next image |
| The kernel, the base, FRR, Kea, nftables, strongSwan, OpenVPN, Podman | A new image built from a newer vyos-build. There is no in-place package update path |
| A container you installed | The 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#
- What is finished, and what is not — including the security gaps.
- Accounts and access control.
- The wiki: the security section and disclosure.