stop creating the container bind-mount directory too
~/.local/dockers existed only so a rootless container's inner uid could traverse to a bind source. No daemon, no need. Commented out with its reasoning intact in confineUserTree. .local itself stays — it is not Docker's. The claude installer targets ~/.local/bin, and the comment above it records that directory being created root-owned and blocking the install. Nothing to change in deprovisionOsAccount: it never called os-user-docker.ts. Its only Docker-shaped part is capturing /etc/subuid before userdel, which already treats a missing entry as normal and is worth keeping for any rootless tooling. Verified: transpiles. No test referenced composeDir. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
+43
-31
@@ -369,7 +369,17 @@ export async function confineUserTree(params: {
|
||||
}
|
||||
}
|
||||
|
||||
// ── One directory where container bind mounts can live ──
|
||||
// ── One directory where container bind mounts can live: DISABLED 2026-08-13, code kept ──
|
||||
//
|
||||
// `~/.local/dockers` existed only for rootless Docker, and members no longer get a daemon — see the
|
||||
// commented-out step in api/users/provision-os.ts. Creating a blessed bind-mount directory for a daemon
|
||||
// that is not there is a promise about container storage the platform no longer keeps.
|
||||
//
|
||||
// `.local` itself STAYS. It is not Docker's: the `claude` installer targets `~/.local/bin`, and the
|
||||
// paragraph below is the record of that directory being created root-owned and blocking it.
|
||||
//
|
||||
// The reasoning is kept in full because turning containers back on means uncommenting the block, not
|
||||
// rediscovering why 711 and why the ACLs come off.
|
||||
//
|
||||
// The default ACLs above are inherited by everything created in the home afterwards, including
|
||||
// `default:other::---`. A rootless container's INNER uid is neither the service user nor the member —
|
||||
@@ -416,37 +426,39 @@ export async function confineUserTree(params: {
|
||||
]);
|
||||
if (!madeLocalDir.ok) return { ok: false, error: `could not create ${localDir}: ${madeLocalDir.out}` };
|
||||
|
||||
const composeDir = join(home, '.local', 'dockers');
|
||||
const madeComposeDir = await run([
|
||||
'sudo',
|
||||
'-n',
|
||||
'install',
|
||||
'-d',
|
||||
'-o',
|
||||
String(params.uid),
|
||||
'-g',
|
||||
String(params.gid),
|
||||
// 711, not 700, and this is the whole point of the directory. A container's inner uid is `other`, so it
|
||||
// needs `x` HERE to reach a bind source inside — 700 blocks the path before any ACL matters. `r` stays
|
||||
// off, so nothing can list it. Safe because the home above is 700: no other account can traverse this
|
||||
// far in the first place, and the only uids that get here are the member's own containers.
|
||||
'-m',
|
||||
'711',
|
||||
composeDir,
|
||||
]);
|
||||
if (!madeComposeDir.ok) return { ok: false, error: `could not create ${composeDir}: ${madeComposeDir.out}` };
|
||||
|
||||
// `-b`, not `-k`: remove ACCESS entries as well as defaults, leaving plain POSIX modes.
|
||||
// const composeDir = join(home, '.local', 'dockers');
|
||||
// const madeComposeDir = await run([
|
||||
// 'sudo',
|
||||
// '-n',
|
||||
// 'install',
|
||||
// '-d',
|
||||
// '-o',
|
||||
// String(params.uid),
|
||||
// '-g',
|
||||
// String(params.gid),
|
||||
// // 711, not 700, and this is the whole point of the directory. A container's inner uid is `other`, so
|
||||
// // it needs `x` HERE to reach a bind source inside — 700 blocks the path before any ACL matters. `r`
|
||||
// // stays off, so nothing can list it. Safe because the home above is 700: no other account can
|
||||
// // traverse this far in the first place, and the only uids that get here are the member's own
|
||||
// // containers.
|
||||
// '-m',
|
||||
// '711',
|
||||
// composeDir,
|
||||
// ]);
|
||||
// if (!madeComposeDir.ok) return { ok: false, error: `could not create ${composeDir}: ${madeComposeDir.out}` };
|
||||
//
|
||||
// `-k` alone left `mask::---` behind — inherited named entries with every permission masked off, reading
|
||||
// as `user:pastilhas:rwx #effective:---`. An ACL that says one thing and means another is worse than no
|
||||
// ACL, and container storage is the one place in the home that wants ordinary mode bits and nothing else.
|
||||
//
|
||||
// AFTER the recursive grant above, or what it just set is re-inherited here.
|
||||
const stripped = await run(['sudo', '-n', 'setfacl', '-R', '-b', composeDir]);
|
||||
if (!stripped.ok) {
|
||||
return { ok: false, error: `could not clear inherited ACLs from ${composeDir}: ${stripped.out}` };
|
||||
}
|
||||
// // `-b`, not `-k`: remove ACCESS entries as well as defaults, leaving plain POSIX modes.
|
||||
// //
|
||||
// // `-k` alone left `mask::---` behind — inherited named entries with every permission masked off,
|
||||
// // reading as `user:pastilhas:rwx #effective:---`. An ACL that says one thing and means another is worse
|
||||
// // than no ACL, and container storage is the one place in the home that wants ordinary mode bits and
|
||||
// // nothing else.
|
||||
// //
|
||||
// // AFTER the recursive grant above, or what it just set is re-inherited here.
|
||||
// const stripped = await run(['sudo', '-n', 'setfacl', '-R', '-b', composeDir]);
|
||||
// if (!stripped.ok) {
|
||||
// return { ok: false, error: `could not clear inherited ACLs from ${composeDir}: ${stripped.out}` };
|
||||
// }
|
||||
|
||||
return { ok: true };
|
||||
} catch (ex) {
|
||||
|
||||
Reference in New Issue
Block a user