Skip to content

fix(daemon): orphan-reaper only reaps containarium-managed accounts#920

Merged
hsinatfootprintai merged 2 commits into
mainfrom
fix/orphan-reaper-shell-guard
Jul 9, 2026
Merged

fix(daemon): orphan-reaper only reaps containarium-managed accounts#920
hsinatfootprintai merged 2 commits into
mainfrom
fix/orphan-reaper-shell-guard

Conversation

@hsinatfootprintai

Copy link
Copy Markdown
Contributor

Summary

  • RunOrphanReaper (Orphaned host users + sshpiper user dirs accumulate without bound (683 homes / 1296 sshpiper entries) → keysync bloat #835) scans /home/<name> for any directory with an authorized_keys file and deletes the account if no <name>-container exists. This heuristic can't distinguish a genuine leftover Containarium tenant from an unrelated admin account that happens to share the same directory shape.
  • Real incident: upgrading a lab host's daemon (which enabled this reaper for the first time on that host, since older versions only warned) userdel -r'd an operator's own admin login mid-session — it had authorized_keys under /home and no matching container, so it looked identical to a genuine orphan.
  • Fix: gate reaping on the account's login shell actually being the containarium-managed shell wrapper (containerShellPath). Real tenant accounts always run this shell; a manually-provisioned admin account (bash/zsh/etc.) never does, regardless of directory shape.

Test plan

  • go build ./...
  • New tests in orphan_reaper_test.go: only containarium-shell + no-container accounts get reaped; a bash-shell account is never reaped even with no matching container; unknown/lookup-failure accounts are never reaped (fail-safe); directories with no authorized_keys never reach the shell lookup at all.
  • go vet ./...

RunOrphanReaper (#835) swept every /home/<name> directory with an
authorized_keys file and no matching <name>-container, with no way to
tell a genuine leftover tenant apart from an unrelated admin account
sharing the same directory shape. Gate reaping on the account's login
shell actually being the containarium-managed shell wrapper instead.

Real incident: upgrading a lab host's daemon (enabling this reaper for
the first time there) userdel'd an operator's own admin account
mid-session, because it happened to have authorized_keys under /home
and no matching container.

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
Comment thread pkg/core/container/orphan_reaper.go Fixed
userShell() passed directory names straight from os.ReadDir into
exec.Command without validation, unlike the sibling getent-shadow call
in jump_server.go which relies on callers pre-validating via
isValidUsername. Gate reapOnce on isValidUsername before any directory
name reaches the shell lookup, making the #nosec justification true
rather than just copied from the sibling call.

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
@hsinatfootprintai hsinatfootprintai merged commit e602ed2 into main Jul 9, 2026
8 checks passed
@hsinatfootprintai hsinatfootprintai deleted the fix/orphan-reaper-shell-guard branch July 9, 2026 08:03
hsinatfootprintai added a commit that referenced this pull request Jul 9, 2026
)

Co-authored-by: hsinhoyeh <yhh92u@gmail.com>
Co-authored-by: Claude Haiku 4.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants