Merge pull request 'fix(ops/metrics): postgres-exporter печатал пароль БД в лог при каждой ошибке' (#3144) from fix/3114-pg-exporter-leaks-dsn into main
All checks were successful
Deploy / changes (push) Successful in 8s
Deploy / build-frontend (push) Has been skipped
Deploy / deploy-caddy (push) Has been skipped
Deploy Metrics / server (push) Successful in 16s
Deploy Metrics / agent-apps (push) Successful in 22s
Deploy Metrics / agent-infra (push) Successful in 25s
Deploy / build-worker (push) Successful in 40s
Deploy / build-backend (push) Successful in 40s
Deploy / deploy (push) Successful in 1m3s
Deploy / deploy-status (push) Successful in 1s
Deploy / perimeter-smoke (push) Successful in 11s
All checks were successful
Deploy / changes (push) Successful in 8s
Deploy / build-frontend (push) Has been skipped
Deploy / deploy-caddy (push) Has been skipped
Deploy Metrics / server (push) Successful in 16s
Deploy Metrics / agent-apps (push) Successful in 22s
Deploy Metrics / agent-infra (push) Successful in 25s
Deploy / build-worker (push) Successful in 40s
Deploy / build-backend (push) Successful in 40s
Deploy / deploy (push) Successful in 1m3s
Deploy / deploy-status (push) Successful in 1s
Deploy / perimeter-smoke (push) Successful in 11s
This commit is contained in:
commit
f469903adf
2 changed files with 89 additions and 3 deletions
67
backend/tests/ops/test_3114_pg_exporter_version.py
Normal file
67
backend/tests/ops/test_3114_pg_exporter_version.py
Normal file
|
|
@ -0,0 +1,67 @@
|
|||
"""postgres-exporter не должен печатать пароль БД в лог.
|
||||
|
||||
ЧТО БЫЛО. Версия v0.16.0 при КАЖДОЙ ошибке сбора печатала полный DSN вместе с
|
||||
паролем:
|
||||
|
||||
msg="error scraping dsn" err="queryNamespaceMappings returned 1 errors"
|
||||
dsn="postgresql://<роль>:<ПАРОЛЬ>@<хост>:5432/<база>?sslmode=disable"
|
||||
|
||||
Строки уходили в Loki — около 210 в сутки с двух хостов, при ретенции 30 дней
|
||||
это тысячи паролей в хранилище логов (#3114).
|
||||
|
||||
ЧТО ПРОВЕРЕНО ОПЫТОМ 27.08, а не взято из документации. Стенд: чистый постгрес
|
||||
в скретч-контейнере, роль без прав, тот же `ops/metrics/postgres/queries.yml`,
|
||||
что и на проде — то есть ровно та ошибка, что случалась в бою.
|
||||
|
||||
v0.16.0 → строк с паролем: 1
|
||||
v0.18.0 → строк с паролем: 0
|
||||
|
||||
Отдельно опровергнут обходной путь, который предлагался в issue: форма
|
||||
`DATA_SOURCE_URI` + `DATA_SOURCE_USER` + `DATA_SOURCE_PASS` НЕ помогает —
|
||||
экспортер собирает DSN внутри и печатает его целиком точно так же (тот же
|
||||
стенд, тот же результат: 1 строка с паролем). То есть починка «через
|
||||
переменные окружения» была бы работой вхолостую.
|
||||
|
||||
Паритет метрик проверен там же: все пять пользовательских запросов отдаются
|
||||
обеими версиями одинаково, v0.18 добавляет три встроенные и не теряет ни одной.
|
||||
|
||||
Тест сторожит только нижнюю границу версии: откат на v0.16.x вернул бы утечку.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import re
|
||||
from pathlib import Path
|
||||
|
||||
REPO_ROOT = Path(__file__).resolve().parents[3]
|
||||
COMPOSE = REPO_ROOT / "docker-compose.metrics-agent.yml"
|
||||
|
||||
# Первая версия, в которой пароль в лог не попадает (проверено опытом, см. шапку).
|
||||
MIN_SAFE = (0, 18, 0)
|
||||
|
||||
_TAG = re.compile(r"postgres-exporter:v(\d+)\.(\d+)\.(\d+)")
|
||||
|
||||
|
||||
def _versions() -> list[tuple[int, int, int]]:
|
||||
text = COMPOSE.read_text(encoding="utf-8")
|
||||
return [(int(a), int(b), int(c)) for a, b, c in _TAG.findall(text)]
|
||||
|
||||
|
||||
def test_all_three_exporters_are_pinned() -> None:
|
||||
"""Экспортеров три — на всех трёх версия должна быть явной.
|
||||
|
||||
Страховка от того, что тест ниже начнёт проверять пустоту: если кто-то
|
||||
заменит тег на `latest` или удалит сервис, здесь станет видно.
|
||||
"""
|
||||
assert len(_versions()) == 3, (
|
||||
f"ожидались три закреплённые версии postgres-exporter, найдено: {_versions()}"
|
||||
)
|
||||
|
||||
|
||||
def test_no_exporter_older_than_the_leaking_one() -> None:
|
||||
"""Ни один экспортер не смеет быть старше версии, где утечка закрыта."""
|
||||
for v in _versions():
|
||||
assert v >= MIN_SAFE, (
|
||||
f"postgres-exporter v{'.'.join(map(str, v))} печатает пароль БД в лог "
|
||||
f"при каждой ошибке сбора — минимум v{'.'.join(map(str, MIN_SAFE))}"
|
||||
)
|
||||
|
|
@ -163,8 +163,27 @@ services:
|
|||
# ── postgres-exporter: Птица (только на Poincare) ────────────────────────────
|
||||
# WAL/сутки, n_tup_upd против n_tup_hot_upd, рост TOAST, коннекты, горизонт
|
||||
# vacuum — то, из-за отсутствия чего раздутие копилось 91 день незамеченным.
|
||||
# v0.18.0, а не v0.16.0: та печатала ПОЛНЫЙ DSN вместе с паролем в каждую
|
||||
# ошибку сбора (#3114). Строка вида
|
||||
# msg="error scraping dsn" err="queryNamespaceMappings returned 1 errors"
|
||||
# dsn="postgresql://<роль>:<ПАРОЛЬ>@<хост>:5432/<база>?sslmode=disable"
|
||||
# уходила в Loki при каждом скрейпе — около 210 строк в сутки с двух хостов,
|
||||
# при ретенции 30 дней это тысячи паролей в хранилище логов.
|
||||
#
|
||||
# Воспроизведено на скретч-контейнерах 27.08 (постгрес + роль без прав +
|
||||
# этот же queries.yml): v0.16.0 печатает пароль, v0.18.0 — нет.
|
||||
#
|
||||
# NB: форма DATA_SOURCE_URI + DATA_SOURCE_USER + DATA_SOURCE_PASS, которую
|
||||
# предлагали как обходной путь, НЕ помогает — проверено тем же опытом:
|
||||
# экспортер собирает DSN внутри и печатает его целиком точно так же.
|
||||
# Утечку закрывает только версия образа.
|
||||
#
|
||||
# Паритет метрик проверен на том же стенде: все пять пользовательских
|
||||
# запросов из queries.yml отдаются обеими версиями одинаково
|
||||
# (PG_EXPORTER_EXTEND_QUERY_PATH в v0.18 работает), v0.18 добавляет три
|
||||
# встроенные метрики и не теряет ни одной.
|
||||
postgres-exporter-gendesign:
|
||||
image: quay.io/prometheuscommunity/postgres-exporter:v0.16.0
|
||||
image: quay.io/prometheuscommunity/postgres-exporter:v0.18.0
|
||||
container_name: gendesign-pg-exporter-gendesign
|
||||
restart: unless-stopped
|
||||
profiles: ["apps"]
|
||||
|
|
@ -192,7 +211,7 @@ services:
|
|||
|
||||
# ── postgres-exporter: МЕРА (только на Poincare) ─────────────────────────────
|
||||
postgres-exporter-tradein:
|
||||
image: quay.io/prometheuscommunity/postgres-exporter:v0.16.0
|
||||
image: quay.io/prometheuscommunity/postgres-exporter:v0.18.0
|
||||
container_name: gendesign-pg-exporter-tradein
|
||||
restart: unless-stopped
|
||||
profiles: ["apps"]
|
||||
|
|
@ -216,7 +235,7 @@ services:
|
|||
# forgejo + glitchtip. Нужен и сам по себе, и как страховка: рост базы glitchtip
|
||||
# ничем не ограничен — политики ретенции у GlitchTip нет вообще.
|
||||
postgres-exporter-infra:
|
||||
image: quay.io/prometheuscommunity/postgres-exporter:v0.16.0
|
||||
image: quay.io/prometheuscommunity/postgres-exporter:v0.18.0
|
||||
container_name: gendesign-pg-exporter-infra
|
||||
restart: unless-stopped
|
||||
profiles: ["infra"]
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue