feat(users): migrate daniel to kanidm-only identity #498

Merged
lytedev merged 7 commits from kanidm-takeover-daniel into main 2026-04-19 16:16:13 -05:00
Owner

Summary

Removes the local nix-declared users.users.daniel entirely. Daniel is now provided exclusively by kanidm via NSS/PAM; identity, auth, and most group membership flow from the kanidm server. Supersedes PR #495 — the uid_attr_map = "name" fix is folded in here.

Layout changes

  • Flatten /home/daniel/.home/ to /home/daniel/. Kanidm's home_attr = "name" makes it report pw_dir = /home/<name>, matching.
  • New lyte.userHome and lyte.userSshKeys options on the shared user-env module replace the previous config.users.users.daniel.* indirection.

Authorization

  • security.sudo.extraRules grants kanidm's administrators group wheel-equivalent sudo.
  • Local group memberships (wheel, video, dialout, uucp, power, kvm, input, users, plus service-specific groups on beefcake) attach daniel by name via users.groups.<g>.members = [ "daniel" ]. String members don't need a local user declaration.
  • SSH keys written to /etc/ssh/authorized_keys.d/daniel so login survives kanidm-unixd being down.

Migration unit

systemd.services.migrate-daniel-to-kanidm: idempotent one-shot, marker-gated. On each host:

  1. Bails if an active session exists (so we don't chown live files).
  2. Flattens /home/daniel/.home/* up one level if still nested.
  3. Chowns everything under /home/daniel to the kanidm uid/gid (resolved by name via getent, no numeric hardcoding).
  4. userdel daniel if a stale uid=1000 entry is still in /etc/passwd.

Testing

Deployed to thinker via LAN. NixOS itself removed the local daniel on activation ("removing user 'daniel'"). Post-deploy:

  • id daniel → uid/gid from kanidm, full group membership.
  • getent passwd daniel → single entry pointing at /home/daniel.
  • Migration unit bailed because plasma/tty4 still had an active uid=1000 session — will run cleanly on next boot / session end. /home/daniel/.home/ still needs the flatten, files still at 1000:1000; will be resolved when the active session clears.

Follow-up

  • PR #497 (ACL + unifi timeout) should land first so beefcake deploy has a clean path.
  • After beefcake ships this PR, kanidm's uid_attr_map/home_attr take effect at the server level too.
## Summary Removes the local nix-declared `users.users.daniel` entirely. Daniel is now provided exclusively by kanidm via NSS/PAM; identity, auth, and most group membership flow from the kanidm server. Supersedes PR #495 — the `uid_attr_map = "name"` fix is folded in here. ## Layout changes - Flatten `/home/daniel/.home/` to `/home/daniel/`. Kanidm's `home_attr = "name"` makes it report `pw_dir = /home/<name>`, matching. - New `lyte.userHome` and `lyte.userSshKeys` options on the shared user-env module replace the previous `config.users.users.daniel.*` indirection. ## Authorization - `security.sudo.extraRules` grants kanidm's `administrators` group wheel-equivalent sudo. - Local group memberships (wheel, video, dialout, uucp, power, kvm, input, users, plus service-specific groups on beefcake) attach daniel by name via `users.groups.<g>.members = [ "daniel" ]`. String members don't need a local user declaration. - SSH keys written to `/etc/ssh/authorized_keys.d/daniel` so login survives kanidm-unixd being down. ## Migration unit `systemd.services.migrate-daniel-to-kanidm`: idempotent one-shot, marker-gated. On each host: 1. Bails if an active session exists (so we don't chown live files). 2. Flattens `/home/daniel/.home/*` up one level if still nested. 3. Chowns everything under `/home/daniel` to the kanidm uid/gid (resolved by name via `getent`, no numeric hardcoding). 4. `userdel daniel` if a stale uid=1000 entry is still in `/etc/passwd`. ## Testing Deployed to thinker via LAN. NixOS itself removed the local daniel on activation ("removing user 'daniel'"). Post-deploy: - `id daniel` → uid/gid from kanidm, full group membership. - `getent passwd daniel` → single entry pointing at `/home/daniel`. - Migration unit bailed because plasma/tty4 still had an active uid=1000 session — will run cleanly on next boot / session end. `/home/daniel/.home/` still needs the flatten, files still at 1000:1000; will be resolved when the active session clears. ## Follow-up - PR #497 (ACL + unifi timeout) should land first so beefcake deploy has a clean path. - After beefcake ships this PR, kanidm's `uid_attr_map`/`home_attr` take effect at the server level too.
feat(users): migrate daniel to kanidm-only identity
Some checks failed
/ check-format (push) Successful in 8s
/ build (push) Failing after 56s
797aa67f7e
Remove the local `users.users.daniel` declaration and the nested
`/home/daniel/.home` layout. Daniel is now provided exclusively by
kanidm via NSS/PAM, with uid/gid derived from UUID by kanidm.

Changes:

- `lib/modules/shared/user-env.nix`: new `lyte.userHome` and
  `lyte.userSshKeys` options, replaces the indirect lookup through
  `config.users.users.<name>`.
- `lib/modules/nixos/default-module.nix`:
  - Drop `users.users.daniel` / `users.groups.daniel`.
  - Flatten home: tmpfiles creates `/home/daniel` (not `/home/daniel/.home`).
  - Attach daniel to local service groups (wheel, video, dialout,
    uucp, power, kvm, input, users) via `users.groups.<g>.members`
    (string members don't require a local user declaration).
  - Grant kanidm `administrators` group wheel-equivalent sudo.
  - Write `/etc/ssh/authorized_keys.d/daniel` for offline/kanidm-down
    SSH access (the existing `AuthorizedKeysFile` stanza picks these
    up).
  - New `migrate-daniel-to-kanidm` one-shot: flattens `~/.home` if
    still nested, chowns `/home/daniel` to the kanidm-provided uid
    (by name, not number), userdels the pre-migration local entry if
    it's still at uid 1000. Idempotent via marker, bails if a session
    is active.
- `lib/modules/nixos/kanidm.nix`:
  - Set `uid_attr_map` / `gid_attr_map` to "name" (shortname
    resolution, supersedes the pending #495).
  - Set `home_attr = "name"`, `home_alias = "none"` so kanidm
    reports pw_dir as `/home/daniel` and doesn't try to create a
    conflicting UUID-named dir.
- Host files (`dragon,flab,foxtrot,rascal,pinephone`,
  `beefcake/{daniel,restic}`): swap `config.users.users.daniel.*`
  for `config.lyte.{userHome,userSshKeys,username}`. Group
  memberships moved to `users.groups.<g>.members` lists.
- Modules (`syncthing, desktop, opencode, claude, niri,
  virtual-machines`): same pattern.

Supersedes PR #495 (uid_attr_map fix, folded into this one).
feat(kanidm): set fish as default login shell
Some checks failed
/ check-format (push) Successful in 9s
/ build (push) Failing after 1m16s
def387a54a
Without a local users.users.daniel declaration, kanidm's unixd serves
whatever shell it has configured. Default is bash; override globally
to fish so plasma login, sshd, etc. all give users a consistent
interactive shell.
fix(migrate-daniel): retarget symlinks pointing into old .home
Some checks failed
/ check-format (push) Successful in 7s
/ build (push) Failing after 1m5s
7447555547
After flattening /home/daniel/.home/ up to /home/daniel/, any symlinks
that referenced /home/daniel/.home/... are dangling. In practice most
of these come from Steam's proton prefixes (hundreds of .dll symlinks)
plus .nix-profile, .nix-defexpr/channels, and Steam runtime paths.
Rewriting them is a mechanical string replace on the symlink target.
fix(users): unblock CI for hosts that referenced daniel
All checks were successful
/ check-format (push) Successful in 7s
/ build (push) Successful in 7m12s
873e2b5275
Three call sites that still created/depended-on users.users.daniel
after the removal, causing CI to fail on dragon (music-production),
and foxtrot/flab/dragon/syncthing (sops-nix secret owner lookup):

- lib/modules/nixos/music-production.nix: convert `users.users.<u>
  .extraGroups = [ "audio" ]` to `users.groups.audio.members`.
- All sops secrets with `owner = "daniel"`: add explicit
  `group = "users"`. sops-nix defaults the group to
  `users.${owner}.group` at eval time, which crashes when daniel
  isn't a declared user.
- Add daniel to `networkmanager` group so NetworkManager's polkit
  rules let him edit connections (resolves the 'not authorized'
  prompt hit on thinker).
feat(plasma): cache kanidm user with accountsservice so greeter shows them
All checks were successful
/ check-format (push) Successful in 7s
/ build (push) Successful in 5m27s
b806ed3076
plasma-login-manager queries accounts-daemon (which walks /etc/passwd
directly, not NSS) to populate the user list. Kanidm users aren't in
/etc/passwd, so the greeter shows no avatar for daniel and you have
to pick 'Other…' + type the username.

Add a small systemd oneshot that calls accountsservice's CacheUser
D-Bus method after both kanidm-unixd and accounts-daemon are up.
CacheUser just persists prefs against an NSS-provided entry — it
doesn't modify /etc/passwd or create a local account.
revert(plasma): drop CacheUser hack, note the kanidm enum gap
Some checks failed
/ check-format (push) Successful in 7s
/ build (push) Has been cancelled
98ae4280ec
Revert the busctl CacheUser oneshot — it worked but it's a
workaround for a design mismatch between accounts-daemon (wants
/etc/passwd) and kanidm-unixd (NSS-enumeration exists in 1.10-dev
code but doesn't end-to-end-populate getpwent in our deploy yet).

Leave a note documenting the limitation + how to log in. Real fix
is to get kanidm-unixd's enumeration actually returning the cached
users via NSS — that way accounts-daemon and the greeter see them
the same way files-based users work.
revert(plasma): drop CacheUser hack, note the kanidm enum gap
Some checks failed
/ check-format (push) Successful in 8s
/ build (push) Failing after 10s
2d61769a14
Revert the busctl CacheUser oneshot — it worked but it's a
workaround for a design mismatch between accounts-daemon (wants
/etc/passwd) and kanidm-unixd (NSS-enumeration exists in 1.10-dev
code but doesn't end-to-end-populate getpwent in our deploy yet).

Leave a note documenting the limitation + how to log in. Real fix
is to get kanidm-unixd's enumeration actually returning the cached
users via NSS — that way accounts-daemon and the greeter see them
the same way files-based users work.
lytedev deleted branch kanidm-takeover-daniel 2026-04-19 16:16:13 -05:00
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
lytedev/nix!498
No description provided.