Skip to main content

Module pg_client

Module pg_client 

Source
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§

MysqlConnection
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 to run_ephemeral(image, argv, env, …).

Functions§

mysql_admin_invocation
Build a mysql invocation 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 atomic RENAME TABLE swap, DROP DATABASE, and the information_schema table enumeration the swap is built from. No default database is named (so CREATE DATABASE works and every object is schema-qualified in the SQL we build), -N --batch give header-less, tab-separated output for the enumeration query. Host/port/user explicit; password in MYSQL_PWD.
mysql_restore_invocation
Build the mysql invocation 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 own USE <db> (from --databases), so no positional db is needed; a --data-only dump 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 mysql invocation 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 a LANGUAGE-escalating routine) fails at apply. The dump (which carries NO USE) is fed on stdin and lands in shadow_db because it is named as the default database positionally. Host/port/user explicit; password in MYSQL_PWD; shadow_db is a driver-derived identifier (not a credential), so argv is correct.
mysqlbinlog_capture_invocation
Build the mysqlbinlog --read-from-remote-server invocation that CAPTURES + DB-SCOPES + REWRITES the binlog delta (SQL true-delta drain). Connects to the live server like a replica (no filesystem access to the data dir) as the EPHEMERAL least-privilege capture user (REPLICATION SLAVE, REPLICATION CLIENT only), reading from start_log (the base dump’s recorded MASTER_LOG_FILE) --to-last-log, and DECODES to stdout (NOT --raw) — the member IS this SQL stream (BINLOG '…' row events), directly pipeable to mysql at replay with no re-decode. The flags are the SECURITY + correctness core:
mysqldump_capture_base_invocation
Build the tables-only, SNAPSHOT-PINNED mysqldump for the MySQL binlog TRUE-DELTA base (SQL true-delta program — the mysql analogue of the pg capture slot). Identical to mysqldump_tables_only_invocation (tables-only so it restores into a differently-named shadow) EXCEPT it records the dump’s consistent-snapshot binlog coordinate with --source-data=2 --set-gtid-purged=COMMENTED:
mysqldump_invocation
Build the mysqldump invocation for an EXPORT. --single-transaction takes a consistent snapshot without locking (InnoDB); --no-tablespaces avoids the PROCESS privilege requirement. Host/port/user are explicit (conn_args) because mysqldump ignores MYSQL_HOST. A FULL dump emits schema + data under --databases <db> (so the stream carries CREATE DATABASE/USE); --data-only emits only the rows for <db> (--no-create-info), restored into the target’s existing schema. No --connect-timeout: mysqldump does not register it (unknown variable); the overall ephemeral-run timeout bounds a stuck dump instead.
mysqldump_tables_only_invocation
Build the tables-only FULL mysqldump for the inc2 chunked/shadow path (Stage D). Unlike the inc1 FULL dump (mysqldump_invocation with --databases <db>, which emits CREATE 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 carries CREATE TABLE + data and NO CREATE DATABASE/USE. That lets mysql <shadow> (mysql_shadow_invocation) apply it into a freshly-created shadow database whose name differs from the source. --single-transaction takes a consistent InnoDB snapshot without locking; --no-tablespaces avoids the PROCESS privilege. Host/port/user are explicit (mysqldump ignores MYSQL_HOST); the password stays in MYSQL_PWD.
pg_dump_invocation
Build the pg_dump invocation for an EXPORT. Custom format (-Fc): compressed and the natural input to pg_restore. --data-only emits 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-only dump would carry its rows and a pg_restore --data-only --exit-on-error into 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 own boatramp_migrations ledger intact (the stated property).
pg_restore_invocation
Build the pg_restore invocation for an IMPORT (reads the custom-format dump from stdin). --data-only restores only the data into the target’s existing migrated schema; --exit-on-error aborts loudly on the first failed object. Unlike psql/ pg_dump, pg_restore does NOT infer the target from PGDATABASE — 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_restore invocation that applies the attacker-crafted bundle dump into a pre-created SHADOW database (inc2 Stage C) — the UNTRUSTED side of the atomic-shadow split.
pg_server_major
Parse the server’s major version from the integer SHOW server_version_num banner (major*10000 + minor, PG10+: "160004" → 16, "170000" → 17). Returns None for a banner we cannot parse (the caller then stays on the conservative --no-comments path).
psql_invocation
Build a psql invocation 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, the ALTER DATABASE … RENAME swap, DROP DATABASE — never a byte of the attacker-crafted bundle dump (that is applied by pg_restore_shadow_invocation as the NON-superuser target role).