Skip to main content

max / alloy

4.3 KB · 83 lines History Blame Raw
1 # The installer, reachable over ssh on the installer medium.
2 #
3 # The headless flow (wiki `alloy-image-minting`, GO alloy 1fc19a8d): a machine
4 # with no screen boots the ISO, and the operator continues the install from
5 # another machine with `ssh installer@<name>.local`. The credential is the
6 # public key baked into the medium at mint time by `alloy image` — see the
7 # identity block in the Containerfile. No password auth, no one-time code on a
8 # console the machine does not have, no window in which a box on the LAN is
9 # takeoverable.
10 #
11 # THE GATE IS THE ACCOUNT, NOT THIS FILE.
12 #
13 # `alloy install` writes disks. Reaching it over ssh must be impossible on an
14 # installed machine, and the flag that says which machine this is lives on the
15 # kernel command line, which sshd_config cannot read: `Match` takes User,
16 # Group, Host, LocalAddress, LocalPort, RDomain and Address, and nothing else.
17 # (`Match exec` is ssh_config, the client side. It is not available here.)
18 #
19 # So this file ships everywhere and does nothing on its own. The `installer`
20 # account it names is created by alloy-installer-ssh.service, which carries the
21 # same `ConditionKernelCommandLine=alloy.installer` as alloy-installer.service
22 # and so runs only on the live medium. On an installed system there is no such
23 # user, `Match User installer` matches nothing, and the block below is unread.
24 # That direction matters: the failure mode of the unit not running is a login
25 # that does not happen, rather than one that should not have.
26 #
27 # It is also why nothing here is written at boot. Copying a drop-in into
28 # /etc/ssh at runtime would put the gate in a race with sshd's own start, and
29 # losing that race fails open.
30
31 Match User installer
32
33 # The baked key, at an absolute path. The account has no home directory to
34 # hold a `~/.ssh`, and giving it one would mean the credential lived somewhere
35 # a half-finished install could have written to.
36 AuthorizedKeysFile /usr/lib/alloy/authorized_keys
37
38 # The session IS the installer. Not a shell that offers to start it: whatever
39 # the client asked to run is replaced by this, so there is no command line to
40 # get wrong and no fallback to a prompt.
41 #
42 # Its exit status is the session's exit status, and there is no retry here or
43 # in the unit. A crashed installer has left a disk in an unknown state, and the
44 # next person to connect should meet a machine that says so rather than a fresh
45 # wizard offering to go over it again. That is the same ruling
46 # alloy-installer.service records for tty1 in its `Restart=no`.
47 #
48 # UNDER `run0`, AND WITHOUT IT THIS DOOR OPENS ONTO NOTHING. `alloy install`
49 # does not escalate: on tty1 it does not need to, because
50 # alloy-installer.service runs it as root, and here it would land in the
51 # unprivileged account this file matches. Measured 2026-08-26 (GO alloy
52 # 2cf04f20): the wizard drew and answered all six steps and the erase
53 # confirmation, then failed on `Querying root privilege` and every command
54 # after it. The whole headless flow reached a wizard that could not write a
55 # disk.
56 #
57 # run0 rather than sudo, per wiki `alloy-privilege`, and the grant it needs is
58 # usr/share/polkit-1/rules.d/50-alloy-installer.rules — one action, one user,
59 # and inert wherever this file is inert, because both hang off an account that
60 # only the live medium creates.
61 #
62 # `--background=` empty on purpose. run0 tints the terminal to signal a
63 # privileged session, which is right for a shell and wrong in front of a
64 # full-screen TUI that paints its own surface from the theme. The cue this
65 # session needs is the one the installer draws itself.
66 ForceCommand run0 --background= /usr/bin/alloy install
67
68 # A TUI needs a terminal. ForceCommand alone does not allocate one, and the
69 # installer would draw into a pipe and be unusable.
70 PermitTTY yes
71
72 # Back to global scope, and this line is load-bearing rather than tidy.
73 #
74 # sshd applies a `Match` to everything that follows it, and `Include` does not
75 # end that scope: /etc/ssh/sshd_config puts its
76 # `Include /etc/ssh/sshd_config.d/*.conf` at line 15 and then sets keywords for
77 # another two hundred. Without this, every one of those — and every drop-in
78 # sorting after this one, which is most of Fedora's — would be read as part of
79 # the block above and apply to the installer account alone. The symptom is not
80 # a parse error; it is a machine whose global ssh policy quietly stopped
81 # existing.
82 Match all
83