Problem
Each bootable profile is its own btrfs root subvolume with its own /etc, so anything the user
sets up during onboarding lands in exactly one profile. Every other profile stays at factory
defaults, and every profile is offered in the boot menu. The weakest profile therefore decides
the security of the device: hardening @Desktop means nothing if @Minimal still accepts
user/user, and /home is shared, so a login to the weak profile reaches the same data.
We should decide, before onboarding ships, which parts of "who this device belongs to" are
per-profile and which follow the device, and how the ones that follow the device get there.
What is shared and what is not
| Path |
Backing |
Consequence |
/home |
shared @home subvolume |
user data and ~/.ssh are already device-wide |
/var/log, /var/cache, /boot |
shared subvolumes |
not identity, listed for completeness |
/etc |
per-profile |
shadow, passwd, group, ssh, sudoers.d, NetworkManager connections all diverge |
/root |
per-profile |
root's authorized_keys diverges |
/var/lib |
per-profile |
service state, for example tailscale, diverges |
Observed drift on a device where one profile had been set up:
testuser, added in @Desktop, is present in @Desktop and in the profile migrated from it,
and absent in @Minimal, @Router, @TV-Media-Box, @No-Graphics.
- The
user account still carries the build's factory hash in all five factory profiles.
- NetworkManager connections are per-profile, so a network joined in one profile does not exist
in the others.
- SSH host keys currently happen to match across profiles, but only because the build bakes one
key set into the image (see "Related" below), not because anything propagates them.
What counts as identity, and where it lives today
| Item |
Location |
Today |
| User password hashes, added accounts, group membership |
/etc/{shadow,passwd,group,gshadow} |
per-profile |
| User SSH authorized keys |
/home/<user>/.ssh |
already shared |
| Root SSH authorized keys |
/root/.ssh |
per-profile |
| SSH host keys (the device's identity to clients) |
/etc/ssh/ssh_host_* |
per-profile, baked at build |
| Network credentials |
/etc/NetworkManager/system-connections |
per-profile |
| sudo policy |
/etc/sudoers.d |
per-profile |
| Hostname, machine-id |
/etc/machine-id, generated banner |
deliberately per-profile |
| Timezone, locale, keyboard |
/etc/localtime, /etc/default/keyboard |
per-profile |
| User-added apt sources and keyrings |
/etc/apt, /usr/share/keyrings |
per-profile |
Decisions we need
- Which of the above should follow the device, and which should legitimately stay
per-profile? machine-id is intentionally per-profile so a profile first-boots and takes
its own hostname. Host keys are the opposite: they identify the device, not the profile.
- Should onboarding push to the other profiles at the moment it completes, or should each
profile pull on its first boot from a shared store?
- What happens to profiles that appear after onboarding, whether from
create-profile, from
receive-snapshot of a fresh build, or from a future image update?
- Is leaving factory
user/user valid on a non-onboarded profile acceptable at all, or should a
profile that has never been onboarded refuse logins until it is?
Options
A. Push at onboarding completion. Onboarding mounts the top level and writes the chosen
credentials into every profile's /etc. Direct and offline, and it can reuse the per-entry
account merge already written for migrate-profile rather than overwriting databases wholesale.
Downside: it edits other roots behind their back, and it does nothing for profiles created later,
so it needs pairing with option C.
B. Share the files. Move the device-wide items to a shared subvolume and have each profile
reference them, by symlink or bind mount. Nothing to propagate, since there is one copy. Good fit
for /etc/ssh/ssh_host_* and root's authorized_keys. A poor fit for /etc/shadow, because
profiles legitimately differ in service accounts, which is exactly why migrate-profile merges
those databases per entry instead of copying them.
C. Pull on first boot. Onboarding writes a device identity record to a shared location, and a
first-boot unit in each profile applies whatever is present. Covers profiles created later for
free, including ones received from a build, and composes with create-profile already resetting
machine-id so a new profile first-boots. Downside: an identity store on disk needs care, since
it holds hashes and keys.
D. Do nothing and document it. Tell users that each profile has its own credentials. Cheapest,
and defensible for a developer board, but it leaves a default-credential root in the boot menu of
an otherwise hardened device.
Suggested direction
C for the general mechanism, plus B for the SSH host keys specifically, since those have no reason
to differ per profile and sharing them removes a class of host-key warnings entirely. A as the
immediate action at the end of onboarding, so the profiles that already exist are fixed at once
rather than at their next boot.
Interaction with the existing tooling
create-profile snapshots its source, so a profile cloned from an onboarded profile inherits
its credentials already. The gap is profiles built from a _stock, which carry factory
defaults.
migrate-profile merges account databases per entry, carries usr/local and keyrings, and
since f99f26a carries /etc/ssh/ssh_host_* explicitly, because the baked-in keys never appear
in its delta. Whatever we choose here should reuse those semantics rather than invent new ones.
receive-snapshot lands a _stock from a build, which by definition has never been onboarded.
Related
The build bakes one SSH host key set into every image, so all devices flashed from a given build
share a host identity, and reflashing changes it. That is worth a separate issue, and option B
above would make it moot for profile-to-profile drift but not for device-to-device sharing.
@alchark @zhovner
Problem
Each bootable profile is its own btrfs root subvolume with its own
/etc, so anything the usersets up during onboarding lands in exactly one profile. Every other profile stays at factory
defaults, and every profile is offered in the boot menu. The weakest profile therefore decides
the security of the device: hardening
@Desktopmeans nothing if@Minimalstill acceptsuser/user, and/homeis shared, so a login to the weak profile reaches the same data.We should decide, before onboarding ships, which parts of "who this device belongs to" are
per-profile and which follow the device, and how the ones that follow the device get there.
What is shared and what is not
/home@homesubvolume~/.sshare already device-wide/var/log,/var/cache,/boot/etcshadow,passwd,group,ssh,sudoers.d, NetworkManager connections all diverge/rootauthorized_keysdiverges/var/libObserved drift on a device where one profile had been set up:
testuser, added in@Desktop, is present in@Desktopand in the profile migrated from it,and absent in
@Minimal,@Router,@TV-Media-Box,@No-Graphics.useraccount still carries the build's factory hash in all five factory profiles.in the others.
key set into the image (see "Related" below), not because anything propagates them.
What counts as identity, and where it lives today
/etc/{shadow,passwd,group,gshadow}/home/<user>/.ssh/root/.ssh/etc/ssh/ssh_host_*/etc/NetworkManager/system-connections/etc/sudoers.d/etc/machine-id, generated banner/etc/localtime,/etc/default/keyboard/etc/apt,/usr/share/keyringsDecisions we need
per-profile?
machine-idis intentionally per-profile so a profile first-boots and takesits own hostname. Host keys are the opposite: they identify the device, not the profile.
profile pull on its first boot from a shared store?
create-profile, fromreceive-snapshotof a fresh build, or from a future image update?user/uservalid on a non-onboarded profile acceptable at all, or should aprofile that has never been onboarded refuse logins until it is?
Options
A. Push at onboarding completion. Onboarding mounts the top level and writes the chosen
credentials into every profile's
/etc. Direct and offline, and it can reuse the per-entryaccount merge already written for
migrate-profilerather than overwriting databases wholesale.Downside: it edits other roots behind their back, and it does nothing for profiles created later,
so it needs pairing with option C.
B. Share the files. Move the device-wide items to a shared subvolume and have each profile
reference them, by symlink or bind mount. Nothing to propagate, since there is one copy. Good fit
for
/etc/ssh/ssh_host_*and root'sauthorized_keys. A poor fit for/etc/shadow, becauseprofiles legitimately differ in service accounts, which is exactly why
migrate-profilemergesthose databases per entry instead of copying them.
C. Pull on first boot. Onboarding writes a device identity record to a shared location, and a
first-boot unit in each profile applies whatever is present. Covers profiles created later for
free, including ones received from a build, and composes with
create-profilealready resettingmachine-idso a new profile first-boots. Downside: an identity store on disk needs care, sinceit holds hashes and keys.
D. Do nothing and document it. Tell users that each profile has its own credentials. Cheapest,
and defensible for a developer board, but it leaves a default-credential root in the boot menu of
an otherwise hardened device.
Suggested direction
C for the general mechanism, plus B for the SSH host keys specifically, since those have no reason
to differ per profile and sharing them removes a class of host-key warnings entirely. A as the
immediate action at the end of onboarding, so the profiles that already exist are fixed at once
rather than at their next boot.
Interaction with the existing tooling
create-profilesnapshots its source, so a profile cloned from an onboarded profile inheritsits credentials already. The gap is profiles built from a
_stock, which carry factorydefaults.
migrate-profilemerges account databases per entry, carriesusr/localand keyrings, andsince
f99f26acarries/etc/ssh/ssh_host_*explicitly, because the baked-in keys never appearin its delta. Whatever we choose here should reuse those semantics rather than invent new ones.
receive-snapshotlands a_stockfrom a build, which by definition has never been onboarded.Related
The build bakes one SSH host key set into every image, so all devices flashed from a given build
share a host identity, and reflashing changes it. That is worth a separate issue, and option B
above would make it moot for profile-to-profile drift but not for device-to-device sharing.
@alchark @zhovner