16240466ec
Domyka drabinę produktów: astrodemo (dwie funkcje) → astroklient → astrololo. Host astroklient.czernobog.pl, obraz astrololo-astroklient, port 8006. WŁASNY STOS, NIE WSPÓLNY. Nowa para data-astroklient + logic-astroklient i nowy udział /mnt/Tank1/astrololo-klient. Pule kont izolują klientów od siebie w każdym wariancie, ale to izolacja PROGRAMOWA — opiera się na poprawności mechanizmu pul. Granica na poziomie systemu plików nie zależy od tego, czy w kodzie niczego nie przeoczono, a przy sprzątaniu demo nie da się przez pomyłkę skasować cudzych danych, bo leżą gdzie indziej. Klaster ma zapas (węzły na 24–38% pamięci), więc koszt dwóch podów nie był argumentem przeciw. Silnik własny (permisywny) — silnik B (AGPL) nie wchodzi do produktu oddawanego klientom. data-astroklient dostaje nodeAffinity na etykietę zdolności procesora, jak pozostałe warstwy danych: ciągnie pandas, a przez nią NumPy z bazą x86-64-v2. WPIS W LIŚCIE OBSERWOWANYCH OBRAZÓW od razu, z komentarzem dlaczego. Bez niego usługa nie deployuje się sama: obraz powstaje w rejestrze, kustomization zostaje na starym tagu, i wygląda to na zepsute CI. Tak zawisł kiedyś render. Certyfikat obejmuje trzeci host; DNS już wskazuje (wildcard). RUNBOOK: udział NFS ze WSZYSTKIMI CZTEREMA węzłami, konta jako hashe scrypt, własny SESSION_SECRET i własna nazwa ciasteczka (trzy produkty nie mają powodu uznawać nawzajem swoich sesji), unieważnianie dostępu bez wolumenu stanu, oraz sprawdzenie po wdrożeniu. W sprawdzeniu poprawiona rzecz, którą najpierw napisałem błędnie: BEZ SESJI każdy adres oddaje 303, także nieistniejący, bo bramka logowania działa przed trasowaniem. Pętla curl bez ciasteczka pokazywałaby 303 dla wszystkiego i sugerowała, że nieobecne ekrany „są". Sprawdzenie ma sens dopiero z sesją — i wtedy dają 404, nie 403. Zweryfikowane: kustomize build (37 obiektów), dry-run serwerowy przyjmuje wszystkie osiem nowych obiektów, kody odpowiedzi sprawdzone na złożonym drzewie. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
137 lines
5.5 KiB
Markdown
137 lines
5.5 KiB
Markdown
# Astroklient — wdrożenie warstwy pośredniej
|
|
|
|
Pełne funkcje astrologiczne, bez generowania tekstu i bez administracji.
|
|
Trzeci produkt drabiny: **astrodemo** (dwie funkcje) → **astroklient** → **astrololo**.
|
|
|
|
Host: `astroklient.czernobog.pl` · obraz `astrololo-astroklient` · port 8006.
|
|
|
|
## Jak to jest odizolowane
|
|
|
|
Trzy granice, w kolejności od najmocniejszej:
|
|
|
|
**Obraz.** Plików ekranów, których ten produkt nie ma, NIE MA w obrazie — usuwa
|
|
je `usun.txt` przy budowaniu. Nie ma więc czego wyłączać ani czym odsłaniać:
|
|
uprawnienia liczą się z katalogu funkcji, a katalog składa się ze zgłoszeń
|
|
ekranów obecnych w obrazie. Test w repo aplikacji sprawdza to na złożonym
|
|
drzewie, razem z zaporą słownikową — po nieobecnych funkcjach nie zostaje nawet
|
|
słowo w komentarzu.
|
|
|
|
**Udział NFS.** Własny `astrololo-klient`, osobny od produkcyjnego i od demo.
|
|
Pule kont izolują klientów od siebie, ale to izolacja PROGRAMOWA — opiera się na
|
|
poprawności mechanizmu pul. Granica na poziomie systemu plików nie zależy od
|
|
tego, czy w kodzie niczego nie przeoczono. Przy sprzątaniu demo nie da się przez
|
|
pomyłkę skasować cudzych danych, bo leżą gdzie indziej.
|
|
|
|
**Warstwa danych i logiki.** Własna para `data-astroklient` + `logic-astroklient`,
|
|
z silnikiem własnym (permisywnym) — silnik B (AGPL) nie wchodzi do produktu
|
|
oddawanego klientom.
|
|
|
|
---
|
|
|
|
## Krok 1 — udział `astrololo-klient` na TrueNAS
|
|
|
|
```bash
|
|
midclt call sharing.nfs.create '{
|
|
"path": "/mnt/Tank1/astrololo-klient",
|
|
"hosts": ["192.168.1.73", "192.168.1.80", "192.168.1.81", "192.168.1.82"],
|
|
"maproot_user": "root", "maproot_group": "root", "enabled": true
|
|
}'
|
|
midclt call service.restart nfs
|
|
```
|
|
|
|
**Wszystkie CZTERY węzły.** Klaster ma cztery (`kubectl get nodes -o wide`), a nie
|
|
trzy — pominięcie jednego daje `access denied by server` dopiero wtedy, gdy
|
|
scheduler tam coś postawi, czyli w losowym momencie tygodnie później.
|
|
|
|
Kontrolę wyłapującą braki i literówki w adresach masz w
|
|
[README.md](README.md) → „Kontrola: czy każdy udział obejmuje każdy węzeł".
|
|
|
|
Jeśli katalog jeszcze nie istnieje, utwórz dataset przed eksportem.
|
|
|
|
## Krok 2 — konta klientów
|
|
|
|
Każde konto to osobna pula, więc **rozdajesz konta, nie jedno hasło**.
|
|
Wszystkie mają ten sam poziom dostępu — konta rozdziela się dla rozdzielenia
|
|
plików, nie uprawnień.
|
|
|
|
```bash
|
|
# skrypt w repo aplikacji; format hasha wspólny dla wszystkich usług
|
|
python services/presentation/scripts/make_user.py klientA # wypisze: scrypt$…
|
|
```
|
|
|
|
```bash
|
|
kubectl -n astrololo create secret generic astrololo-astroklient \
|
|
--from-literal=ASTROKLIENT_USERS='klientA:scrypt$…,klientB:scrypt$…' \
|
|
--from-literal=SESSION_SECRET="$(openssl rand -hex 32)"
|
|
```
|
|
|
|
`SESSION_SECRET` jest **własny**, nie ten z pełnej aplikacji ani z demo: trzy
|
|
produkty nie mają powodu uznawać nawzajem swoich sesji. Ciasteczko też ma własną
|
|
nazwę (`astroklient_sesja`), więc logowanie do jednego nie wyrzuca z drugiego.
|
|
|
|
**Pod bez `SESSION_SECRET` celowo nie wstanie** — logowanie bez klucza podpisu
|
|
nie miałoby czym się bronić.
|
|
|
|
### Unieważnienie dostępu
|
|
|
|
Nie ma wolumenu stanu, więc nie ma licznika pokolenia sesji. Zamiast tego:
|
|
|
|
- usunięcie konta z `ASTROKLIENT_USERS` albo zmiana jego hasła **natychmiast ubija
|
|
jego otwarte sesje** — odcisk poświadczenia w ciasteczku przestaje pasować,
|
|
- rotacja `SESSION_SECRET` wylogowuje wszystkich naraz.
|
|
|
|
**Pula zostaje na udziale.** Odebranie dostępu nie kasuje plików klienta —
|
|
przestaje być tylko komu je pokazywać.
|
|
|
|
### Dodanie konta później
|
|
|
|
```bash
|
|
STARE=$(kubectl -n astrololo get secret astrololo-astroklient -o jsonpath='{.data.ASTROKLIENT_USERS}' | base64 -d)
|
|
|
|
kubectl -n astrololo create secret generic astrololo-astroklient \
|
|
--from-literal=ASTROKLIENT_USERS="${STARE},klientC:scrypt\$…" \
|
|
--dry-run=client -o yaml | kubectl apply -f -
|
|
|
|
kubectl -n astrololo rollout restart deploy/astroklient
|
|
```
|
|
|
|
## Krok 3 — wdrożenie
|
|
|
|
Sekret i udział **muszą istnieć wcześniej**. Potem wystarczy merge do `master` —
|
|
ArgoCD zsynchronizuje, a image-updater będzie odtąd podbijał tag sam, bo obraz
|
|
jest dopisany do [`image-updater.yaml`](image-updater.yaml).
|
|
|
|
## Sprawdzenie po wdrożeniu
|
|
|
|
```bash
|
|
kubectl -n astrololo get pods -l app=astroklient -o wide
|
|
kubectl -n astrololo logs -l app=astroklient --tail=30
|
|
curl -sk https://astroklient.czernobog.pl/ -o /dev/null -w '%{http_code}\n' # 303 → bramka logowania odpowiada
|
|
```
|
|
|
|
Po zalogowaniu w nawigacji ma być **sześć** pozycji: Horoskop, Interpretacje,
|
|
Kalendarz, Synastria, Sygnifikatory, Pliki.
|
|
|
|
**Bez zalogowania każdy adres oddaje 303** — także nieistniejący. Bramka
|
|
logowania działa przed trasowaniem, więc niezalogowany nie wyczyta z kodów
|
|
odpowiedzi, które ekrany istnieją. Sprawdzanie nieobecnych adresów bez sesji nic
|
|
więc nie mówi: pokaże 303 dla wszystkiego, łącznie z `/nie-ma-takiego-ekranu`.
|
|
|
|
Sprawdzenie ma sens dopiero Z SESJĄ, i wtedy nieobecne ekrany dają **404, nie
|
|
403** — odmowa z powodem byłaby informacją, że coś tam jest:
|
|
|
|
```bash
|
|
CIASTKO='astroklient_sesja=<wartość z przeglądarki po zalogowaniu>'
|
|
for A in / /compile /settings /accounts /files; do
|
|
printf "%-12s %s\n" "$A" \
|
|
"$(curl -sk -o /dev/null -w '%{http_code}' -H "Cookie: $CIASTKO" https://astroklient.czernobog.pl$A)"
|
|
done
|
|
# oczekiwane: / → 200, reszta → 404
|
|
```
|
|
|
|
## Import puli klienta do pełnej aplikacji
|
|
|
|
Pula to zwykły katalog na udziale — kopiuje się go tak samo jak w demo,
|
|
patrz [README-astrodemo.md](README-astrodemo.md) → „Import puli klienta".
|
|
Ścieżka źródłowa to `/mnt/Tank1/astrololo-klient/<login>`.
|