put rootless docker back, behind the developer gate
Uncommented at all three sites: the call and import in provisionOsAccount, the ~/.local/dockers bind-mount directory in confineUserTree, and the DOCKER_HOST block in the member zshrc. Not restored unconditionally, which is how it was before. It now sits behind the same Developer check as the Postgres role — the gate that prompted disabling it in the first place. So rolePermitsDatabase is renamed rolePermitsDevTools: it gates two things now and a name saying "database" while deciding whether you get containers is the kind of comment that goes stale silently. ~/.local/dockers is created for EVERY account rather than only Developers. It is two install calls, and confineUserTree is the function that places the layout, not the one that knows who is a Developer — so a member promoted later finds it already correct. Postgres role work is untouched and still in place. Verified: transpiles. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -3,7 +3,7 @@ import { getUsers, getUserById, updateUser, deleteUser, USER_ROLES, OWNER_USER_I
|
||||
import type { UserRole } from 'officerdb';
|
||||
import * as errors from '@@/custom-errors';
|
||||
import { deprovisionOsAccount } from '@@/os-user-deprovision';
|
||||
import { dropPostgresRole, provisionPostgresRole, rolePermitsDatabase } from '@@/os-user-postgres';
|
||||
import { dropPostgresRole, provisionPostgresRole, rolePermitsDevTools } from '@@/os-user-postgres';
|
||||
import { lookupOsUser } from '@@/os-user';
|
||||
import { DATA_PATH } from '@@/data-path';
|
||||
|
||||
@@ -90,7 +90,7 @@ export const updateUserRoleHandler: Handler = async function (ctx) {
|
||||
|
||||
// ── The database role follows the platform role ──
|
||||
//
|
||||
// Without this the gate in `rolePermitsDatabase` is decorative: it would decide what a Developer gets at
|
||||
// Without this the gate in `rolePermitsDevTools` is decorative: it would decide what a Developer gets at
|
||||
// creation and then never look again, so demoting one would leave their Postgres role, their databases
|
||||
// and a working password in their ~/.zshenv. A permission that survives its own revocation is worse than
|
||||
// not having gated it, because the UI then says something untrue.
|
||||
@@ -98,8 +98,8 @@ export const updateUserRoleHandler: Handler = async function (ctx) {
|
||||
// REVOKE BEFORE RECORDING, grant after. Dropping first means a failure aborts with the role unchanged, so
|
||||
// the account still says Developer and the whole thing can be retried. Doing it the other way round would
|
||||
// leave a Member holding database access with nothing in the row to indicate it.
|
||||
const had = rolePermitsDatabase(existing.role);
|
||||
const wants = rolePermitsDatabase(role as UserRole);
|
||||
const had = rolePermitsDevTools(existing.role);
|
||||
const wants = rolePermitsDevTools(role as UserRole);
|
||||
|
||||
if (had && !wants && existing.osUser) {
|
||||
const dropped = await dropPostgresRole(existing.osUser);
|
||||
|
||||
@@ -4,9 +4,8 @@ import { ensureOsUser, osUserHome } from '@@/os-user';
|
||||
import { provisionSshAccess } from '@@/os-user-ssh';
|
||||
import { seedShellConfig } from '@@/os-user-shell';
|
||||
import { provisionClaudeCli } from '@@/os-user-claude';
|
||||
import { provisionPostgresRole, rolePermitsDatabase } from '@@/os-user-postgres';
|
||||
// Disabled 2026-08-13 — see the commented-out step in provisionOsAccount below.
|
||||
// import { provisionRootlessDocker } from '@@/os-user-docker';
|
||||
import { provisionPostgresRole, rolePermitsDevTools } from '@@/os-user-postgres';
|
||||
import { provisionRootlessDocker } from '@@/os-user-docker';
|
||||
import { provisionUserDirs } from '@@/data-path';
|
||||
|
||||
// Giving an account its Linux side: the directory skeleton, the Linux user, the confinement, the keys.
|
||||
@@ -42,7 +41,7 @@ export async function provisionOsAccount(params: {
|
||||
userId: number;
|
||||
email: string;
|
||||
username: string;
|
||||
/** Decides whether they get a Postgres role. See `rolePermitsDatabase`. */
|
||||
/** Decides whether they get a Postgres role. See `rolePermitsDevTools`. */
|
||||
role: UserRole;
|
||||
/** Inbound SSH key for `authorized_keys`. Already validated by the caller. */
|
||||
inboundKey?: string | null;
|
||||
@@ -84,7 +83,7 @@ export async function provisionOsAccount(params: {
|
||||
|
||||
// A Postgres login role of the same name, with CREATEDB — for a Developer, and nobody else. What
|
||||
// replaced rootless Docker for the "let me run a database to develop against" case, and it inherits
|
||||
// that feature's role gate along with its purpose: `rolePermitsDatabase` is the one place that rule is
|
||||
// that feature's role gate along with its purpose: `rolePermitsDevTools` is the one place that rule is
|
||||
// written down.
|
||||
//
|
||||
// Also the step that shuts PUBLIC out of the platform's own database — deliberately inside the function
|
||||
@@ -93,7 +92,7 @@ export async function provisionOsAccount(params: {
|
||||
//
|
||||
// Null, not a skipped-but-ok result: "there is no database role because of who they are" and "the
|
||||
// database role worked" are different answers and the caller reports them differently.
|
||||
const postgres = rolePermitsDatabase(params.role)
|
||||
const postgres = rolePermitsDevTools(params.role)
|
||||
? await provisionPostgresRole({
|
||||
email: params.email,
|
||||
osUser: account.osUser,
|
||||
@@ -102,24 +101,21 @@ export async function provisionOsAccount(params: {
|
||||
})
|
||||
: null;
|
||||
|
||||
// ── Rootless Docker: DISABLED 2026-08-13, code kept ──
|
||||
// Their own rootless Docker daemon — for a Developer, and nobody else. Last, and the most tolerant of
|
||||
// failure: a host without the uidmap package or a kernel that will not do rootless still gets a perfectly
|
||||
// good account, minus containers.
|
||||
//
|
||||
// Every member got their own rootless daemon, unconditionally, on the argument that "can I run a database
|
||||
// to develop against" should not be an administrative request. The cost is the counter-argument and it is
|
||||
// per member, not per install: one daemon, one image cache, one subuid range — see os-user-docker.ts.
|
||||
//
|
||||
// Turned off at the call rather than deleted, so turning it back on is uncommenting this block and the
|
||||
// import. `os-user-docker.ts` is otherwise unreferenced now; nothing else calls it.
|
||||
//
|
||||
// Note this only stops NEW accounts. An account provisioned before today keeps its daemon, and
|
||||
// `deprovisionOsAccount` still cleans one up, which is what an existing member needs.
|
||||
//
|
||||
// const docker = await provisionRootlessDocker({
|
||||
// osUser: account.osUser,
|
||||
// uid: account.uid,
|
||||
// gid: account.gid,
|
||||
// home: osUserHome(params.email),
|
||||
// });
|
||||
// Behind the role gate rather than unconditional. The cost is real and it is per member, not per install:
|
||||
// one daemon, one image cache and one subuid range each — see os-user-docker.ts. That is worth paying for
|
||||
// someone whose job is to build things here, and not for an account that just reads its dashboards.
|
||||
const docker = rolePermitsDevTools(params.role)
|
||||
? await provisionRootlessDocker({
|
||||
osUser: account.osUser,
|
||||
uid: account.uid,
|
||||
gid: account.gid,
|
||||
home: osUserHome(params.email),
|
||||
})
|
||||
: null;
|
||||
|
||||
// The Linux account is recorded either way: it exists, it is confined, and a member's terminal can run as
|
||||
// it. Only the keys are missing, and that is what the error says.
|
||||
@@ -129,7 +125,7 @@ export async function provisionOsAccount(params: {
|
||||
// Reported in order of consequence, not in order of execution: no keys matters more than a plain prompt,
|
||||
// which matters more than no containers. Only one is surfaced because the UI shows one line — the rest are
|
||||
// in the log.
|
||||
for (const step of [claude, shell, postgres]) {
|
||||
for (const step of [claude, shell, postgres, docker]) {
|
||||
if (step && !step.ok) console.warn(`[users] ${params.email}: ${step.error}`);
|
||||
}
|
||||
const error = !ssh.ok
|
||||
@@ -140,6 +136,8 @@ export async function provisionOsAccount(params: {
|
||||
? shell.error
|
||||
: postgres && !postgres.ok
|
||||
? postgres.error
|
||||
: null;
|
||||
: docker && !docker.ok
|
||||
? docker.error
|
||||
: null;
|
||||
return { osUser: account.osUser, sshPublicKey, error };
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user