pipe the installer into bash, and stop inventing a root-owned .local
Green's first provision failed three ways. host caught all three on the live
box; two are fixed here and the third is his to bisect.
THE INSTALLER IS BASH AND WE PIPED IT INTO SH. A script read on stdin never has
its shebang honoured — the interpreter you name is the one that runs it — and
install.sh declares #!/bin/bash and uses [[ =~ ]] on line 9. On Ubuntu /bin/sh
is dash, so it died with `Syntax error: "(" unexpected`, which reads like a
corrupt download rather than the wrong interpreter. scripts/setup.sh carried the
same line for the owner's own install and is fixed too.
INSTALL -D CREATED ~/.local AS ROOT. `install -d` makes missing parents but
applies -o/-g/-m only to the final component, so blessing ~/.local/dockers
invented a root:root .local inside the member's own home. Rootless Docker then
died on `mkdir …/.local/share: permission denied`, and the Claude installer
targets ~/.local/bin, so fixing the shell alone would have hit this next.
That is 71589ae for the second time — same function shape, same silent parent,
same class of consequence. Its own commit message said this surfaces "weeks
later as one tool mysteriously failing"; it took twenty minutes. Grepped the
other install -d/-D sites: os-user-shell already creates its parent explicitly,
os-user-ssh has no implicit parent.
NOT fixed: the file browser's ACL mask on a member home, where access mask is
--- while default:mask is rwx. That pattern means a chmod ran after the setfacl
and clamped only the access side, so the primitive is right and something later
is wrong. host has the live filesystem and has already half-excluded the
suspect; guessing from here would churn a working block. Noted that this commit
adds an install -d before the one he was about to bisect, so it wants a
reprovision first.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -393,6 +393,31 @@ export async function confineUserTree(params: {
|
||||
// The cost is that the file browser cannot read inside it, which is the same trade already accepted for
|
||||
// Docker's internal storage — consistent rather than a new exception. Not enforced: a member can bind
|
||||
// mount from anywhere and will hit the denial there. This is the documented place that works.
|
||||
// `.local` FIRST, explicitly, with the member's ownership. `install -d` creates missing parents but
|
||||
// applies `-o`/`-g`/`-m` only to the FINAL component, so letting it invent `.local` leaves that directory
|
||||
// root:root — inside the member's own home, unwritable by them.
|
||||
//
|
||||
// This is `71589ae` for the second time. That commit found the identical thing for `~/.config` and wrote
|
||||
// "a single wrong-owner directory in a home is the kind of thing that surfaces weeks later as one tool
|
||||
// mysteriously failing". It surfaced in twenty minutes: rootless Docker died on
|
||||
// `mkdir …/.local/share: permission denied`, and the Claude installer targets `~/.local/bin`, so it was
|
||||
// blocked by the same directory. Grep before adding another `install -d`/`-D` whose parent is implicit.
|
||||
const localDir = join(home, '.local');
|
||||
const madeLocalDir = await run([
|
||||
'sudo',
|
||||
'-n',
|
||||
'install',
|
||||
'-d',
|
||||
'-o',
|
||||
String(params.uid),
|
||||
'-g',
|
||||
String(params.gid),
|
||||
'-m',
|
||||
'700',
|
||||
localDir,
|
||||
]);
|
||||
if (!madeLocalDir.ok) return { ok: false, error: `could not create ${localDir}: ${madeLocalDir.out}` };
|
||||
|
||||
const composeDir = join(home, '.local', 'dockers');
|
||||
const madeComposeDir = await run([
|
||||
'sudo',
|
||||
|
||||
Reference in New Issue
Block a user