Expand description
Pure builders for the Postgres export/import client invocation (argv + PG* env);
the credential non-leak rule (password in env, never argv) lives here.
Pure builders for the SQL export/import client invocation (Postgres + MySQL) that
the driver runs inside
run_ephemeral: the argv
and the process environment.
§The credential non-leak rule lives here
The password rides in an environment variable (PGPASSWORD for Postgres,
MYSQL_PWD for MySQL), NEVER in argv. Both argv and the container environment are
visible to a privileged docker inspect, but argv leaks far more broadly — it shows
in every process listing (ps, /proc/<pid>/cmdline) inside and outside the
container, to any local observer, no daemon privilege required — whereas the
container environment is only reachable with daemon/inspect access. Env is therefore
the smaller exposure (not zero), and it is the right choice over argv. No DSN string
is assembled at all: the discrete PG* / MYSQL* connection variables connect the
client, so there is no connection string to accidentally format! into a log. The
only secret is the password env var.
§--data-only vs full
--data-only emits/restores ONLY the table data; the target keeps its OWN migrated
schema (Phase 1 is data-only: app data, no identity/RBAC/routing). A full export
carries schema + data. --no-owner/--no-privileges keep a restore from trying to
reassign ownership/ACLs across environments, and pg_restore --exit-on-error makes a
failed object abort loudly rather than leave a half-restored database.
Kept pure so the data-only divergence and the cred-placement invariant are unit-mutation-gated in the merge gate.
Structs§
- Mysql
Connection - A resolved MySQL connection target for the client. The password goes in
MYSQL_PWD(the env the mysql/mysqldump clients read), never argv. - PgConnection
- A resolved Postgres connection target for the client. Assembled by the driver from
the sealed-credential unseal + the resolved replica endpoint; consumed only to emit
the
PG*environment. - SqlInvocation
- A fully-built client invocation: the
argv(no credential) + the environment (the credential), ready to hand torun_ephemeral(image, argv, env, …).
Functions§
- mysql_
admin_ invocation - Build a
mysqlinvocation that runs trusted, we-generate-it SQL fed on stdin — the SUPERUSER / maintenance side of the inc2 mysql shadow split (Stage D):CREATE DATABASE <shadow>, the atomicRENAME TABLEswap,DROP DATABASE, and theinformation_schematable enumeration the swap is built from. No default database is named (soCREATE DATABASEworks and every object is schema-qualified in the SQL we build),-N --batchgive header-less, tab-separated output for the enumeration query. Host/port/user explicit; password inMYSQL_PWD. - mysql_
restore_ invocation - Build the
mysqlinvocation for an IMPORT (reads the SQL dump from stdin). Host/port/ user are explicit (conn_args) so the client connects over TCP, not the local socket. A FULL bundle’s dump carries its ownUSE <db>(from--databases), so no positional db is needed; a--data-onlydump has none, so the target<db>is named on the command line. The database name is NOT a credential, so passing it in argv is fine. - mysql_
shadow_ invocation - Build the
mysqlinvocation that applies the attacker-crafted tables-only dump into a pre-created SHADOW database by NAME (inc2 Stage D) — the UNTRUSTED side of the atomic-shadow split. Meant to run as the TARGET PROJECT’S own NON-SUPERUSER role (the driver resolves that credential) so a crafted statement needing elevated privileges (LOAD DATA INFILE,INTO OUTFILE, creating aLANGUAGE-escalating routine) fails at apply. The dump (which carries NOUSE) is fed on stdin and lands inshadow_dbbecause it is named as the default database positionally. Host/port/user explicit; password inMYSQL_PWD;shadow_dbis a driver-derived identifier (not a credential), so argv is correct. - mysqldump_
invocation - Build the
mysqldumpinvocation for an EXPORT.--single-transactiontakes a consistent snapshot without locking (InnoDB);--no-tablespacesavoids thePROCESSprivilege requirement. Host/port/user are explicit (conn_args) becausemysqldumpignoresMYSQL_HOST. A FULL dump emits schema + data under--databases <db>(so the stream carriesCREATE DATABASE/USE);--data-onlyemits only the rows for<db>(--no-create-info), restored into the target’s existing schema. No--connect-timeout:mysqldumpdoes not register it (unknown variable); the overall ephemeral-run timeout bounds a stuck dump instead. - mysqldump_
tables_ only_ invocation - Build the tables-only FULL
mysqldumpfor the inc2 chunked/shadow path (Stage D). Unlike the inc1 FULL dump (mysqldump_invocationwith--databases <db>, which emitsCREATE DATABASE/USE <original>and so TARGETS THE ORIGINAL NAME), this dumps the db’s TABLES ONLY — a positional<db>with NO--databases— so the stream carriesCREATE TABLE+ data and NOCREATE DATABASE/USE. That letsmysql <shadow>(mysql_shadow_invocation) apply it into a freshly-created shadow database whose name differs from the source.--single-transactiontakes a consistent InnoDB snapshot without locking;--no-tablespacesavoids thePROCESSprivilege. Host/port/user are explicit (mysqldumpignoresMYSQL_HOST); the password stays inMYSQL_PWD. - pg_
dump_ invocation - Build the
pg_dumpinvocation for an EXPORT. Custom format (-Fc): compressed and the natural input topg_restore.--data-onlyemits only the data AND EXCLUDES the host-owned migration ledger’s rows (boatramp_migrations.*): the ledger is a schema INSIDE the tenant database, so a plain--data-onlydump would carry its rows and apg_restore --data-only --exit-on-errorinto the already-migrated target (the matching-fingerprint case — the PRIMARY use) would hit a primary-key conflict on the ledger and abort. Excluding it keeps the target’s ownboatramp_migrationsledger intact (the stated property). - pg_
restore_ invocation - Build the
pg_restoreinvocation for an IMPORT (reads the custom-format dump from stdin).--data-onlyrestores only the data into the target’s existing migrated schema;--exit-on-erroraborts loudly on the first failed object. Unlikepsql/pg_dump,pg_restoredoes NOT infer the target fromPGDATABASE— without-d(restore into a database) or-f(emit a script) it refuses with “one of -d/–dbname and -f/–file must be specified”; so the target database is named with-d(the host/ port/user/password still come from the env, and the db name is not a credential). - pg_
restore_ shadow_ invocation - Build the
pg_restoreinvocation that applies the attacker-crafted bundle dump into a pre-created SHADOW database (inc2 Stage C) — the UNTRUSTED side of the atomic-shadow split. - psql_
invocation - Build a
psqlinvocation that runs one trusted, we-generate-it DDL/utility statement (inc2 Stage C). This is the SUPERUSER / maintenance side of the atomic-shadow split: it runs ONLY our own generated control SQL —CREATE DATABASE <shadow>,pg_terminate_backend, theALTER DATABASE … RENAMEswap,DROP DATABASE— never a byte of the attacker-crafted bundle dump (that is applied bypg_restore_shadow_invocationas the NON-superuser target role).