diff --git a/astrololo/README.md b/astrololo/README.md new file mode 100644 index 0000000..0cd7036 --- /dev/null +++ b/astrololo/README.md @@ -0,0 +1,70 @@ +# astrololo — deploy (namespace `astrololo`) + +Manifesty k8s składane Kustomize. Obrazy podbija automatycznie image-updater +(`kustomization.yaml` → `images: newTag`) po każdym buildzie z repo aplikacji. + +Warstwy: `presentation` (NodePort, wejście z przeglądarki) → `logic` → `data` +(pliki Excel montowane **z NFS**, nie z obrazu). + +## ⚠️ Sekret `astrololo-auth` — utwórz PRZED wdrożeniem + +Aplikacja wystawia treść **oryginalnych baz interpretacyjnych**, dlatego wymaga +logowania i tokenu międzywarstwowego (LOG-32). Manifesty odwołują się do sekretu +`astrololo-auth` i **celowo nie zawierają jego wartości** — to repo GitOps, więc +cokolwiek by tu wpadło, zostałoby w historii gita na zawsze. + +To ta sama konwencja co `gitea-registry`: sekret tworzymy poza repo. + +**Pody nie wstaną bez tego sekretu — i tak ma być.** Wolimy widoczną awarię niż +cichy start aplikacji bez ochrony. + +```bash +# hasło wczytane bez zapisu w historii powłoki +read -rs -p "Hasło do aplikacji (APP_PASSWORD): " APP_PASSWORD; echo + +kubectl -n astrololo create secret generic astrololo-auth \ + --from-literal=APP_PASSWORD="$APP_PASSWORD" \ + --from-literal=INTERNAL_TOKEN="$(openssl rand -hex 32)" + +unset APP_PASSWORD +``` + +`INTERNAL_TOKEN` jest losowany i **nikt go nigdy nie musi oglądać** — służy tylko +usługom do rozmowy między sobą. `APP_PASSWORD` wpisujesz w przeglądarce +(użytkownik: `astrololo`, zmienny przez `APP_USER` w `presentation.yaml`). + +### Zmiana hasła +```bash +read -rs -p "Nowe hasło: " NEW; echo +kubectl -n astrololo create secret generic astrololo-auth \ + --from-literal=APP_PASSWORD="$NEW" \ + --from-literal=INTERNAL_TOKEN="$(kubectl -n astrololo get secret astrololo-auth \ + -o jsonpath='{.data.INTERNAL_TOKEN}' | base64 -d)" \ + --dry-run=client -o yaml | kubectl apply -f - +kubectl -n astrololo rollout restart deploy/presentation +unset NEW +``` +(Zachowujemy istniejący `INTERNAL_TOKEN`; jego zmiana wymaga restartu **wszystkich +trzech** usług naraz, inaczej przestaną się dogadywać.) + +### Sprawdzenie po wdrożeniu +```bash +kubectl -n astrololo rollout status deploy/presentation deploy/logic deploy/data +NODE_PORT=$(kubectl -n astrololo get svc presentation -o jsonpath='{.spec.ports[0].nodePort}') +curl -s -o /dev/null -w "bez hasła: %{http_code}\n" http://:$NODE_PORT/ +curl -s -o /dev/null -w "z hasłem: %{http_code}\n" -u astrololo:'' http://:$NODE_PORT/ +``` +Oczekiwane: **401** bez hasła, **200** z hasłem. `/health` zostaje publiczny (sondy k8s). + +## Czego to nie załatwia +- **Brak TLS** — Basic Auth idzie po sieci w postaci łatwej do podsłuchania. Przy + niezaufanej sieci potrzebny ingress z certyfikatem. +- **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). +- **Sekret w etcd** jest tylko zakodowany base64. Docelowo: szyfrowanie etcd at-rest + albo Sealed Secrets / SOPS, jeśli chcecie trzymać sekrety deklaratywnie w repo. + +## Profil ze swissephem +Wariant z silnikiem B: [`../astrololo-swisseph/`](../astrololo-swisseph/README.md). +Dziedziczy powyższe zmienne z tej bazy, więc też wymaga sekretu `astrololo-auth`. diff --git a/astrololo/data.yaml b/astrololo/data.yaml index efe9182..600810d 100644 --- a/astrololo/data.yaml +++ b/astrololo/data.yaml @@ -18,11 +18,17 @@ spec: - name: DATA_PROVIDER value: "excel" - name: EXCEL_DIR - value: "/app/data_files" # pliki są W OBRAZIE (COPY . .) + value: "/app/data_files" # montowane z NFS (patrz volumes) — NIE z obrazu - name: CACHE_DIR value: "/app/.cache" - name: INDEXED_KEYS value: "name,id,symbol" + # Token międzywarstwowy (LOG-32). Ta warstwa oddaje SUROWE wiersze baz — + # najwrażliwszy punkt systemu. Bez tokenu dałoby się ją odpytać + # bezpośrednio, z pominięciem i logiki, i logowania w UI. + - name: INTERNAL_TOKEN + valueFrom: + secretKeyRef: { name: astrololo-auth, key: INTERNAL_TOKEN } volumeMounts: - name: cache mountPath: /app/.cache diff --git a/astrololo/logic.yaml b/astrololo/logic.yaml index 7e5c798..82d62cc 100644 --- a/astrololo/logic.yaml +++ b/astrololo/logic.yaml @@ -20,6 +20,12 @@ spec: - name: EPHEMERIS_ENGINE value: "own" # silnik własny; swisseph pominięty # ENGINE_SWISSEPH_URL celowo NIE ustawiamy — engine-swisseph nie jest wdrażany + # Token międzywarstwowy (LOG-32): logika oddaje treść baz, więc musi + # odrzucać żądania z pominięciem logowania w prezentacji. Ten sam token + # służy jej do uwierzytelnienia się w warstwie danych. + - name: INTERNAL_TOKEN + valueFrom: + secretKeyRef: { name: astrololo-auth, key: INTERNAL_TOKEN } resources: requests: { cpu: "100m", memory: "128Mi" } limits: { cpu: "500m", memory: "512Mi" } diff --git a/astrololo/presentation.yaml b/astrololo/presentation.yaml index 5ace4e5..89841e4 100644 --- a/astrololo/presentation.yaml +++ b/astrololo/presentation.yaml @@ -17,6 +17,19 @@ spec: env: - name: LOGIC_URL value: "http://logic:8001" + # Logowanie do aplikacji (LOG-32). Bez tych sekretów aplikacja stoi + # OTWARTA — a wystawia treść baz interpretacyjnych. Sekret tworzony + # POZA repo (jak gitea-registry) — patrz README.md. + - name: APP_PASSWORD + valueFrom: + secretKeyRef: { name: astrololo-auth, key: APP_PASSWORD } + - name: INTERNAL_TOKEN + valueFrom: + secretKeyRef: { name: astrololo-auth, key: INTERNAL_TOKEN } + - name: APP_USER + value: "astrololo" + - name: RATE_LIMIT_PER_MIN + value: "120" # 0 = bez limitu resources: requests: { cpu: "100m", memory: "128Mi" } limits: { cpu: "300m", memory: "256Mi" }