Skip to content

feat(install): prepare-host admin step (#1178, client) - #377

Merged
LukasWodka merged 1 commit into
feat/lpi-tier0from
feat/lpi-prepare-host
Jul 23, 2026
Merged

feat(install): prepare-host admin step (#1178, client)#377
LukasWodka merged 1 commit into
feat/lpi-tier0from
feat/lpi-prepare-host

Conversation

@LukasWodka

@LukasWodkaLukasWodka commented Jul 22, 2026

Copy link
Copy Markdown
Contributor

What (#1178 — prepare-host, client side)

The standalone, admin-run Tier-2 step. An administrator runs it once on a host a researcher can't install on unprivileged; afterwards the researcher installs at Tier 0 with no admin.

  • Invoked as curl -fsSL https://tracebloc.io/i.sh | bash -s -- prepare-host (the bootstrap already forwards "$@" to install-k8s.sh, so no bootstrap change was needed) or, later, tracebloc prepare-host.
  • run_prepare_host does only the privileged prerequisites, reusing the exact functions the full install uses: preflight_sudo + install_docker_engine + install_system_deps. It then grants the researcher (SUDO_USER, or TB_PREPARE_USER) docker-group access — so their later install is Tier 0.
  • It never mints a credential, creates a cluster, or installs the CLI — an admin can safely run it on a shared host without provisioning anything as themselves.
  • Terminal mode in main() (mirrors --diagnose). Non-Linux gets a Docker-Desktop / WSL2 pointer.

Tests

  • 3 new setup-linux.bats: installs the runtime prereqs + adds the researcher to the docker group (and does not create a cluster / install the CLI); best-effort when there's no target user; non-Linux errors with the right pointer.
  • Full suite adds no new failures; shellcheck clean; R8 manifest current.

Scope

Client half of #1178. The clitracebloc prepare-host subcommand (a thin wrapper that runs … | bash -s -- prepare-host, like tracebloc upgrade) is a separate cli PR. The rootless/userns-enable nuance for hosts that need Tier 1 lives with #1177.

Stack

Stacked on #1175 → #1172 → #1171. Base is feat/lpi-tier0; merge in order.

Part of the least-privilege install epic (#1168), Wave 2.

Part of tracebloc/backend#1178 (client half; cli half to follow).

🤖 Generated with Claude Code


Note

Medium Risk
Runs privileged package/Docker setup and usermod on shared hosts; behavior is narrowed to an explicit admin step with guarded messaging and tests, but mis-set TB_PREPARE_USER could still leave researchers without socket access until fixed manually.

Overview
Adds a one-time administrator path so shared Linux hosts can be prepped without running the full tracebloc install as the admin.

prepare-host / --prepare-host is a terminal mode in install-k8s.sh (like --diagnose): it skips provisioning, cluster creation, and CLI install, uses a lightweight EXIT trap to stop the sudo keepalive from orphaning (#377), and calls run_prepare_host in setup-linux.sh.

run_prepare_host runs the same privileged pieces as a full install (preflight_sudo, Docker engine, system deps) and optionally adds a named researcher to the docker group via TB_PREPARE_USER only—no fallback to SUDO_USER (#377). Success text promises a no-admin install only when usermod actually succeeded.

Five new setup-linux.bats cases cover happy path, missing user, failed grant, admin-not-granted, and non-Linux. manifest.sha256 is updated for the touched scripts.

Reviewed by Cursor Bugbot for commit 7a625b0. Bugbot is set up for automated code reviews on this repo. Configure here.

@LukasWodka

Copy link
Copy Markdown
ContributorAuthor

bugbot run

Comment threadscripts/install-k8s.sh
Comment threadscripts/install-k8s.sh
@LukasWodka

Copy link
Copy Markdown
ContributorAuthor

bugbot run

LukasWodka added a commit that referenced this pull request Jul 23, 2026
prepare-host cleared the EXIT trap (install_cleanup does not apply to a host-prep
run), but run_prepare_host still calls preflight_sudo, which backgrounds a sudo
credential keepalive — left orphaned, polling sudo every 50s. Replace the blanket
`trap - EXIT` with a lightweight EXIT trap that kills SUDO_KEEPALIVE_PID (whether
run_prepare_host returns or error()s), without install_cleanup's credential-shred
+ messaging that do not apply here.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@LukasWodka
LukasWodkaforce-pushed the feat/lpi-prepare-host branch from 2643238 to 72780ccCompareJuly 23, 2026 06:58
@LukasWodka

Copy link
Copy Markdown
ContributorAuthor

bugbot run

Comment threadscripts/lib/setup-linux.sh
@LukasWodka
LukasWodkaforce-pushed the feat/lpi-prepare-host branch from 72780cc to 2a66916CompareJuly 23, 2026 07:19
LukasWodka added a commit that referenced this pull request Jul 23, 2026
prepare-host cleared the EXIT trap (install_cleanup does not apply to a host-prep
run), but run_prepare_host still calls preflight_sudo, which backgrounds a sudo
credential keepalive — left orphaned, polling sudo every 50s. Replace the blanket
`trap - EXIT` with a lightweight EXIT trap that kills SUDO_KEEPALIVE_PID (whether
run_prepare_host returns or error()s), without install_cleanup's credential-shred
+ messaging that do not apply here.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@LukasWodka

Copy link
Copy Markdown
ContributorAuthor

bugbot run

@cursorcursorBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ Bugbot reviewed your changes and found no new issues!

1 issue from previous review remains unresolved.

Fix All in Cursor

Comment @cursor review or bugbot run to trigger another review on this PR

Reviewed by Cursor Bugbot for commit 2a66916. Configure here.

Comment threadscripts/lib/setup-linux.sh
LukasWodka added a commit that referenced this pull request Jul 23, 2026
prepare-host cleared the EXIT trap (install_cleanup does not apply to a host-prep
run), but run_prepare_host still calls preflight_sudo, which backgrounds a sudo
credential keepalive — left orphaned, polling sudo every 50s. Replace the blanket
`trap - EXIT` with a lightweight EXIT trap that kills SUDO_KEEPALIVE_PID (whether
run_prepare_host returns or error()s), without install_cleanup's credential-shred
+ messaging that do not apply here.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@LukasWodka
LukasWodkaforce-pushed the feat/lpi-prepare-host branch from e694781 to e522814CompareJuly 23, 2026 07:56
Comment threadscripts/lib/setup-linux.sh
LukasWodka added a commit that referenced this pull request Jul 23, 2026
…EPARE_USER, never SUDO_USER (Bugbot #377)
SUDO_USER is the ADMIN who ran prepare-host, not the researcher — the fallback
added the wrong user to the docker group and reported success while the
researcher still could not install at Tier 0. Require TB_PREPARE_USER explicitly;
otherwise skip the grant and tell the admin exactly how to grant it. New bats
asserts SUDO_USER is not used.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@LukasWodka

Copy link
Copy Markdown
ContributorAuthor

bugbot run

Comment threadscripts/lib/setup-linux.sh Outdated
LukasWodka added a commit that referenced this pull request Jul 23, 2026
prepare-host cleared the EXIT trap (install_cleanup does not apply to a host-prep
run), but run_prepare_host still calls preflight_sudo, which backgrounds a sudo
credential keepalive — left orphaned, polling sudo every 50s. Replace the blanket
`trap - EXIT` with a lightweight EXIT trap that kills SUDO_KEEPALIVE_PID (whether
run_prepare_host returns or error()s), without install_cleanup's credential-shred
+ messaging that do not apply here.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@LukasWodka
LukasWodkaforce-pushed the feat/lpi-prepare-host branch from 2a66916 to e694781CompareJuly 23, 2026 08:27
Comment threadscripts/tests/setup-linux.bats Outdated
@LukasWodka

Copy link
Copy Markdown
ContributorAuthor

bugbot run

@cursorcursorBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ Bugbot reviewed your changes and found no new issues!

1 issue from previous review remains unresolved.

Fix All in Cursor

Comment @cursor review or bugbot run to trigger another review on this PR

Reviewed by Cursor Bugbot for commit c9e11e9. Configure here.

@LukasWodka
LukasWodkaforce-pushed the feat/lpi-prepare-host branch from 78d2285 to c9e11e9CompareJuly 23, 2026 08:48
LukasWodka added a commit that referenced this pull request Jul 23, 2026
prepare-host cleared the EXIT trap (install_cleanup does not apply to a host-prep
run), but run_prepare_host still calls preflight_sudo, which backgrounds a sudo
credential keepalive — left orphaned, polling sudo every 50s. Replace the blanket
`trap - EXIT` with a lightweight EXIT trap that kills SUDO_KEEPALIVE_PID (whether
run_prepare_host returns or error()s), without install_cleanup's credential-shred
+ messaging that do not apply here.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
LukasWodka added a commit that referenced this pull request Jul 23, 2026
…EPARE_USER, never SUDO_USER (Bugbot #377)
SUDO_USER is the ADMIN who ran prepare-host, not the researcher — the fallback
added the wrong user to the docker group and reported success while the
researcher still could not install at Tier 0. Require TB_PREPARE_USER explicitly;
otherwise skip the grant and tell the admin exactly how to grant it. New bats
asserts SUDO_USER is not used.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
LukasWodka added a commit that referenced this pull request Jul 23, 2026
…appened
The closing "The researcher can now install with no administrator rights"
message printed unconditionally — even when no TB_PREPARE_USER was given
(we only hinted how to grant access) or the usermod grant failed. In those
cases the researcher still can't reach the docker socket, so the promise was
a lie that sends them into an install which then demands sudo (Bugbot #377).
Track whether a grant succeeded and branch the closing message: the no-admin
promise only when granted, otherwise an honest "once they have docker-group
access, they can install" note. Add tests for the granted / no-target /
usermod-failed paths.
Also resolve stray git conflict markers left in setup-linux.bats by the
earlier rebase cascade (the _tier0_gpu_flags #375 tests and the run_prepare_host
#377 tests both belong here; the non-nvidia test's closing brace had been
swallowed by the ======= marker).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@LukasWodka

Copy link
Copy Markdown
ContributorAuthor

bugbot run

…all unprivileged (#1178)
Adds the admin-only prepare-host path: installs the container runtime +
prerequisites and grants a named non-admin user docker-group access, so they
later install tracebloc at Tier 0 with no root. Rebased onto the current
feat/lpi-tier0 (which now targets develop after routing #374 merged); collapsed
from the prior 4-commit history to avoid replaying an intermediate that carried
resolved conflict markers.
Incorporates the three Bugbot fixes from that history:
- reap the sudo keepalive on EXIT (no lingering background sudo -v loop);
- grant docker-group ONLY to the explicit TB_PREPARE_USER, never $SUDO_USER
(the admin who ran prepare-host);
- only print the "researcher can now install with no admin" message when a
docker-group grant actually succeeded.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

@cursorcursorBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ Bugbot reviewed your changes and found no new issues!

Comment @cursor review or bugbot run to trigger another review on this PR

Reviewed by Cursor Bugbot for commit aedbc73. Configure here.

@LukasWodka
LukasWodkaforce-pushed the feat/lpi-prepare-host branch from aedbc73 to 7a625b0CompareJuly 23, 2026 09:35

@divyasinghdsdivyasinghds left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approve. My earlier finding on the docker-group targeting is fully resolved:

  • local target="${TB_PREPARE_USER:-}" — the $SUDO_USER fallback is gone, with a comment explaining SUDO_USER is the admin, not the researcher.
  • New granted flag gates the closing message, so it no longer falsely promises "no administrator rights" when no grant happened (no TB_PREPARE_USER, or usermod failed) — that was a secondary half of the same problem, and it's handled.
  • Four new tests cover it, including one explicitly asserting SUDO_USER (the admin) is NOT added, and the usermod-fails path.

The rest still checks out: reuses the exact privileged install functions (no drift), terminal mode with the keepalive-reaping EXIT trap, non-Linux pointer. $OS is set at source time in common.sh so the guard is safe despite skipping run_preflight. CI green. LGTM.

shujaatTracebloc pushed a commit to tracebloc/cli that referenced this pull request Jul 23, 2026
* feat(cli): tracebloc prepare-host — one-time admin step wrapper (RFC 0001 #1178)
Adds `tracebloc prepare-host`: a thin, discoverable wrapper that re-runs the
official installer's verified prepare-host step (curl … | bash -s -- prepare-host),
exactly like `tracebloc upgrade` delegates to the installer rather than
re-implementing privileged host prep in the CLI. Registered in root; copy catalog
gains 11-prepare-host.golden + the strings flow into zz-all-strings. build/vet/
staticcheck/deadcode clean; catalog no-drift; gofmt clean.
Pairs with the client installer prepare-host step (tracebloc/client#377).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* fix(cli): set -o pipefail in prepare-host so a curl failure is not swallowed (Bugbot #394)
Without pipefail, `curl … | bash -s -- prepare-host` under `bash -c` exits 0 when
curl fails (bash reads empty stdin), so the command reported success while
prepare-host never ran. Prepend `set -o pipefail;` so curl's non-zero propagates
and c.Run() surfaces the failure. Regression guard added.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* prepare-host: run installer pipeline in its own process group
Cancelling the context (Ctrl-C / parent shutdown) previously killed only
the top-level `bash -c`, leaving the `curl` and the `bash -s` prepare-host
child -- which performs privileged host prep -- running detached after the
CLI had already reported failure and exited (Bugbot #394).
Extract prepareHostCmd(ctx): set SysProcAttr.Setpgid so the pipeline gets
its own process group, and a Cancel that group-signals (kill -PGID SIGINT)
so the whole pipeline stops when the user aborts. Add a test asserting
Setpgid + Cancel are wired.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* prepare-host: run installer from a temp file, not a pipe (keep stdin on TTY)
`curl | bash -s -- prepare-host` makes the inner bash read its *program*
from the pipe, so the installer's stdin is no longer the terminal — any
interactive prompt in prepare-host (e.g. which non-admin user gets runtime
access) gets EOF (Bugbot #394, second finding).
Switching to `bash <(curl …)` would fix stdin but reopen the FIRST #394
finding: process-substitution exit codes bypass pipefail, so a failed curl
would silently run nothing. Instead download to a temp file under `set -e`
(so a failed `curl -o` aborts) and run the file (stdin stays on the TTY).
Best of both: fail-closed on download error AND interactive-capable.
The manual-run hint shown on failure uses `bash <(curl …)` — the repo's
recommended idiom — since a human re-running it keeps their own TTY.
Add tests: fail-closed on download error (set -e + curl -o), and never pipe
the script into bash.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* prepare-host: fix Windows build — move POSIX process-group logic behind build tags
The process-group cancel added for Bugbot #394 used syscall.SysProcAttr.Setpgid
and syscall.Kill(-pid, …), which are POSIX-only — the Windows build failed to
compile (unknown field Setpgid; undefined syscall.Kill).
Extract configureProcessGroup into build-tagged files:
- prepare_host_unix.go (//go:build !windows): sets Setpgid + the group-killing
Cancel (unchanged behavior on linux/darwin).
- prepare_host_windows.go (//go:build windows): no-op — POSIX process groups
don't exist there, and exec.CommandContext still kills the top-level process;
prepare-host is a Linux host op regardless.
Move the Setpgid assertion test into prepare_host_unix_test.go (also
!windows-tagged) so `go test`/`go vet` compile on Windows too.
Verified: `GOOS=windows go build/vet ./...` now pass for amd64 + arm64, and the
native build/test/staticcheck stay green.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* prepare-host: run in the foreground group, quiet interrupt on cancel (Bugbot #394)
The process-group approach was wrong for an interactive command and Bugbot
flagged five follow-ups:
- Setpgid put the installer in its own (background) process group while stdin
was the TTY, so any prepare-host prompt got SIGTTIN and hung (High).
- the SIGINT-only Cancel with no WaitDelay could hang Wait forever if a
privileged child ignored the signal, re-opening the orphaned-work risk.
- a user Ctrl-C was wrapped as "prepare-host didn't complete — retry" instead
of a quiet interrupt.
Drop Setpgid and the custom Cancel entirely: keep the installer in the CLI's
foreground process group so prompts work and a terminal Ctrl-C signals the whole
pipeline (bash + curl + child) at once. Add WaitDelay=5s so a programmatic
cancel can't hang Wait. Treat ctx cancel as exitInterrupted (130), matching the
other cancellable paths. This also removes all syscall usage, so the Windows
build no longer needs the build-tagged split (files deleted).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* prepare-host: reuse installCmd for the hint + robust interrupt detection (Bugbot #394)
- prepareHostManualHint duplicated the bootstrap idiom owned by installCmd
(doctor.go), so a URL/idiom change could leave the prepare-host fallback
stale. Build it from installCmd + " prepare-host" (same value, one source).
- Interrupt detection only checked ctx.Err() after c.Run(); on a terminal
Ctrl-C the child dies (bash exits 130) and Run can return before
NotifyContext flips ctx.Err(), so an abort was mis-reported as a failed
install. Add prepareHostInterrupted(ctx, err): interrupt if ctx cancelled OR
the run exited 130 (128+SIGINT). Unit-tested (cancelled ctx, exit 130, exit 1).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* prepare-host: accept a researcher username to grant docker-group access (Divya #377)
prepare-host was cobra.NoArgs with no flag and no mention of TB_PREPARE_USER,
so an admin had no discoverable way to name the researcher — the installer's
docker-group grant (which reads TB_PREPARE_USER) was always skipped and the
feature couldn't deliver Tier-0 access end-to-end.
Add an optional positional arg: `tracebloc prepare-host <researcher-username>`
(MaximumNArgs(1)). The username is validated (Linux-username shape) and passed
to the installer via the TB_PREPARE_USER environment variable (not the command
string, so it can't be shell-interpreted; the installer quotes it for usermod).
Help text explains the arg and stresses it's the researcher, not the admin.
Without it, prepare-host installs prereqs only and prints how to grant access.
The client installer already grants ONLY TB_PREPARE_USER (never $SUDO_USER), so
this closes the loop.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* fix(cli): guard prepare-host on Windows (Bugbot #394)
prepare-host shells out to bash/curl/mktemp and readies a Linux server / HPC
login node (container runtime, docker group) — a Unix-only concept. On Windows
it appeared in --help then failed with a cryptic missing-bash error and a
Unix-only retry hint. It now stops early with a clear explanation (a
no-op-with-message), mirroring upgrade's Windows handling. Adds a regression
test for the OS guard.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Co-authored-by: shujaat hasan <shujaathasan@shujaats-MacBook-Pro.local>
@LukasWodka
LukasWodka merged commit f474a63 into feat/lpi-tier0Jul 23, 2026
3 checks passed
LukasWodka added a commit that referenced this pull request Jul 24, 2026
…e admin (Bugbot)
install_docker_engine's tail assumed the invoking user is the end-user:
on the documented non-root prepare-host path it added the ADMIN to the
docker group and the sg re-exec re-ran the script WITHOUT the
prepare-host argument -- a silent FULL provision as the admin -- and a
socket-less admin aborted before the TB_PREPARE_USER grant. In
TB_PREPARE_HOST_MODE the daemon check runs via sudo, the group-add and
re-exec are skipped, and only the researcher named by TB_PREPARE_USER is
ever granted the socket (same least-privilege rule as #377). Two bats
regressions pin both escapes.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@LukasWodka

Copy link
Copy Markdown
ContributorAuthor

Functional review — NOT passed, held deliberately

I advanced 25 of the 29 items in FR on dev to Ready for staging today, each with its evidence recorded on the PR. This one I am holding, because I cannot produce evidence for it and signing it off would be a rubber stamp:

Only 4 checks ran on this PR and none of them is behavioural — unlike its sibling installer PRs, which each ran the multi-distro E2E cluster suite and the squid auth-proxy e2e. This is the prepare-host admin step, i.e. a privileged host-modification path, which is precisely the kind of change that wants a real run rather than inference. Needs a manual pass on a real host (ideally a Windows one, given the elevation flow), or a re-run with the e2e suites enabled.

LukasWodka added a commit that referenced this pull request Jul 27, 2026
… client) (#381)
* feat(install): prepare-host — one-time admin step so researchers install unprivileged (#1178)
Adds the admin-only prepare-host path: installs the container runtime +
prerequisites and grants a named non-admin user docker-group access, so they
later install tracebloc at Tier 0 with no root. Rebased onto the current
feat/lpi-tier0 (which now targets develop after routing #374 merged); collapsed
from the prior 4-commit history to avoid replaying an intermediate that carried
resolved conflict markers.
Incorporates the three Bugbot fixes from that history:
- reap the sudo keepalive on EXIT (no lingering background sudo -v loop);
- grant docker-group ONLY to the explicit TB_PREPARE_USER, never $SUDO_USER
(the admin who ran prepare-host);
- only print the "researcher can now install with no admin" message when a
docker-group grant actually succeeded.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* prepare-host: sudo-verified engine gate; never sg-re-exec or grant the admin (Bugbot)
install_docker_engine's tail assumed the invoking user is the end-user:
on the documented non-root prepare-host path it added the ADMIN to the
docker group and the sg re-exec re-ran the script WITHOUT the
prepare-host argument -- a silent FULL provision as the admin -- and a
socket-less admin aborted before the TB_PREPARE_USER grant. In
TB_PREPARE_HOST_MODE the daemon check runs via sudo, the group-add and
re-exec are skipped, and only the researcher named by TB_PREPARE_USER is
ever granted the socket (same least-privilege rule as #377). Two bats
regressions pin both escapes.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* fix(prepare-host): reach the sub-script on healthy machines + terminal active-daemon corner (Bugbot r2)
Two follow-ups on the prepare-host re-land:
- install.sh: prepare-host/--prepare-host now skip the healthy-install
bailout — the argument is useful precisely on a machine that is already
set up (grant ANOTHER researcher docker-group access), and the bailout
would open the home screen and silently skip the TB_PREPARE_USER grant.
It skips ONLY the bailout: the force flag is split out (_tb_force), so
host-prep is never mislabeled as a forced reinstall — a stale sub-script
without the prepare-host dispatch would otherwise treat the run as a
full forced provision.
- setup-linux.sh: in prepare-host mode, a daemon that systemd reports
ACTIVE but that doesn't answer 'sudo docker info' is now terminal with
prepare-host-appropriate guidance, instead of falling through to the
docker-group 'log out and back in' abort meant for regular users.
Regression tests: bootstrap bats stamps DEFAULT_REF like the release
pipeline (assignment line only) to prove prepare-host passes the bailout
with TB_FORCE_REINSTALL unset; setup-linux bats covers the active-but-
unresponsive daemon corner. 16/16 + 58/58.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* fix(prepare-host): terminal daemon-down path + trim TB_PREPARE_USER (Bugbot r3)
- Daemon DOWN in prepare-host mode: start it via sudo (starting Docker IS
host preparation), re-verify, and stay terminal in prepare-host wording
on every failure — the shared diagnostics end with 're-run this
installer', which for the admin means a full provision as themselves,
the exact outcome prepare-host exists to prevent. Reboot-required case
gets prepare-host wording too.
- TB_PREPARE_USER is trimmed before the non-empty gate and the grant, so
a pasted value with stray spaces doesn't fail usermod while skipping
the honest no-grant messaging.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* fix(prepare-host): dispatch at any argument position (Bugbot r4)
install.sh's bailout exemption scans all of "$@" for prepare-host, but
the sub-script only dispatched on $1 — so '--force prepare-host' skipped
the bailout, exported the force flag, and then fell through into a FULL
FORCED provision as the admin. The dispatch now scans every argument,
matching the bootstrap's contract.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* fix(prepare-host): enable docker on boot in the already-running path (Bugbot r5)
Running NOW isn't host-prep: after a reboot the Tier-0 researcher can't
start the daemon themselves. The success path now ensures docker.service
is enabled on boot (best-effort), matching the fresh-install and
daemon-down recovery paths.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* fix(prepare-host): don't let diagnostics pipelines abort before the guidance (Bugbot r6)
systemctl status exits 3 for an inactive unit (and the shared block's
grep exits 1 on no match) — under set -e -o pipefail the failing
diagnostics pipeline aborted BEFORE the terminal error message, a silent
death with no re-run guidance. Both the prepare-host block and the shared
daemon-down block now guard the pipeline with || true; the bats stub
returns the real rc 3 to exercise the path.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
shujaatTracebloc added a commit that referenced this pull request Jul 31, 2026
…(Bugbot r2)
The sudo-refuse prepare-host remedy filled TB_PREPARE_USER=${SUDO_USER}, but SUDO_USER
is the ADMIN who ran sudo — the "setting up for someone else" case targets a DIFFERENT
researcher. Following it would grant the admin docker-group access and leave the
intended user locked out (the #377 least-privilege footgun). Use a
<researcher-username> placeholder and say "not yourself". Test updated to require the
placeholder and reject $SUDO_USER.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
shujaatTracebloc added a commit that referenced this pull request Jul 31, 2026
…installs (#511)
* fix(#427): grant docker group on any daemon run; refuse sudo-wrapped installs
Two compounding identity bugs on Linux:
1. The docker-group grant ran ONLY inside the fresh-Docker-install branch. On a box
where Docker was already present and the user wasn't in the group, the else-branch
printed "Docker" without granting, and the recovery path dead-ended at "Could not
connect to Docker. Try logging out and back in…" — which couldn't help, because
membership was never granted. Re-runs looped on the same message.
Fix: after the install/else, ensure the invoking user is in the docker group
whenever the daemon path is chosen, regardless of a fresh install. Skip if already
a member (no redundant usermod); prepare-host stays exempt (only TB_PREPARE_USER
is granted, later — least-privilege, #381). The existing sg-docker re-exec then
activates the new membership in-session, so no dead-end loop.
2. Nothing was SUDO_USER-aware: `sudo bash install.sh` ran the WHOLE provision as
root — usermod granted root (not the user), and ~/.tracebloc, ~/.kube/config, and
the chmod-600 credential landed root-owned under /root, with no chown anywhere to
undo it. The installer's model is to run as the daily user and elevate per-step
(RFC-0002), so rather than a fragile ownership remap, refuse the sudo-wrapped full
run early (before any file is created): refuse_sudo_wrapped_install errors when
EUID 0 AND $SUDO_USER is a real (non-root) user. Exemptions: a genuine root login
(no SUDO_USER); prepare-host (the admin path, already dispatched+exited in main()).
Because we never run as root-with-SUDO_USER, every $HOME/$USER path in the tree
stays correct with no remap.
Also: honor TB_PREPARE_USER as the grant target in the main install (new
_real_install_user helper; the #418 Windows peer).
Tests: install_docker_engine grants on the pre-installed path + when only
TB_PREPARE_USER differs, skips when already a member, never grants the admin in
prepare-host, still grants on a fresh install; refuse_sudo_wrapped_install refuses
sudo+SUDO_USER but allows root-login / sudo -i / non-root; _real_install_user.
Closes#427
* fix(#427): grant the invoking user (not TB_PREPARE_USER); name TB_PREPARE_USER in the refuse hint
Bugbot:
- The main-install docker-group grant targeted _real_install_user (TB_PREPARE_USER
when set), but socket access and the sg-docker re-exec key off $USER. When they
differ (e.g. a leftover `export TB_PREPARE_USER=` from prepare-host), the invoking
user never got membership and hit the same dead-end this PR fixes. Grant $USER
directly — the sudo-wrapped run is already refused, so $USER is the real daily
user — and keep TB_PREPARE_USER on the prepare-host path only (where it's granted).
Removed the now-unused _real_install_user helper + its tests.
- The sudo-refuse hint pointed admins at a BARE `prepare-host`, but run_prepare_host
only grants when TB_PREPARE_USER is set — so that remedy prepares the daemon and
grants nobody. Name it: `export TB_PREPARE_USER=<user> && … prepare-host`.
Tests: the grant targets $USER even with a leftover TB_PREPARE_USER; the refuse hint
includes TB_PREPARE_USER=<user>.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* fix(#427): refuse hint names a RESEARCHER placeholder, not the admin (Bugbot r2)
The sudo-refuse prepare-host remedy filled TB_PREPARE_USER=${SUDO_USER}, but SUDO_USER
is the ADMIN who ran sudo — the "setting up for someone else" case targets a DIFFERENT
researcher. Following it would grant the admin docker-group access and leave the
intended user locked out (the #377 least-privilege footgun). Use a
<researcher-username> placeholder and say "not yourself". Test updated to require the
placeholder and reject $SUDO_USER.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* fix(#427): key the sg-docker re-exec off _grant_user, consistent with the grant (reviewer)
The grant resolved its target with a `${USER:-$(id -un)}` fallback, but the in-session
sg-docker re-exec guard still keyed off bare `$USER`. In the exact USER-unset case the
fallback exists for, the grant landed on `$(id -un)` while that guard saw an empty
`$USER`, skipped the re-exec, and re-introduced the "log out and back in" dead-end.
Hoist _grant_user to the top of install_docker_engine and use it in both places so
they can't disagree. Guard test added.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
@LukasWodka
LukasWodka deleted the feat/lpi-prepare-host branch August 14, 2026 13:53
Sign up for freeto 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

@LukasWodka@divyasinghds@saadqbal