thiagowfx's avatar

Β¬ just serendipity πŸ€ (not just serendipity)

ssh: pipe SQL through doas with base64

β€’ 353 words β€’ 2 min

Problem statement: nested shell quotes made a remote PostgreSQL update harder to run than the update itself.

The target is an Alpine Linux host. Its Miniflux configuration contains the database connection string, so psql must run after sourcing /etc/miniflux.conf through doas.

An inline command quickly becomes a quoting puzzle. SQL contains single quotes, Unicode, and sometimes apostrophes. The local shell also expands unquoted characters before ssh sends anything to the server.

I wrote SQL to a temporary file, encoded it locally, and sent only base64 over SSH:

shell
% base64 /tmp/miniflux-flags.sql | tr -d '\n' | \
  ssh knol 'doas sh -c '\''
    . /etc/miniflux.conf
    base64 -d | psql "$DATABASE_URL" -X -v ON_ERROR_STOP=1
  '\''

Remote shell receives a simple command. base64 -d reconstructs SQL on the server. psql reads it from standard input. ON_ERROR_STOP=1 makes a SQL error stop the command instead of continuing to later statements.

The file contained real, guarded updates:

sql
BEGIN;
UPDATE feeds SET title = title || ' πŸ‡ΊπŸ‡Έ'
WHERE id = 1394 AND title = 'furbo.org: Craig Hockenberry'
RETURNING id, title;
UPDATE feeds SET title = title || ' πŸ‡¨πŸ‡¦'
WHERE id = 1406 AND title = 'Ansuz: Matthew Skala'
RETURNING id, title;
UPDATE feeds SET title = title || ' πŸ‡¬πŸ‡§'
WHERE id = 1411 AND title = 'Ian Jackson'
RETURNING id, title;
-- eight more exact-title guards
COMMIT;

The remote output stayed useful:

text
BEGIN
 id  |              title
-----+---------------------------------
1394 | furbo.org: Craig Hockenberry πŸ‡ΊπŸ‡Έ
(1 row)
UPDATE 1
...
UPDATE 1
COMMIT

A second query verified every ID after commit:

text
 id  |              title
-----+---------------------------------
1394 | furbo.org: Craig Hockenberry πŸ‡ΊπŸ‡Έ
1406 | Ansuz: Matthew Skala πŸ‡¨πŸ‡¦
1411 | Ian Jackson πŸ‡¬πŸ‡§
1444 | Matthew Garrett: mjg59 πŸ‡¬πŸ‡§
1459 | Cloudscaling by Randy Bias πŸ‡ΊπŸ‡Έ
1460 | Marcin Juszkiewicz πŸ‡΅πŸ‡±
1464 | Liss is More by Casey Liss πŸ‡ΊπŸ‡Έ
1472 | Jerod Santo πŸ‡ΊπŸ‡Έ
1482 | Matthias Kirschner πŸ‡©πŸ‡ͺ
1545 | Charles Leifer πŸ‡ΊπŸ‡Έ
(10 rows)

Base64 does not encrypt anything. It only moves one opaque payload through several shells. That is enough to keep local parsing away from SQL.


πŸ€– Drafted with /bloggify. ∎