Adopt a host¶
nerthus-config host adopt is how a rented machine becomes a host of the estate. It is one of the three times the tool reaches a host over SSH; after it, the host's own agent pulls its declaration every five minutes and nothing pushes to it again. This page is the procedure, from a provider's delivery mail to a merged declaration.
What adopt does, in order¶
- Confirms the host key before anything else is sent. The key must be the one the provider console shows: the host's row in
inventory/hosts.yamlnames it (ssh_host_key_sha256), or you pass it as--fingerprint, or you confirm it at the prompt. A host presenting any other key is not even logged into. - Reads the host: its OS family and release against the declaration, its architecture, and every key in root's and
nerthus-admin'sauthorized_keys. A key that is neither yours nor declared in the row stops the adopt here, before any write, and is printed with its fingerprint and comment. - Installs the agent the declaration pins (
pins/agent.yaml), checking its SHA-256 on the host. - Converges the adopt cookbook: git, an SSH client and sudo; the admin account
nerthus-adminwith your key and no password; root's initial password discarded; password logins off; the agent's configuration. It then plans the cookbook again and refuses unless that second plan changes nothing. - Runs the agent's
init, which mints the host's age identity and the SSH key it readsNerthus.Infrawith, both on the host. Only their public halves come back. - Reads everything back: the authorized keys by fingerprint,
sshd -T, root's password field, the agent's ownverify. - Declares the host on your checkout's branch: the row gains the confirmed key, the identity,
ssh_admin_user: nerthus-adminand the keys that may log in; a read-only deploy key onnerthus.infracarries the read key; asops_recipientgives the hostvault/hosts/<host>/; the console password row moves toretired:and its file is deleted.validatejudges the result, and edits that add a finding are undone.
The provider's console password is gone after this. Root has no password, so the console's password prompt no longer opens the machine; recovering from a broken sshd is a reinstall at the provider. Everything on the host can be rebuilt from Nerthus.Infra.
Before you start¶
- The host key, read at the provider console. Log in there with the delivered password and run
ssh-keygen -lf /etc/ssh/ssh_host_ed25519_key.pub. What it prints is the value you confirm; what the network answers is not evidence. - Your key on root. The delivery comes with a password; put your public key into
/root/.ssh/authorized_keys(the provider's panel, or onessh-copy-idover that password) and prove it withssh -o PasswordAuthentication=no root@<address> true. - The pinned agent. Download the file
pins/agent.yamlnames fromnerthus.config's package registry (nerthus-config-agent/<version>/nerthus-config-agent-<version>-x86_64-unknown-linux-musl). Adopt refuses a file whose SHA-256 differs. - A
Nerthus.Infracheckout on a task branch, cut fromorigin/main. Adopt commits there and refusesmain.
The procedure¶
Dry run first. It confirms the key, reads the host and prints the cookbook's plan, and changes nothing:
cd ~/gitlab/margonem/nerthus/it/Nerthus.Infra
nerthus-config host adopt nerthus-host-2 --agent ~/Downloads/nerthus-config-agent-0.7.0-x86_64-unknown-linux-musl
A host that is not declared yet takes its facts as flags, and gets a row with no roles:
nerthus-config host adopt nerthus-host-11 --address 203.0.113.7 --family debian --release 13 \
--provider instantnode --console-credential NERTHUS_HOST_11_CONSOLE_PASSWORD --agent …
Then the adopt itself:
It pushes the branch and opens the merge request (--no-push keeps it local). After the merge, nerthus-config apply . --from laptop --only gitlab_deploy_key creates the host's read key at GitLab; until then the agent's tick refuses with fetch.failed, and that is expected.
How you know it worked¶
On the host, journalctl -u nerthus-config-agent (Alpine: /var/log/messages) shows a tick every five minutes ending in plan: empty (no role is declared) and converged <commit>, and /var/lib/nerthus/facts.yaml exists. A second host adopt of the same host refuses: its row carries an identity.
When the agent refuses¶
The agent exits 0 on an ordinary refusal and names the cause:
| Cause | What it means |
|---|---|
fetch.failed |
the read key is not at GitLab yet, or the network is down |
host.unknown |
the host's row is not on main — the merge request is not merged |
pin.backwards |
pins/agent.yaml names an agent older than the one the host converged with; a rollback ships as a higher version |
agent.behind |
the pin names a newer agent; host adopt <host> --reinstall --agent <file> --no-dry-run installs it |
secrets.not-tmpfs |
the secrets directory is not on tmpfs, and a decrypted secret is written nowhere else |
It exits non-zero only on a broken installation: no configuration, or an identity that anyone but root can read. The timer's unit refuses to start in that case, and says why in the journal.
Replacing a host's identity¶
nerthus-config host rekey <host> --identity <recovery identity file> --no-dry-run mints a new identity and read key on the host, re-encrypts vault/hosts/<host>/ to them with the recovery identity, and declares both. Use it when a host's disk may have been read by somebody else.