give each member a postgres role instead of a docker daemon
A Postgres login role named the same as their Linux account, with CREATEDB, plus a
~/.pgpass so psql never prompts. This is what replaces rootless Docker: the case it
was really there for was "let me run a database to develop against", and a container
per member answered it with a daemon, an image cache and a subuid range each.
Measured on postgres:18-alpine before writing any of it, because three things I
asserted turned out to be wrong:
- a fresh LOGIN role CAN connect to `officer` (datacl NULL = PUBLIC has CONNECT),
but CANNOT read any application table — privileges are owner-only, so
has_table_privilege('users','UPDATE') is false. The capability model was never
reachable from here.
- the `trust` line in pg_hba does not cover host connections: Docker's NAT rewrites
the source, so they fall through to scram-sha-256. Verified with a wrong password.
- revoking from the ROLE does nothing. Privileges are additive and there is no DENY;
only revoking from PUBLIC is a lock.
So ensureAppDatabaseClosed revokes CONNECT+TEMPORARY on the platform's own database
from PUBLIC, and it runs inside provisionPostgresRole rather than in the setup script
— an install set up before today, or restored from a dump, then still cannot end up
with a member who can connect to `officer`.
Password is generated per member, 40 chars, rejection-sampled over an alphanumeric
alphabet: CREATE ROLE is a utility statement and cannot take a bind parameter, so the
safety comes from the alphabet rather than from escaping. Not stored anywhere — it
lives in their 600 ~/.pgpass, the same posture as their SSH key, where we keep only
the public half. Only (re)set when .pgpass is missing, so a reprovision does not
rotate a credential they may have pasted into an app config.
KNOWN RESIDUE, not handled: a database one member creates is metadata-readable by
another. datacl is not inherited from the template (measured: closing template1 and
creating from it still produced NULL), and CREATE DATABASE fires no event trigger, so
nothing can close it at creation. A second member can read table and column NAMES from
the catalogue. They cannot read a row and cannot create anything. Closing it needs a
sweep or a pg_hba rule per member; both are decisions, not details.
Verified live against the running cluster: role creation, refusal on `officer`,
creating and using two databases, and the DROP ... WITH (FORCE) teardown. All probe
roles and databases dropped afterwards.
NOT verified: bunx tsgo, still — node_modules is empty in this tree. The drizzle
return shape was checked by reading PostgresJsQueryResultHKT (RowList<T[]>, extends
Array) rather than by running it.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -105,11 +105,31 @@ else
|
||||
%F{blue}❯%f '
|
||||
fi
|
||||
|
||||
# ── Docker ──
|
||||
# Your own rootless daemon, if Officer provisioned one. Containers you start run as your account in your own
|
||||
# user namespace — root inside them is you outside them, and you cannot see anyone else's containers.
|
||||
# ── Postgres ──
|
||||
# Officer gave your account a Postgres role named the same as your login, and a ~/.pgpass holding its
|
||||
# password — so psql never prompts you and you never have to know it.
|
||||
#
|
||||
# Set from $XDG_RUNTIME_DIR rather than a hard-coded uid so this line is the same in every account's file.
|
||||
# createdb myapp as many as you like; each one is yours
|
||||
# psql myapp connect to one
|
||||
# dropdb myapp
|
||||
#
|
||||
# Nobody else can read your data, and you cannot reach Officer's own database.
|
||||
#
|
||||
# PGHOST is set because Postgres runs in a container published on loopback: there is no unix socket on
|
||||
# this machine, and without this psql fails with "No such file or directory", which reads like Postgres
|
||||
# is not installed rather than like it is one flag away.
|
||||
#
|
||||
# Read out of ~/.pgpass rather than hard-coded, for the same reason the PATH lines above are $HOME-relative
|
||||
# — one fact, in one place, that cannot come to disagree with itself.
|
||||
if [ -f "$HOME/.pgpass" ]; then
|
||||
export PGHOST="${$(head -1 "$HOME/.pgpass")%%:*}"
|
||||
export PGPORT="$(head -1 "$HOME/.pgpass" | cut -d: -f2)"
|
||||
fi
|
||||
|
||||
# ── Docker ──
|
||||
# Officer no longer provisions a rootless daemon per account (2026-08-13) — a Postgres role covers the
|
||||
# case it was really there for, at none of the cost. Kept because it costs one stat and does the right
|
||||
# thing if you install rootless Docker yourself.
|
||||
if [ -S "${XDG_RUNTIME_DIR:-/run/user/$(id -u)}/docker.sock" ]; then
|
||||
export DOCKER_HOST="unix://${XDG_RUNTIME_DIR:-/run/user/$(id -u)}/docker.sock"
|
||||
fi
|
||||
|
||||
Reference in New Issue
Block a user