Jedno wspólne hasło nie mówiło, KTO sięgał do baz, a odebranie dostępu jednej osobie wymagało zmiany hasła wszystkim. Przy bazach o realnej wartości handlowej to za mało. KONTA IMIENNE: `APP_USERS='alicja:scrypt$…,bartek:scrypt$…'`. Hash liczy scrypt ze STDLIB — zero nowych zależności; hasła nie ma w konfiguracji jawnie. Zakładanie konta: scripts/make_user.py (hasło interaktywnie, nie w historii powłoki). Odebranie dostępu = usunięcie wpisu, reszta nie zmienia haseł. Gdy APP_USERS jest ustawione, wspólne APP_PASSWORD PRZESTAJE działać (ostrzeżenie przy starcie) — działające obok kont byłoby tylnym wejściem bez śladu w dzienniku. Dopóki APP_USERS nie jest ustawione, stary tryb działa jak dotąd (zgodność wstecz). DZIENNIK AUDYTOWY: każde żądanie zostawia wpis „kto, skąd, co, status, ILE rekordów, ile ms". Liczba rekordów jest tu sednem — pojedyncze zapytanie wygląda niewinnie, suma pokazuje powolne wypompowywanie bazy przez osobę uprawnioną. Liczona dla wyszukiwarki sygnifikatorów, raportu i eksportu do Excela (ten wynosi najwięcej naraz). W logach NIE MA treści — ani rekordów, ani promptów. Dwa realne błędy złapane po drodze: - `secrets.compare_digest` rzuca TypeError na znakach spoza ASCII, więc hasło z polskimi literami wywracało logowanie błędem 500 zamiast odmowy (błąd ZASTANY, sprzed tej zmiany) — porównujemy teraz bajty; - dziennik był PUSTY na żywym serwerze: domyślna konfiguracja uvicorna nie obsługuje naszych loggerów. Niewidoczny dziennik jest gorszy niż jego brak, więc audyt dostał własny handler na stdout. Oba przypadki mają testy regresji. Instrukcja wdrożeniowa: docs/konta-i-audyt.md. Testy: +15. Prezentacja 243. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2.5 KiB
Konta imienne i dziennik audytowy (PRE-17)
Zamiast jednego wspólnego hasła: konta imienne, bo przy bazach o realnej wartości handlowej trzeba wiedzieć kto sięgał do treści — i móc odciąć jedną osobę bez zmiany hasła całej reszcie.
Zakładanie konta
Hasło podajesz interaktywnie (nie trafia do historii powłoki ani do listy procesów); na wyjściu dostajesz hash, nie hasło:
cd services/presentation && python scripts/make_user.py alicja
Wynik wklejasz do APP_USERS (wpisy po przecinku):
APP_USERS='alicja:scrypt$…,bartek:scrypt$…'
Hash liczy scrypt ze stdlib — bez nowych zależności. Sekret ustawiasz jak
resztę (kubectl create secret …, secretKeyRef), nigdy w repo GitOps.
Odebranie dostępu jednej osobie
Usuń jej wpis z APP_USERS i zrestartuj presentation. Pozostali nie zmieniają
haseł — to była główna bolączka wspólnego hasła.
Uwaga: wspólne hasło przestaje działać
Gdy APP_USERS jest ustawione, stare APP_PASSWORD nie działa (aplikacja
zgłasza to ostrzeżeniem przy starcie). Celowo: działające obok kont wspólne hasło
byłoby tylnym wejściem bez śladu w dzienniku, czyli dokładnie problemem, który to
wymaganie zamyka. Po migracji usuń APP_PASSWORD z konfiguracji.
Zgodność wstecz: dopóki APP_USERS nie jest ustawione, APP_USER/APP_PASSWORD
działa jak dotąd — aktualizacja nie wywraca istniejącego wdrożenia.
Dziennik audytowy
Każde żądanie do chronionej ścieżki zostawia wpis na stdout (w k8s zbierany standardowo):
2026-08-03 21:45:44 INFO AUDYT user=alicja ip=10.1.2.3 method=POST path=/interpret status=200 records=428 ms=1530
| Pole | Znaczenie |
|---|---|
user |
kto (- przy nieudanym logowaniu — nie podpowiadamy, które konto istnieje) |
path, method, status |
co robił i z jakim skutkiem |
records |
ile rekordów baz oddaliśmy (- gdy żądanie nie dotyka baz) |
ms |
czas obsługi |
records jest tu najważniejsze: pojedyncze zapytanie wygląda niewinnie, ale suma
pokazuje powolne wypompowywanie bazy przez osobę uprawnioną — czego żadne
uwierzytelnienie nie wykryje. Liczone m.in. dla wyszukiwarki sygnifikatorów,
raportu interpretacji i eksportu do Excela (ten wynosi najwięcej naraz).
W dzienniku nie ma treści — ani rekordów, ani promptów, ani danych urodzeniowych. Logi byłyby kolejnym nośnikiem wycieku; do wykrycia nadużycia wystarczą metadane i liczby.
Poziom sterujesz przez AUDIT_LEVEL (domyślnie INFO).