feat(security): zamkniecie dostepu do baz interpretacyjnych (LOG-32) #10

Merged
gitea merged 1 commits from feat/harden-access into master 2026-07-21 16:50:19 +00:00
Owner

Dlaczego teraz

Bazy sa rdzeniem produktu i wlasnie je kupiliscie. Tymczasem aplikacja nie miala
zadnego uwierzytelniania: prezentacja to NodePort, wiec kazdy w LAN wchodzil bez
logowania, a /search oddawal surowe wiersze, do 50 000 na jedno zapytanie.
Bazy mogly wyjsc przez sama aplikacje, bez udzialu jakiegokolwiek LLM — to bylo
wieksze i pilniejsze ryzyko niz wysylka promptu na zewnatrz.

Co robi

Warstwa Ochrona
prezentacja HTTP Basic (APP_USER/APP_PASSWORD) + limit zadan na IP (RATE_LIMIT_PER_MIN, domyslnie 120/min)
logika, dane token miedzywarstwowy X-Astrololo-Token (INTERNAL_TOKEN)
/search gorny limit 50 000 -> 5000 (tyle realnie uzywa build_report); publiczne /api/query zostaje na 200

Szczegoly, ktore maja znaczenie:

  • limit dziala takze PRZED uwierzytelnieniem — inaczej zgadywanie hasla i sondowanie
    API byloby darmowe;
  • token miedzywarstwowy zamyka obejscie logowania przez uderzenie wprost w logike lub
    dane (warstwa danych oddaje surowe wiersze — najwrazliwszy punkt systemu);
  • /health celowo publiczny — sondy k8s go nie uwierzytelnia;
  • porownania hasla i tokenu przez secrets.compare_digest (odporne na atak czasowy);
  • nie logujemy tresci zadan ani promptow — logi to kolejny nosnik wycieku.

Fail-open — swiadomie, ale glosno

Bez ustawionych sekretow ochrona jest wylaczona (zgodnosc wstecz i wygoda dev), ale
przy starcie leci wyrazne ostrzezenie. Chodzi o to, by nikt nie wdrozyl tego w
przekonaniu, ze jest chroniony.

=> Zeby dzialalo w produkcji, trzeba dolozyc sekrety w repo deploy (APP_PASSWORD,
INTERNAL_TOKEN dla wszystkich trzech uslug). Moge przygotowac ten PR od reki — powiedz slowo.

Weryfikacja

  • 17 nowych testow: 12 w prezentacji (nowy katalog + nowy job w CI) i 5 w logice.
    Logika calosciowo: 104 passed / 1 skipped.
  • Na zywym stosie: bez hasla 401, z haslem 200, logika wprost bez tokenu 401,
    z tokenem 200, /health 200, limit=50000 odrzucony (422) — a prezentacja
    nadal poprawnie liczy horoskop przez logike (silnik skyfield w odpowiedzi).

Czego to NIE zalatwia (uczciwie)

  • HTTP bez TLS — Basic Auth idzie po LAN w postaci latwej do podejrzenia. Jesli w sieci
    moga byc niepowolane osoby, potrzebny jest TLS (ingress z certyfikatem).
  • NFS 192.168.1.34:/mnt/Tank1/astrololo — kto ma dostep do share'u, bierze pliki
    zrodlowe wprost, z pominieciem calej aplikacji. To do zabezpieczenia po stronie infrastruktury.
  • Znaczniki canary w bazach (wykrycie i udowodnienie wycieku) — osobny temat, operacyjny.
