From ed71292f03f7b27f2efd8c595d40a37dd5d78b9c Mon Sep 17 00:00:00 2001 From: bot-backend Date: Sun, 6 Sep 2026 01:46:53 +0500 Subject: [PATCH] =?UTF-8?q?fix(metrics):=20=D0=BC=D0=B0=D1=81=D0=BA=D0=B8?= =?UTF-8?q?=D1=80=D0=BE=D0=B2=D0=B0=D1=82=D1=8C=20=D1=81=D0=B5=D0=BA=D1=80?= =?UTF-8?q?=D0=B5=D1=82=D1=8B=20=D0=B8=D0=B7=20query-=D1=81=D1=82=D1=80?= =?UTF-8?q?=D0=BE=D0=BA=D0=B8=20=D0=B2=20Alloy=20=D0=B4=D0=BE=20=D0=BE?= =?UTF-8?q?=D1=82=D0=BF=D1=80=D0=B0=D0=B2=D0=BA=D0=B8=20=D0=B2=20Loki?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Скруббер #3115 знал одну форму — пароль в DSN (scheme://user:pass@host). Секрет в query-строке (`?secret=<64 hex>` вебхука GlitchTip, #3154) проходил насквозь и оседал в Loki на 30 суток ретенции. Приложение чистит это у себя (#3353), но фильтр стоит на логгере ОДНОГО процесса. Второй слой на сборщике закрывает всё, что придёт мимо: sidecar, чужой процесс, будущий логгер без фильтра. Множество имён параметров взято из log_scrub.py дословно, включая суффиксные client_secret/refresh_token. Группа захвата стоит на значении, а не на имени параметра: Alloy заменяет содержимое ГРУПП, а не весь совпавший фрагмент. Группа вокруг `?secret=` затёрла бы имя и оставила сам секрет — то есть ровно наоборот. Стадия добавлена и в alloy-infra.alloy: на инфра-хосте Forgejo и GlitchTip с их `?token=` в адресах пишут в тот же Loki, дефект там тот же. Closes #3354 --- ops/metrics/alloy/alloy-apps.alloy | 20 ++++++++++++++++++++ ops/metrics/alloy/alloy-infra.alloy | 12 ++++++++++++ 2 files changed, 32 insertions(+) diff --git a/ops/metrics/alloy/alloy-apps.alloy b/ops/metrics/alloy/alloy-apps.alloy index 9d0b931b..709d621f 100644 --- a/ops/metrics/alloy/alloy-apps.alloy +++ b/ops/metrics/alloy/alloy-apps.alloy @@ -172,6 +172,26 @@ loki.process "scrub_credentials" { expression = "://[^:@/ ]+:([^@ ]+)@" replace = "***" } + + // ── Секреты в query-строке (#3354) ───────────────────────────────────────── + // Прод-факт (#3154): uvicorn пишет в access-log ПОЛНЫЙ путь с query, и туда + // уезжал `?secret=<64 hex>` вебхука GlitchTip. Приложение чистит это у себя + // (tradein-mvp/backend/app/core/log_scrub.py), здесь — второй слой на случай + // строки, пришедшей мимо фильтра: другой процесс, sidecar, будущий логгер. + // + // Множество имён держим ОДИНАКОВЫМ с log_scrub.py. Префикс `[\w.-]*` перед + // альтернацией — ради суффиксных имён (`client_secret`, `refresh_token`, + // `webhook_secret`); значение обрывается на `&`, пробеле или кавычке, потому + // что access-строка uvicorn обрамляет запрос кавычками. + // + // Группа захвата стоит на ЗНАЧЕНИИ, а не на имени параметра: Alloy заменяет + // содержимое групп, а не весь совпавший фрагмент (ровно как у DSN выше — там + // группа на пароле, поэтому пользователь и хост уцелевают). Обернуть группой + // `?secret=` значило бы затереть имя и оставить сам секрет. + stage.replace { + expression = "(?i)[?&][\\w.-]*(?:secret|token|api[-_]?key|apikey|access[-_]?token|password|signature|sig)=([^&\\s\"'<>]+)" + replace = "***" + } } loki.relabel "journal" { diff --git a/ops/metrics/alloy/alloy-infra.alloy b/ops/metrics/alloy/alloy-infra.alloy index 1b688957..41d21770 100644 --- a/ops/metrics/alloy/alloy-infra.alloy +++ b/ops/metrics/alloy/alloy-infra.alloy @@ -131,6 +131,18 @@ loki.process "scrub_credentials" { expression = "://[^:@/ ]+:([^@ ]+)@" replace = "***" } + + // ── Секреты в query-строке (#3354) ───────────────────────────────────────── + // То же выражение, что в alloy-apps.alloy, и по той же причине — подробности + // там. Здесь оно нужно не меньше: на инфра-хосте живут Forgejo и GlitchTip, + // у обоих есть ручки с `?token=` в адресе, и их логи идут в тот же Loki. + // + // Группа захвата — на ЗНАЧЕНИИ: Alloy заменяет содержимое групп, а не весь + // совпавший фрагмент (как у DSN выше, где группа на пароле). + stage.replace { + expression = "(?i)[?&][\\w.-]*(?:secret|token|api[-_]?key|apikey|access[-_]?token|password|signature|sig)=([^&\\s\"'<>]+)" + replace = "***" + } } loki.relabel "journal" {