Phase 402 — base users, groups, and shell policy
| Item | Value |
|---|---|
| Command | make phase 402 |
| Underlying make target/script | vm/phase4/materialize-etc.sh --accounts |
| Mutates disk/image? | Yes, it mounts artifacts/onix-image/onix.raw and updates the root filesystem |
| Boots QEMU? | No |
| Main proof | The image has a real account database policy and generated /etc/passwd, /etc/group, /etc/shadow, /etc/gshadow, /etc/nsswitch.conf, and /etc/shells. |
The basic Linux idea
Linux needs a way to answer simple identity questions:
Who is UID 0?
What group is GID 0?
Which users exist?
Which groups exist?
Which shell should a user get after login?
The old and still-important answer is a set of files under /etc:
/etc/passwd
/etc/group
/etc/shadow
/etc/gshadow
/etc/shells
/etc/nsswitch.conf
These files are not optional decoration.
Even a very small system needs to know that UID 0 is root.
/etc/passwd
Despite the name, modern /etc/passwd does not normally contain password
hashes.
It maps user names to identity information:
name:password-marker:uid:gid:description:home:shell
Example:
root:x:0:0:Super User:/root:/usr/sbin/nologin
Read it as:
user name = root
password = x, meaning "look in /etc/shadow"
uid = 0
primary gid = 0
description = Super User
home = /root
shell = /usr/sbin/nologin
UID 0 is special. The kernel treats UID 0 as the superuser.
/etc/group
/etc/group maps group names to numeric group IDs.
Example:
root:x:0:
wheel:x:10:
The important thing is that software can refer to names like root, tty, or
wheel, while the kernel mostly cares about numeric IDs.
/etc/shadow
/etc/shadow stores password state.
It is separated from /etc/passwd because /etc/passwd is commonly readable by
normal programs, while password hashes must not be exposed.
In Phase 402 we are not setting a root password.
That is deliberate.
The root account exists, but it is not an interactive login account yet.
Why use systemd-sysusers
ONIX uses systemd as PID 1, so Phase 402 uses the systemd-native account materialization tool:
systemd-sysusers
The package-owned policy lives here:
/usr/lib/sysusers.d/onix-base.conf
That file says, in a declarative way:
these users and groups should exist
Then systemd-sysusers creates missing live entries under:
/etc/passwd
/etc/group
/etc/shadow
/etc/gshadow
This matches the ONIX split from Phase 401:
/usr/lib/sysusers.d/onix-base.conf package-owned policy
/etc/passwd live machine state
The important behavior is conservative:
create missing users/groups
do not blindly overwrite local account choices
What Phase 402 creates
Phase 402 creates a minimal base identity policy:
root UID 0 GID 0
nobody UID 65534 GID 65534
It also creates common base groups:
root
bin
daemon
sys
adm
tty
disk
wheel
shadow
systemd-journal
users
nogroup
This is not the final user model.
It is the first stable base that lets the booted image stop being anonymous.
Why root uses /usr/sbin/nologin
Phase 402 intentionally sets root’s shell to:
/usr/sbin/nologin
That means:
root exists
root is UID 0
root is not yet an interactive login account
This is safer than pretending login is ready.
At this point ONIX still has not proved:
- a real shell such as
/bin/sh - a working
agettyor equivalent serial terminal service - a working
loginprogram or explicit password/authentication policy
Those belong in Phase 403.
Where /usr/sbin/nologin comes from
The current ONIX image still uses the bootstrap systemd userspace payload from Phase 213.
That payload includes a Nix-store copy of util-linux’s nologin.
Phase 402 exposes it at the normal path:
/usr/sbin/nologin -> /nix/store/...-util-linux-minimal-...-login/bin/nologin
This is still bootstrap glue.
Later, ONIX should own this through real stones instead of reaching into a borrowed host-built closure.
/etc/nsswitch.conf
Programs need to know where to look up users, groups, hosts, services, and other names.
That policy lives in:
/etc/nsswitch.conf
Phase 402 starts simply:
passwd: files
group: files
shadow: files
hosts: files dns
For users and groups, files means:
look in /etc/passwd and /etc/group
For hosts, files dns means:
try /etc/hosts first, then DNS
Later networking phases can make this more complete.
/etc/shells
/etc/shells lists shells that the system considers valid account shells.
Phase 402 writes:
/usr/sbin/nologin
That looks strange at first, but it is intentional: Phase 402 is saying:
the only shell policy we have proved is non-interactive
Phase 403 is where we add and prove a temporary bootstrap serial console.
What the script writes as proof
Phase 402 writes:
/usr/share/onix/bootstrap/account-policy.txt
That file records:
- account policy lives in
/usr/lib/sysusers.d/onix-base.conf systemd-sysuserscreates missing live account entries under/etc- local account choices are not silently overwritten
- root exists but is intentionally non-interactive
- authenticated serial login is still a future proof
Run it
From the repo root:
make phase 402
Expected output includes lines like:
symlink : /usr/sbin/nologin -> /nix/store/.../bin/nologin
policy : /usr/lib/sysusers.d/onix-base.conf
sysusers : materialized missing /etc passwd/group/shadow entries
proof : /usr/share/onix/bootstrap/account-policy.txt
Then the phase verifies:
/usr/lib/sysusers.d/onix-base.confexists/usr/sbin/nologinpoints to an executable Nix-storenologin/etc/passwdhasrootandnobody/etc/grouphas base groups such asroot,shadow,wheel, andnogroup/etc/shadowand/etc/gshadowexist/etc/nsswitch.confuses local files for users and groups/etc/shellslists/usr/sbin/nologin/usr/share/onix/bootstrap/account-policy.txtrecords the limitation
What this phase does not do
Phase 402 does not prove login.
That is important.
After Phase 402, the image knows who root is, but it still should not promise:
you can authenticate as root on a serial console
Phase 403 should be the bootstrap serial-console proof. A later authenticated login step still needs to answer:
- Which shell exists?
- Which getty or terminal service starts?
- Which login/authentication program is used?
- Is root password login allowed, disabled, or replaced by some other first-boot mechanism?