Review-bot PR #510 нашёл P0: `set_config(name,value,is_local)` возвращает установленное значение, psql печатает rows на stdout без `-q`/`-A`/`-t`, итог — фактический pw появляется в Forgejo Actions deploy logs (retained, visible всем с repo read access). Fix: оборачиваем оба `SELECT set_config(...)` в `\o /dev/null` / `\o` brackets. Output mutes только для этих строк — NOTICE-сообщения из DO block (idempotency signal 'tradein_fdw_reader password set') остаются видимыми. Также добавлен rollback hint в комментарии: НЕ revert файл (вернёт сломанный :'pw' внутри $$), а unset GENDESIGN_FDW_PASSWORD в .env.runtime на VPS.
51 lines
2.8 KiB
SQL
51 lines
2.8 KiB
SQL
-- Set tradein_fdw_reader password from env.
|
||
-- Applied by .forgejo/workflows/deploy.yml after main DB migrations:
|
||
-- psql -v pw="$GENDESIGN_FDW_PASSWORD" -f ops/db-bootstrap/set_tradein_fdw_password.sql
|
||
--
|
||
-- Idempotent: ALTER if role exists, NOTICE and continue if missing
|
||
-- (migration 100_tradein_fdw_role.sql might not have applied yet on first deploy).
|
||
-- Password is NEVER stored in this file or in git — only the variable name.
|
||
--
|
||
-- Format %L escapes the password as a SQL string literal — safe even with quotes.
|
||
-- Combined with the regex whitelist on the backend side (fdw.py _PASSWORD_RE)
|
||
-- this gives defense-in-depth.
|
||
--
|
||
-- psql variable substitution (:'pw') НЕ интерполируется внутри dollar-quoted
|
||
-- блока ($$...$$) — это правило psql, не bug. Поэтому password передаём в DO
|
||
-- через сессионный GUC (set_config), который psql интерполирует ВНЕ dollar
|
||
-- quote, и читаем внутри через current_setting().
|
||
-- Reference incident: deploy 2026-05-24 (post-merge PR #503) упал на
|
||
-- "syntax error at or near ':'" в LINE 4 этого файла — :'pw' дошёл до сервера
|
||
-- as literal вместо интерполяции.
|
||
--
|
||
-- ⚠️ `set_config(name, value, is_local) -> text` ВОЗВРАЩАЕТ установленное
|
||
-- значение. Без `\o /dev/null` бы psql напечатал password на stdout → leak в
|
||
-- Forgejo Actions deploy logs (retained, visible всем с repo read access).
|
||
-- Поэтому оборачиваем оба set_config вызова в `\o /dev/null` / `\o` brackets
|
||
-- — output mutes только для этих строк, NOTICE-сообщения из DO block
|
||
-- (idempotency signal) остаются видимыми.
|
||
--
|
||
-- Rollback path: НЕ revert этого файла (вернёт сломанный `:'pw'` внутри $$).
|
||
-- Корректный rollback — unset GENDESIGN_FDW_PASSWORD в /opt/gendesign/backend
|
||
-- /.env.runtime на VPS, deploy.yml тогда пропустит этот шаг полностью.
|
||
|
||
\o /dev/null
|
||
SELECT set_config('app.fdw_pw', :'pw', false);
|
||
\o
|
||
|
||
DO $$
|
||
BEGIN
|
||
IF EXISTS (SELECT 1 FROM pg_roles WHERE rolname = 'tradein_fdw_reader') THEN
|
||
EXECUTE format('ALTER ROLE tradein_fdw_reader WITH PASSWORD %L', current_setting('app.fdw_pw'));
|
||
RAISE NOTICE 'tradein_fdw_reader password set';
|
||
ELSE
|
||
RAISE NOTICE 'tradein_fdw_reader role missing — migration 100_tradein_fdw_role.sql not applied yet';
|
||
END IF;
|
||
END $$;
|
||
|
||
-- Clear GUC after use (defense-in-depth — не оставляем password в session state
|
||
-- даже на short connection). Same \o trick — set_config return value is empty
|
||
-- string here, но всё равно лишний row в stdout не нужен.
|
||
\o /dev/null
|
||
SELECT set_config('app.fdw_pw', '', false);
|
||
\o
|