## Dlaczego teraz Bazy sa rdzeniem produktu i wlasnie je kupiliscie. Tymczasem aplikacja nie miala **zadnego uwierzytelniania**: prezentacja to `NodePort`, wiec kazdy w LAN wchodzil bez logowania, a `/search` oddawal **surowe wiersze, do 50 000 na jedno zapytanie**. Bazy mogly wyjsc **przez sama aplikacje, bez udzialu jakiegokolwiek LLM** — to bylo wieksze i pilniejsze ryzyko niz wysylka promptu na zewnatrz. ## Co robi | Warstwa | Ochrona | |---|---| | prezentacja | **HTTP Basic** (`APP_USER`/`APP_PASSWORD`) + **limit zadan na IP** (`RATE_LIMIT_PER_MIN`, domyslnie 120/min) | | logika, dane | **token miedzywarstwowy** `X-Astrololo-Token` (`INTERNAL_TOKEN`) | | `/search` | gorny limit **50 000 -> 5000** (tyle realnie uzywa `build_report`); publiczne `/api/query` zostaje na 200 | Szczegoly, ktore maja znaczenie: - **limit dziala takze PRZED uwierzytelnieniem** — inaczej zgadywanie hasla i sondowanie API byloby darmowe; - **token miedzywarstwowy** zamyka obejscie logowania przez uderzenie wprost w logike lub dane (warstwa danych oddaje surowe wiersze — najwrazliwszy punkt systemu); - `/health` celowo **publiczny** — sondy k8s go nie uwierzytelnia; - porownania hasla i tokenu przez `secrets.compare_digest` (odporne na atak czasowy); - **nie logujemy tresci zadan ani promptow** — logi to kolejny nosnik wycieku. ## Fail-open — swiadomie, ale glosno Bez ustawionych sekretow ochrona jest **wylaczona** (zgodnosc wstecz i wygoda dev), ale przy starcie leci **wyrazne ostrzezenie**. Chodzi o to, by nikt nie wdrozyl tego w przekonaniu, ze jest chroniony. **=> Zeby dzialalo w produkcji, trzeba dolozyc sekrety w repo `deploy`** (`APP_PASSWORD`, `INTERNAL_TOKEN` dla wszystkich trzech uslug). Moge przygotowac ten PR od reki — powiedz slowo. ## Weryfikacja - **17 nowych testow**: 12 w prezentacji (nowy katalog + **nowy job w CI**) i 5 w logice. Logika calosciowo: 104 passed / 1 skipped. - **Na zywym stosie**: bez hasla `401`, z haslem `200`, logika wprost bez tokenu `401`, z tokenem `200`, `/health` `200`, `limit=50000` odrzucony (`422`) — a prezentacja **nadal poprawnie liczy horoskop** przez logike (silnik skyfield w odpowiedzi). ## Czego to NIE zalatwia (uczciwie) - **HTTP bez TLS** — Basic Auth idzie po LAN w postaci latwej do podejrzenia. Jesli w sieci moga byc niepowolane osoby, potrzebny jest TLS (ingress z certyfikatem). - **NFS `192.168.1.34:/mnt/Tank1/astrololo`** — kto ma dostep do share'u, bierze pliki zrodlowe wprost, z pominieciem calej aplikacji. To do zabezpieczenia po stronie infrastruktury. - **Znaczniki canary** w bazach (wykrycie i udowodnienie wycieku) — osobny temat, operacyjny.
gitea added 1 commit 2026-07-21 15:52:02 +00:00
feat(security): zamkniecie dostepu do baz interpretacyjnych (LOG-32)
Testy / Testy warstwy logicznej (silnik) (push) Successful in 10m50s
Testy / Testy warstwy prezentacji (dostęp do baz) (push) Successful in 9m53s
Testy / Build obrazu silnika B (swisseph) (push) Failing after 28s
Testy / Kontrola składni wszystkich warstw (push) Successful in 24s
Testy / Testy warstwy logicznej (silnik) (pull_request) Successful in 10m45s
Testy / Testy warstwy prezentacji (dostęp do baz) (pull_request) Successful in 9m56s
Testy / Build obrazu silnika B (swisseph) (pull_request) Failing after 24s
Testy / Kontrola składni wszystkich warstw (pull_request) Successful in 24s
bfa2d1f9d3
Bazy sa rdzeniem produktu i wlasnie zostaly kupione — a aplikacja nie miala
ZADNEGO uwierzytelniania. Prezentacja to NodePort, wiec kazdy w LAN wchodzil
bez logowania, a `/search` oddawal surowe wiersze do 50 000 na zapytanie.
Bazy mogly wyjsc przez sama aplikacje, bez udzialu jakiegokolwiek LLM.

- prezentacja: HTTP Basic (APP_USER/APP_PASSWORD) + limit zadan na IP
  (RATE_LIMIT_PER_MIN, domyslnie 120/min). Limit dziala TAKZE przed
  uwierzytelnieniem, zeby zgadywanie hasla i sondowanie API nie bylo darmowe.
- logika i dane: token miedzywarstwowy X-Astrololo-Token (INTERNAL_TOKEN) —
  bez niego dalo sie ominac logowanie, uderzajac wprost w warstwe nizej.
  Warstwa danych oddaje surowe wiersze, wiec to najwrazliwszy punkt.
- /search: gorny limit 50 000 -> 5000 (tyle realnie uzywa build_report).
  Publiczne /api/query zostaje na 200.
- /health celowo publiczny (sondy k8s go nie uwierzytelnia).
- swiadomie nie logujemy tresci zadan ani promptow — logi to kolejny nosnik.

Fail-open przy braku konfiguracji (zgodnosc wstecz i dev), ale z GLOSNYM
ostrzezeniem przy starcie, zeby nikt nie wdrozyl tego w przekonaniu, ze jest
chroniony. Wlaczenie w produkcji wymaga ustawienia sekretow w repo deploy.

Testy: 12 (prezentacja, nowy katalog + job w CI) i 5 (logika). Zweryfikowane
na zywym stosie: bez hasla 401, z haslem 200, logika wprost bez tokenu 401,
z tokenem 200, /health 200, limit 50000 odrzucony (422), a prezentacja nadal
liczy horoskop przez logike.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
gitea merged commit 752f477a27 into master 2026-07-21 16:50:19 +00:00
gitea deleted branch feat/harden-access 2026-07-21 16:50:21 +00:00
Sign in to join this conversation.
No Reviewers
No Label
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: gitea/astrololo#10