What first boot does#
wheelhouse-firstboot.service runs
/usr/lib/wheelhouse/firstboot.sh once per machine, as
root, before the agent starts. It does three things: mints the agent its own router API
key, pins the router's HTTP API to loopback, and — only if nobody else has — writes the
product's login banners. It is the reason a customer never types an API key.
The unit's condition is the key file itself:
ConditionPathExists=!/config/wheelhouse/api-keySo it runs until it has produced that file and then never again, including after an
image upgrade, because /config survives one.
In order#
- Wait for the router.
wait-for-vyos.shpollsvyos-router.serviceuntil itsSubStatereadsexited, which is the only reliable signal that the boot configuration has been loaded. Exits 1 if the router service failed, or after five minutes. - Stop if the key already exists. A non-empty
/config/wheelhouse/api-keymeans there is nothing to do. - Generate a key. 30 bytes from
/dev/urandom, base64, with/,+and=stripped, truncated to 40 characters. If fewer than 32 characters survive, the script fails rather than writing a short key. - Read the base version and the documentation directory, for the banner text below.
Commit three nodes in one configuration session:
set service https api keys id wheelhouse key <the generated key> set service https api rest set service https listen-address 127.0.0.1- Save, naming the file explicitly.
- Verify by reading the saved configuration back, not by trusting the exit status.
- Write the key to disk at
/config/wheelhouse/api-key, mode 0600. - Log it:
logger -t wheelhouse "first boot: API key created, HTTP API bound to loopback". - Optionally write the login banners, in a second commit.
Why each of the three nodes#
| Node | Why |
|---|---|
service https api keys id wheelhouse key … | The agent's own credential. Root-equivalent on this router, which is why the file it lands in is 0600 and why the API it opens is closed in the same commit. |
service https api rest | The REST endpoints are off until asked for. With only a key configured, the router's API answers 404 to everything — which is the single most confusing failure mode there is, because the key looks right and nothing works. |
service https listen-address 127.0.0.1 | The router's API stops being a network service. The agent, on the same box, is the management surface. |
See Ports and listeners.
Believe the file, not the exit status#
commit can answer 0 while refusing the change — it does exactly that during boot, with
"the configuration was not applied". So the script greps the saved configuration for
the key it just set, and refuses to write the key file if it is not there:
wheelhouse first boot: the API key did not reach the configuration; not writing itThat refusal is load-bearing. A failed run leaves /config/wheelhouse/api-key absent,
which leaves the unit's condition true, so the next boot tries again — rather than
starting an agent that holds a key the router never accepted.
The login banners#
Written only when nobody else has written any, checked before the configuration session opens:
grep -qE '^[[:space:]]*(pre-login|post-login) ' /config/config.bootA Wheelhouse OS image already carries banners from its image flavor, and an operator may have written their own; neither is overwritten. The case this covers is the package installed on a plain VyOS box, or an image built before the flavor carried them.
set system login banner pre-login "Wheelhouse\n"
set system login banner post-login "Wheelhouse\n\n Web UI https://[this router]:8443\n Licences <docs>\n\nBuilt on VyOS <base> (GPL). Corresponding source: <docs>\n"<docs> is /usr/share/doc/wheelhouse on an image and
/usr/share/doc/wheelhouse-agent when only the package is installed, picked at runtime
so two banners on one box never name different directories. <base> is VYOS_VERSION
from /etc/os-release, falling back to VERSION_ID when /etc/os-release is VyOS' own —
never /opt/vyatta/etc/version, which on a product image holds the product's version
and would credit the base with the wrong number. An empty value prints the credit
without a version rather than with a wrong one.
Without these nodes, VyOS renders its own template into /etc/motd: "Welcome to VyOS",
the VyOS logo and the upstream support portal, on the console and — through pam_motd —
over SSH. Setting the node replaces that template wholesale. The upstream credit stays,
on its own line, which is where it belongs.
This is a second commit, deliberately after the key is on disk: a failure here must not cost the router its agent. If it fails, the script says so and carries on.
The pre-login node is also what VyOS writes into /etc/issue.net, the file sshd
serves before authentication — which is why
the console banner script no longer writes that file
itself. The console banner carries the first-boot password and every interface address;
neither belongs in front of an unauthenticated SSH connection.
Two shell traps this script documents#
Worth knowing if you ever write a script against the router's CLI:
set -ecannot be used. VyOS' script template sources the CLI's completion setup, whoseshopt -p extglobreturns non-zero, and the script dies there.exitis an alias. Aftersource /opt/vyatta/etc/functions/script-template,exitexpands toeval $(vyatta_exit_configure)— it tears down the configuration session and returns; it does not end the script.exit 1is worse than useless: it passes a stray1to that eval, bash reports1: command not found, and execution carries straight on to the next line. Everywhere the script really means "stop" it saysbuiltin exit.
Checking it worked#
systemctl status wheelhouse-firstboot
journalctl -t wheelhouse
sudo ls -l /config/wheelhouse/api-key # -rw------- root root
show configuration commands | match 'service https'If the key file is missing and the unit has failed, the journal line names which of the two verifications did not pass.
See also#
- systemd units — the unit and its ordering.
- Ports and listeners — what the loopback binding buys.
- Files and directories —
api-keyand its mode. - The console banner — the other banner, and why they differ.
- Identifying the system — where
VYOS_VERSIONcomes from. - What the installer does
Checked against#
packaging/firstboot.sh,
packaging/wheelhouse-firstboot.service,
packaging/wait-for-vyos.sh,
packaging/iso/wheelhouse.toml,
packaging/console-banner.sh,
docs/security.md "The default posture".