Decision 0095
95. A password is asked at installation, and the genome says where its hash is
Architecture decision record 0095: 95. A password is asked at installation, and the genome says where its hash is
Refines ADR-0041 · ADR-0036 · ADR-0007
Context
Every machine this installer wrote had the same password. owner.nix carried
initialHashedPassword for tacid, and that hash is published in genesis.rs. The owner is in
wheel, so it was also the password for sudo.
The comment beside it said to change it on the first boot. That could not last. The root is made
again at every boot (ADR-0036), /etc/shadow
is not kept, and users.mutableUsers was never set. passwd changed a file that the next boot
threw away. The password that opened the console, the greeter and sudo on every Tacid machine
was therefore the one in the source, at every boot, whatever its owner had done about it.
SSH was not affected, because password logins are off wherever sshd runs. Everything else was.
Decision
The owner’s password is asked at installation, hashed where it was typed, and kept on the
machine’s /persist. The genome names the file and never carries the hash.
tacid install --yesat a terminal asks twice, with nothing echoed.--password-fileis for scripts and tests. With neither, the installation does not go ahead. The passphrase and the TPM PIN are asked the same way when their files are not given.- The CLI hashes the password with SHA-512 crypt (RustCrypto’s
sha-crypt). What crosses the keeper’s socket is the$6$string. The keeper scrubs it from everything it writes, as it does the passphrase. - A new guarded stem operation,
PasswordWrite { mount, user, hash }, writes/persist/etc/tacid-passwords/<user>. The directory is mode 0700 and the file 0600, both root’s. The stem checks the hash’s shape independently of the keeper: no:, no line break, and one of$6$,$y$,$7$or$2b$. owner.nixsayshashedPasswordFile = "/persist/etc/tacid-passwords/<user>". Every boot rebuilds/etc/shadowfrom that file, because every boot starts with no/etc/shadowto keep.tacid passwordchanges it later. The same operation runs withmount = "/", thenchpasswd --encryptedapplies it now, with the line on standard input. Only the owner may do this, and only for their own account.
Consequences
- A genome can be published, shared or carried to another machine without carrying anything anyone can try passwords against.
passwdlasted only until the next reboot when this was written. ADR-0098 carries a change made with it to/persist.tacid passwordchanges both at once.- The hash survives a reinstall of the genome only if
/persistdoes. A genome carried to a new machine brings the file’s name and not its contents. The new machine’s installation asks again, which is the right moment to ask. - The reference hosts keep the published
tacid. They are machines somebody boots from this tree to look at, not machines this code installs for a person. - The stem’s vocabulary grows by one guarded operation and one tool (
chpasswd, from shadow, on every NixOS machine). Neither carries a command line from a caller.
Alternatives rejected
- The hash in
owner.nix. Simplest, and it puts an offline-crackable hash into a repository that is committed, read by people, and meant to travel. Changing it would also mean editing the one file Tacid never writes and signing the constitution again. - Keep
tacid, force a change at first login, and persist/etc/shadow. It keeps a window in which every new machine has a published password. It also binds a file that NixOS’s activation rewrites, which is the same mistake this tree has already made with/etc/ssh. users.mutableUsers = false. It changes nothing here, because there is no/etc/shadowto keep. It would also makepasswdfail outright rather than last until a reboot.