From 5beed7dfce205a176413bc40f0b391808bfa4e68 Mon Sep 17 00:00:00 2001 From: lekss361 Date: Sat, 23 May 2026 11:54:58 +0300 Subject: [PATCH] =?UTF-8?q?fix(security):=20forwarder=20PermissionError=20?= =?UTF-8?q?=E2=80=94=20=D1=83=D0=B1=D1=80=D0=B0=D1=82=D1=8C=20USER=20nobod?= =?UTF-8?q?y=20(read=20/var/log/caddy)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Caddy пишет access log под UID 0; контейнер с USER nobody (65534) не может открыть файл → ни один auth event не пересылался (GlitchTip issue #71). Удалён USER nobody из Dockerfile — форвардер запускается под root. Security: caddy_logs mount read-only, нет network listener, нет shell. auth_forwarder_state — write-only форвардера, не shared с другими сервисами. Добавлен throttle на capture_exception: не более 1 Sentry event в 5 минут при persistent ошибке (деque-паттерн как у basic_auth_storm). --- ops/glitchtip-auth-forwarder/Dockerfile | 5 +++-- ops/glitchtip-auth-forwarder/forwarder.py | 16 ++++++++++++---- 2 files changed, 15 insertions(+), 6 deletions(-) diff --git a/ops/glitchtip-auth-forwarder/Dockerfile b/ops/glitchtip-auth-forwarder/Dockerfile index 4506f329..963dab2c 100644 --- a/ops/glitchtip-auth-forwarder/Dockerfile +++ b/ops/glitchtip-auth-forwarder/Dockerfile @@ -7,8 +7,9 @@ RUN pip install --no-cache-dir "sentry-sdk==2.18.0" COPY forwarder.py . -# nobody (uid 65534) — минимальные привилегии -USER nobody +# Запускается под root — необходимо для чтения /var/log/caddy/gendsgn.ru.log +# (Caddy пишет лог под UID 0). Mounts: caddy_logs:ro + auth_forwarder_state (write). +# Attack surface минимален: нет network listener, нет shell, образ python:3.12-slim. # -u для unbuffered stdout — docker logs виден сразу без буферизации CMD ["python", "-u", "forwarder.py"] diff --git a/ops/glitchtip-auth-forwarder/forwarder.py b/ops/glitchtip-auth-forwarder/forwarder.py index 72cec657..e1037550 100644 --- a/ops/glitchtip-auth-forwarder/forwarder.py +++ b/ops/glitchtip-auth-forwarder/forwarder.py @@ -67,6 +67,10 @@ KNOWN_MONITOR_UAS = frozenset( ) _shutdown = False +# Throttle для unhandled exceptions — не более одного Sentry event за 5 минут, +# чтобы persistent ошибка (напр. PermissionError) не спамила GlitchTip. +_last_exc_sent: float = 0.0 +_EXC_THROTTLE_S: float = 300.0 def _signal_handler(signum: int, frame: object) -> None: @@ -299,10 +303,14 @@ def main() -> None: time.sleep(5) except Exception as exc: print(f"[forwarder] unhandled error: {exc}", file=sys.stderr, flush=True) - try: - sentry_sdk.capture_exception(exc) - except Exception: - pass + global _last_exc_sent + now_wall = time.time() + if now_wall - _last_exc_sent > _EXC_THROTTLE_S: + try: + sentry_sdk.capture_exception(exc) + except Exception: + pass + _last_exc_sent = now_wall time.sleep(5) print("[forwarder] shutting down gracefully", flush=True)