diff --git a/astrololo/README-astroklient.md b/astrololo/README-astroklient.md index 801c50c..9ce7e20 100644 --- a/astrololo/README-astroklient.md +++ b/astrololo/README-astroklient.md @@ -1,86 +1,177 @@ -# astroklient — wdrożenie wersji demo (PRE-28) +# astroklient — wdrożenie wersji demo (PRE-28/29) Osobna usługa o dwóch funkcjach: **dodanie pliku bazy** i **zapytanie o -interpretację urodzeniową**. Opis samej aplikacji: `services/astroklient/README.md` +interpretację urodzeniową**. Opis aplikacji: `services/astroklient/README.md` w repo aplikacji. --- -## ⚠️ Przeczytaj, zanim komuś dasz adres +## Jak to jest odizolowane -Demo pracuje na **produkcyjnej warstwie danych**. To była świadoma decyzja, ale -niesie dwie konsekwencje, o których trzeba pamiętać za każdym razem: +``` +astroklient → logic-demo → data-demo → /mnt/Tank1/astrololo-demo + └── klientA/ ← pula konta + └── klientB/ ← pula konta +``` -* **kto ma dostęp do demo, czyta Twoje oryginalne bazy interpretacyjne** — czyli - rdzeń produktu, którego pilnują LOG-32, DAN-25 i PRE-27, -* **pliki wgrane przez demo trafiają do produkcyjnego zbioru** i od razu biorą - udział w wyszukiwaniu, także w pełnej aplikacji. +**Oryginalne bazy są dla demo nieosiągalne.** Nie chodzi o uprawnienia: demo ma +własną warstwę danych, pracującą na osobnym udziale, pustym na starcie. -Jeśli demo ma trafić do kogoś spoza kręgu zaufania, właściwą odpowiedzią jest -osobna warstwa danych z pustym udziałem — **nie jest to dziś zrobione**. +Dlaczego osobna jest też **warstwa logiczna**: zna ona jeden adres warstwy danych, +więc astroklient korzystający z produkcyjnej logiki i tak trafiłby na produkcyjne +bazy. Izolacja musi sięgnąć obu warstw naraz, inaczej nie ma jej wcale. + +**Każde konto ma własną pulę** — swój podkatalog na tym udziale. Konta nie widzą +swoich plików nawzajem ani w liście, ani w wynikach wyszukiwania. Pula bierze się +z **loginu zalogowanej osoby**, nigdy z pola formularza. --- -## Krok 1 — sekret z hasłem demo +## Krok 1 — udział `astrololo-demo` na TrueNAS -Osobny sekret, nie `astrololo-auth`. Dzięki temu demo odcina się **jedną komendą**, -bez ruszania kont głównej aplikacji i bez zmiany hasła komukolwiek. +Ta sama procedura co przy `astrololo-state`, więc jeśli tamten działa, ten też +zadziała. Po SSH na NAS: ```bash -read -rs -p "Hasło do demo (DEMO_PASSWORD): " DEMO; echo +sudo zfs create Tank1/astrololo-demo # albo: sudo mkdir -p /mnt/Tank1/astrololo-demo +``` + +```bash +midclt call sharing.nfs.query '[["path","=","/mnt/Tank1/astrololo-demo"]]' \ + | python3 -m json.tool +``` + +**Pusta lista `[]`** — twórz: + +```bash +midclt call sharing.nfs.create '{ + "path": "/mnt/Tank1/astrololo-demo", + "comment": "astrololo — pule kont wersji demo (PRE-29)", + "hosts": ["192.168.1.73", "192.168.1.80", "192.168.1.81"], + "enabled": true, + "ro": false, + "mapall_user": "root", + "mapall_group": "root" +}' +``` + +**Coś zwróciło** — weź `id` i użyj `sharing.nfs.update ` z tą samą treścią. + +```bash +midclt call service.restart nfs +sudo exportfs -v | grep astrololo-demo +``` + +Musi tam być `rw`. Sprawdź jeszcze zapis z węzła — to jest ten test, którego +zabrakło przy poprzednim udziale: + +```bash +ssh 'sudo mkdir -p /mnt/t && sudo mount -t nfs 192.168.1.34:/mnt/Tank1/astrololo-demo /mnt/t \ + && sudo touch /mnt/t/proba && echo ZAPIS-OK || echo ZAPIS-NIE; sudo rm -f /mnt/t/proba; sudo umount /mnt/t' +``` + +--- + +## Krok 2 — konta demo + +Każde konto to osobna pula, więc **rozdajesz konta, nie jedno hasło**. + +Hasła najlepiej jako hash — wtedy nie leżą nigdzie jawnie (skrypt jest w repo +aplikacji, `services/presentation/scripts/make_user.py`): + +```bash +python scripts/make_user.py klientA # wypisze: scrypt$… +python scripts/make_user.py klientB +``` + +```bash +kubectl -n astrololo create secret generic astrololo-demo \ + --from-literal=DEMO_USERS='klientA:scrypt$…,klientB:scrypt$…' +``` + +### Dodanie konta później + +```bash +STARE=$(kubectl -n astrololo get secret astrololo-demo -o jsonpath='{.data.DEMO_USERS}' | base64 -d) kubectl -n astrololo create secret generic astrololo-demo \ - --from-literal=DEMO_PASSWORD="$DEMO" + --from-literal=DEMO_USERS="${STARE},klientC:scrypt\$…" \ + --dry-run=client -o yaml | kubectl apply -f - -unset DEMO +kubectl -n astrololo rollout restart deploy/astroklient ``` -Login to `demo` (zmienny przez `DEMO_USER` w `astroklient.yaml`). +### Odebranie dostępu -Hasło może być też hashem `scrypt$…` — wtedy nie leży nigdzie jawnie: - -```bash -cd services/presentation && python scripts/make_user.py demo # w repo aplikacji -``` +Usuń wpis z `DEMO_USERS` tą samą drogą. **Pula zostaje na udziale** — pliki +klienta nie znikają, tylko przestaje być komu je pokazywać. --- -## Krok 2 — wdrożenie +## Krok 3 — wdrożenie ```bash kubectl apply -k astrololo -kubectl -n astrololo rollout status deploy/astroklient +kubectl -n astrololo rollout status deploy/data-demo deploy/logic-demo deploy/astroklient ``` -Image-updater ma astroklienta na liście, więc kolejne obrazy podbiją się same. - ---- - -## Krok 3 — wejście z zewnątrz - -Usługa jest `ClusterIP`; z zewnątrz wchodzi się **wyłącznie przez Ingress po -https**, tak samo jak do prezentacji. Dopisz regułę do `ingress.yaml` — osobny -host albo ścieżka, zależnie od tego, jak chcesz demo udostępniać. - -Na czas sprawdzenia wystarczy tunel: +Sprawdzenie na czas jednej sesji, bez wystawiania na świat: ```bash kubectl -n astrololo port-forward deploy/astroklient 8005:8005 ``` -i `http://localhost:8005` — login `demo`, hasło z kroku 1. +Wejście z zewnątrz wymaga reguły w `ingress.yaml` — osobny host albo ścieżka. +**Celowo nie zakładam tego za Ciebie**: to decyzja, pod jakim adresem świat +zobaczy demo. --- -## Odcięcie demo +## Import puli klienta do pełnej aplikacji + +Sedno całego układu: klient, który przechodzi na pełną wersję, **nie traci +dotychczasowych wgrań**. Jego pula to jeden katalog. + +**1. Zobacz, co tam jest** (na NAS): ```bash -kubectl -n astrololo delete secret astrololo-demo -kubectl -n astrololo rollout restart deploy/astroklient +LOGIN=klientA +sudo ls -la /mnt/Tank1/astrololo-demo/$LOGIN/ ``` -> Uwaga: **pod bez sekretu nie wstanie** i to jest zachowanie zamierzone. -> Alternatywnie `kubectl -n astrololo scale deploy/astroklient --replicas=0`, -> jeśli chcesz tylko wyłączyć, zachowując konfigurację. +**2. Sprawdź kolizje nazw** z bazami produkcyjnymi: -Konta głównej aplikacji pozostają nietknięte w obu przypadkach. +```bash +comm -12 \ + <(cd /mnt/Tank1/astrololo-demo/$LOGIN && ls *.xlsx 2>/dev/null | sort) \ + <(cd /mnt/Tank1/astrololo && ls *.xlsx 2>/dev/null | sort) +``` + +Pusto = brak kolizji. Cokolwiek się wypisze, przenieś ręcznie pod inną nazwą — +**nie nadpisuj bazy produkcyjnej**. + +**3. Skopiuj** (`-n` = nie nadpisuj niczego, co już jest): + +```bash +sudo cp -n /mnt/Tank1/astrololo-demo/$LOGIN/*.xlsx /mnt/Tank1/astrololo/ +``` + +> Kopiujemy **tylko pliki `.xlsx`**. Plik `.files-state.json` zostaje — opisuje +> stan wewnątrz puli demo i w produkcji nie ma sensu; produkcja ma własny. + +**4. Włącz je w pełnej aplikacji**: zakładka **Pliki**, nowe bazy pojawią się jako +„gotowa, odstawiona". Klikasz przełącznik przy tych, które mają wejść do użytku. + +To, że nie włączają się same, jest zamierzone: przeniesienie plików jest czynnością +techniczną, a decyzja, które bazy biorą udział w interpretacji, należy do Ciebie. + +--- + +## Odcięcie całego demo + +```bash +kubectl -n astrololo scale deploy/astroklient --replicas=0 +``` + +Pule i dane zostają. Skasowanie sekretu `astrololo-demo` zatrzyma pod (nie wstanie +bez niego) i to też jest zachowanie zamierzone. diff --git a/astrololo/README.md b/astrololo/README.md index dda4149..778b31c 100644 --- a/astrololo/README.md +++ b/astrololo/README.md @@ -33,9 +33,10 @@ NFS. Co zostaje z zabezpieczeń i jak to wdrożyć — w runbooku. Osobna usługa o dwóch funkcjach. Runbook: **[README-astroklient.md](README-astroklient.md)**. -⚠️ Pracuje na **produkcyjnej warstwie danych** — kto ma dostęp do demo, czyta -oryginalne bazy. Konto jest osobne (`astrololo-demo`), więc odcina się bez ruszania -kont głównej aplikacji. +Ma **własną warstwę logiczną i własną warstwę danych** na osobnym udziale +`astrololo-demo` — oryginalne bazy są dla demo nieosiągalne. Każde konto demo ma +własną, niewidoczną dla pozostałych pulę plików, a jego pulę da się później +przenieść do pełnej aplikacji jako jeden katalog. ## ⚠️ Sekret `astrololo-auth` — utwórz PRZED wdrożeniem diff --git a/astrololo/astroklient-stack.yaml b/astrololo/astroklient-stack.yaml new file mode 100644 index 0000000..66d350a --- /dev/null +++ b/astrololo/astroklient-stack.yaml @@ -0,0 +1,122 @@ +# Warstwa danych i logiki WYŁĄCZNIE dla wersji demo (PRE-29). +# +# DLACZEGO OSOBNY KOMPLET, A NIE WSPÓŁDZIELONY. Warstwa logiczna zna JEDEN adres +# warstwy danych, więc astroklient korzystający z produkcyjnej logiki i tak trafiłby +# na produkcyjne bazy. Izolacja demo musi więc sięgnąć obu warstw naraz, inaczej +# nie ma jej wcale. +# +# Udział `astrololo-demo` jest PUSTY na starcie i nigdy nie zawiera oryginalnych +# baz. Każde konto demo dostaje w nim własny podkatalog (pulę) — patrz PRE-29. +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: data-demo + namespace: astrololo +spec: + replicas: 1 + selector: + matchLabels: { app: data-demo } + template: + metadata: + labels: { app: data-demo } + spec: + containers: + - name: data + # TEN SAM obraz co produkcja — różni się wyłącznie udziałem, na którym + # pracuje. Osobny obraz oznaczałby drugi kod do utrzymania i pewność, + # że kiedyś się rozjadą. + image: gitea.czernobog.pl/gitea/astrololo-data:latest + ports: [{ containerPort: 8002 }] + env: + - name: DATA_PROVIDER + value: "excel" + - name: EXCEL_DIR + value: "/app/data_files" + - name: CACHE_DIR + value: "/app/.cache" + - name: INTERNAL_TOKEN + valueFrom: + secretKeyRef: { name: astrololo-auth, key: INTERNAL_TOKEN } + - name: LINK_KEY_LOGIC_DATA + valueFrom: + secretKeyRef: { name: astrololo-link, key: LINK_KEY_LOGIC_DATA } + - name: LINK_ENCRYPTION_REQUIRED + value: "true" + volumeMounts: + - name: cache + mountPath: /app/.cache + - name: demo + mountPath: /app/data_files + resources: + requests: { cpu: "100m", memory: "256Mi" } + limits: { cpu: "500m", memory: "512Mi" } + volumes: + - name: cache + emptyDir: {} + - name: demo + nfs: + server: 192.168.1.34 + # OSOBNY udział, pusty na starcie. NIE /mnt/Tank1/astrololo — + # to jest cała istota izolacji demo. + path: /mnt/Tank1/astrololo-demo +--- +apiVersion: v1 +kind: Service +metadata: + name: data-demo + namespace: astrololo +spec: + type: ClusterIP + selector: { app: data-demo } + ports: [{ port: 8002, targetPort: 8002 }] +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: logic-demo + namespace: astrololo +spec: + replicas: 1 + selector: + matchLabels: { app: logic-demo } + template: + metadata: + labels: { app: logic-demo } + spec: + containers: + - name: logic + image: gitea.czernobog.pl/gitea/astrololo-logic:latest + ports: [{ containerPort: 8001 }] + env: + # JEDYNA różnica wobec produkcyjnej logiki — i zarazem cała izolacja. + - name: DATA_URL + value: "http://data-demo:8002" + - name: EPHEMERIS_ENGINE + value: "own" + - name: INTERNAL_TOKEN + valueFrom: + secretKeyRef: { name: astrololo-auth, key: INTERNAL_TOKEN } + - 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 } + - name: LINK_ENCRYPTION_REQUIRED + value: "true" + # Bez kluczy do modeli językowych. Astroklient nie umie o nie prosić, + # więc ich tu nie ma — czego nie ma w podzie, tego nie wyniesie. + resources: + requests: { cpu: "100m", memory: "128Mi" } + limits: { cpu: "500m", memory: "512Mi" } +--- +apiVersion: v1 +kind: Service +metadata: + name: logic-demo + namespace: astrololo +spec: + type: ClusterIP + selector: { app: logic-demo } + ports: [{ port: 8001, targetPort: 8001 }] diff --git a/astrololo/astroklient.yaml b/astrololo/astroklient.yaml index e6a7003..6af55b1 100644 --- a/astrololo/astroklient.yaml +++ b/astrololo/astroklient.yaml @@ -3,10 +3,14 @@ # Osobna usługa o DWÓCH funkcjach: dodanie pliku bazy i zapytanie o interpretację # urodzeniową. Pozostałe funkcje nie są tu ukryte — nie ma ich w obrazie. # -# ⚠️ PRACUJE NA PRODUKCYJNEJ WARSTWIE DANYCH (świadoma decyzja właściciela). -# Kto ma dostęp do demo, czyta oryginalne bazy interpretacyjne, a pliki przez nie -# wgrane trafiają do produkcyjnego zbioru. Dlatego konto jest OSOBNE: demo odcina -# się skasowaniem sekretu astrololo-demo, bez ruszania kont głównej aplikacji. +# IZOLACJA (PRE-29): astroklient ma WŁASNĄ warstwę logiczną i WŁASNĄ warstwę +# danych (astroklient-stack.yaml), pracujące na osobnym, pustym na starcie +# udziale. Oryginalne bazy interpretacyjne są dla demo NIEOSIĄGALNE. +# +# Wewnątrz demo każde konto ma własną pulę plików — swój podkatalog na tym +# udziale — niewidoczną dla pozostałych kont. Dzięki temu demo można rozdawać +# szeroko, a pliki jednego klienta da się później przenieść do pełnej aplikacji +# jako jeden katalog (patrz README-astroklient.md). apiVersion: apps/v1 kind: Deployment metadata: @@ -25,15 +29,20 @@ spec: image: gitea.czernobog.pl/gitea/astrololo-astroklient:latest ports: [{ containerPort: 8005 }] env: + # WŁASNA warstwa logiczna, wskazująca na WŁASNĄ warstwę danych. + # Wpisanie tu produkcyjnego `logic` cofnęłoby całą izolację demo + # jednym słowem — dlatego stoi to tuż obok siebie w jednym pliku. - name: LOGIC_URL - value: "http://logic:8001" + value: "http://logic-demo:8001" # Konto demo — WŁASNE, nie astrololo-auth. Sekret tworzony POZA repo # (jak pozostałe), patrz README-astroklient.md. - - name: DEMO_USER - value: "demo" - - name: DEMO_PASSWORD + # KONTA DEMO — lista `login:sekret` po przecinku. Każde konto ma + # WŁASNĄ, niewidoczną dla pozostałych pulę baz (PRE-29), więc jedno + # wspólne konto oznaczałoby klientów oglądających swoje wgrania + # nawzajem. Sekrety najlepiej jako hash scrypt. + - name: DEMO_USERS valueFrom: - secretKeyRef: { name: astrololo-demo, key: DEMO_PASSWORD } + secretKeyRef: { name: astrololo-demo, key: DEMO_USERS } # Token międzywarstwowy i klucz łącza — TE SAME co prezentacja: # demo nie jest furtką omijającą ochronę warstwy logicznej. - name: INTERNAL_TOKEN diff --git a/astrololo/kustomization.yaml b/astrololo/kustomization.yaml index 63ed861..314c821 100644 --- a/astrololo/kustomization.yaml +++ b/astrololo/kustomization.yaml @@ -6,6 +6,7 @@ resources: - data.yaml - logic.yaml - presentation.yaml + - astroklient-stack.yaml # OSOBNA warstwa danych i logiki dla demo (PRE-29) - astroklient.yaml # wersja demo (PRE-28) — sekret astrololo-demo POZA repo - render.yaml # składanie raportu PDF (PRE-24), osobny obraz z TeX Live - tls.yaml # certyfikat z własnego CA (wymaga cert-managera)