diff --git a/astrololo/README.md b/astrololo/README.md index a3b726a..1621913 100644 --- a/astrololo/README.md +++ b/astrololo/README.md @@ -125,6 +125,39 @@ Certyfikat serwera żyje 90 dni i odnawia się sam 30 dni przed końcem. Termin krótki **celowo** — odnawianie sprawdza się wtedy w praktyce co kwartał, a nie dopiero za dziesięć lat, gdy nikt nie będzie pamiętał, jak to było skonfigurowane. +## ⚠️ Sekret `astrololo-link` — szyfrowanie między warstwami + +Token międzywarstwowy mówi **kto** pyta, ale nie ukrywa **czego dotyczy odpowiedź** +— a płyną nią surowe wiersze oryginalnych baz. Kto podsłuchał ruch wewnątrz sieci +(drugi pod, mirror portu na switchu, zrzut z węzła), miał je w całości. Łącza są +więc szyfrowane **AES-256-GCM**, osobnym kluczem na każdą parę rozmówców. + +```bash +kubectl -n astrololo create secret generic astrololo-link \ + --from-literal=LINK_KEY_PRESENTATION_LOGIC="$(openssl rand -hex 32)" \ + --from-literal=LINK_KEY_LOGIC_DATA="$(openssl rand -hex 32)" +``` + +Kluczy **nikt nigdy nie musi oglądać** — służą tylko usługom. Rozdzielenie jest +celowe: przejęcie klucza prezentacji nie daje dostępu do warstwy danych, gdzie +leżą całe bazy. Logika trzyma oba, bo rozmawia w obie strony; prezentacja i dane +dostają przez `secretKeyRef` wyłącznie swój. + +**Pody nie wstaną bez tych kluczy** (`LINK_ENCRYPTION_REQUIRED=true`) — i tak ma +być. Usługa, która wstała i po cichu nie szyfruje, jest gorsza niż pod w CrashLoop, +bo awarii nie widać. + +### Wymiana kluczy +```bash +kubectl -n astrololo create secret generic astrololo-link \ + --from-literal=LINK_KEY_PRESENTATION_LOGIC="$(openssl rand -hex 32)" \ + --from-literal=LINK_KEY_LOGIC_DATA="$(openssl rand -hex 32)" \ + --dry-run=client -o yaml | kubectl apply -f - +kubectl -n astrololo rollout restart deploy/presentation deploy/logic deploy/data +``` +Restart **wszystkich trzech naraz** — w trakcie wymiany warstwy chwilowo się nie +dogadują (klucze muszą być zgodne po obu stronach łącza). + ## Klucze do modeli w chmurze — sekret `astrololo-llm` (opcjonalny) Domyślnie działa **model lokalny** i nic nie opuszcza sieci. Żeby móc wybrać w UI @@ -166,6 +199,9 @@ gdzie faktycznie stoi Ollama/vLLM. Konfiguracja jest **per dostawca** - **Własne CA to nie to samo co publiczne zaufanie** — każde nowe urządzenie wymaga importu korzenia (krok 3 powyżej). Gdyby aplikacja miała kiedyś wyjść na świat, właściwą drogą jest Let's Encrypt przez DNS-01. +- **Szyfrowane są ciała żądań, nie nagłówki** — ścieżka (`/search`) i token + międzywarstwowy jadą czytelnie. Sam token nikomu nic nie daje: bez klucza łącza + każde żądanie kończy się odmową. Pełne ukrycie metadanych wymagałoby mTLS. - **NFS `192.168.1.34:/mnt/Tank1/astrololo`** — kto ma dostęp do share'u, bierze pliki baz **z pominięciem całej aplikacji**. Do zamknięcia po stronie infrastruktury (eksport tylko dla IP węzłów, `root_squash`, najlepiej read-only). diff --git a/astrololo/data.yaml b/astrololo/data.yaml index 600810d..a33a80a 100644 --- a/astrololo/data.yaml +++ b/astrololo/data.yaml @@ -29,6 +29,17 @@ spec: - name: INTERNAL_TOKEN valueFrom: secretKeyRef: { name: astrololo-auth, key: INTERNAL_TOKEN } + # Szyfrowanie łącza między warstwami (PRE-16). OSOBNY klucz na każdą + # parę rozmówców: przejęcie klucza prezentacji nie otwiera warstwy + # danych, gdzie leżą całe bazy. Sekret tworzony POZA repo — patrz README. + - name: LINK_KEY_LOGIC_DATA + valueFrom: + secretKeyRef: { name: astrololo-link, key: LINK_KEY_LOGIC_DATA } + # Bez klucza usługa ma NIE wstać, a klient ma nie wysłać niczego. + # Sam serwer broni się fail-closed, ale to za mało: klient bez klucza + # zdążyłby wypuścić zapytanie jawnym tekstem, zanim dostanie odmowę. + - name: LINK_ENCRYPTION_REQUIRED + value: "true" volumeMounts: - name: cache mountPath: /app/.cache diff --git a/astrololo/logic.yaml b/astrololo/logic.yaml index d0c8b6e..d575aa4 100644 --- a/astrololo/logic.yaml +++ b/astrololo/logic.yaml @@ -26,6 +26,20 @@ spec: - name: INTERNAL_TOKEN valueFrom: secretKeyRef: { name: astrololo-auth, key: INTERNAL_TOKEN } + # Szyfrowanie łącza między warstwami (PRE-16). OSOBNY klucz na każdą + # parę rozmówców: przejęcie klucza prezentacji nie otwiera warstwy + # danych, gdzie leżą całe bazy. Sekret tworzony POZA repo — patrz README. + - name: LINK_KEY_PRESENTATION_LOGIC + valueFrom: + secretKeyRef: { name: astrololo-link, key: LINK_KEY_PRESENTATION_LOGIC } + - name: LINK_KEY_LOGIC_DATA + valueFrom: + secretKeyRef: { name: astrololo-link, key: LINK_KEY_LOGIC_DATA } + # Bez klucza usługa ma NIE wstać, a klient ma nie wysłać niczego. + # Sam serwer broni się fail-closed, ale to za mało: klient bez klucza + # zdążyłby wypuścić zapytanie jawnym tekstem, zanim dostanie odmowę. + - name: LINK_ENCRYPTION_REQUIRED + value: "true" # --- Model językowy (LOG-31) --- # Domyślnie LOKALNY: prompt niesie oryginalne opisy z baz, więc nic nie diff --git a/astrololo/presentation.yaml b/astrololo/presentation.yaml index 43dbb9e..9a2cd4c 100644 --- a/astrololo/presentation.yaml +++ b/astrololo/presentation.yaml @@ -37,6 +37,17 @@ spec: # nagłówek staje się furtką do ominięcia limitu. - name: TRUST_PROXY value: "true" + # Szyfrowanie łącza między warstwami (PRE-16). OSOBNY klucz na każdą + # parę rozmówców: przejęcie klucza prezentacji nie otwiera warstwy + # danych, gdzie leżą całe bazy. Sekret tworzony POZA repo — patrz README. + - name: LINK_KEY_PRESENTATION_LOGIC + valueFrom: + secretKeyRef: { name: astrololo-link, key: LINK_KEY_PRESENTATION_LOGIC } + # Bez klucza usługa ma NIE wstać, a klient ma nie wysłać niczego. + # Sam serwer broni się fail-closed, ale to za mało: klient bez klucza + # zdążyłby wypuścić zapytanie jawnym tekstem, zanim dostanie odmowę. + - name: LINK_ENCRYPTION_REQUIRED + value: "true" resources: requests: { cpu: "100m", memory: "128Mi" } limits: { cpu: "300m", memory: "256Mi" }