Do tej pory aplikacja chodzila po zwyklym http, a logowanie szlo przez HTTP Basic — czyli haslo leciało siecia w postaci trywialnej do podsluchania. Poza bezpieczenstwem TLS naprawia DWIE funkcje zepsute dzis z tego samego powodu: geolokalizacja przegladarki („Tu i teraz") i kopiowanie promptu do schowka dzialaja wylacznie w secure context, wiec po http po prostu odmawialy. Wlasne CA, nie Let's Encrypt — klaster stoi w LAN (traefik trzyma LoadBalancera na 192.168.1.x), wiec walidacja HTTP-01 nie ma jak dojsc z internetu, a DNS-01 wymagalby trzymania w klastrze tokena API do domeny. Co dochodzi: - tls.yaml — Issuer selfSigned -> korzen CA (10 lat, importowany raz do przegladarki) -> Issuer CA -> certyfikat serwera (90 dni, odnawiany sam). Krotki termin certyfikatu serwera jest CELOWY: odnawianie sprawdza sie wtedy co kwartal, a nie dopiero za dekade. - ingress.yaml — wejscie po https + naglowki (HSTS rok, nosniff, frameDeny) oraz osobny Ingress na porcie 80 robiacy przekierowanie. Rozdzielone swiadomie: gdyby redirect wisial na tym samym routerze co wejscie po https, odsylalby sam do siebie w kolko. - presentation: Service NodePort -> ClusterIP. NodePort byl druga, nieszyfrowana droga do aplikacji, czyli obejsciem calego PRE-16. Zmiana wymagajaca uwagi: TRUST_PROXY=true. Za Ingressem bezposrednim rozmowca jest zawsze pod Traefika, wiec bez tego limit zadan liczylby WSZYSTKICH do jednego wiadra i pierwsza osoba, ktora go wyklika, odcielaby pozostalych. Sprawdzone: Ingress i Middleware przechodza `kubectl apply --dry-run=server` na zywym klastrze (CRD traefik.io/v1alpha1 sa, entrypointy web/websecure sa). Obiekty cert-managera zwalidowane offline wobec oficjalnego schematu z CRD v1.21.0 z additionalProperties=false — literowka w nazwie pola zostalaby zlapana. `kubectl kustomize` przechodzi dla obu profili (bazowego i swisseph). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
7.9 KiB
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 (wejście z przeglądarki przez Ingress po https) →
logic → data (pliki Excel montowane z NFS, nie z obrazu).
Adres aplikacji: https://astrololo.czernobog.pl — patrz TLS.
⚠️ 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.
# 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
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
kubectl -n astrololo rollout status deploy/presentation deploy/logic deploy/data
curl -s -o /dev/null -w "bez hasła: %{http_code}\n" https://astrololo.czernobog.pl/
curl -s -o /dev/null -w "z hasłem: %{http_code}\n" -u astrololo:'<hasło>' https://astrololo.czernobog.pl/
Oczekiwane: 401 bez hasła, 200 z hasłem. /health zostaje publiczny (sondy k8s).
🔒 TLS — wymagane kroki przed wdrożeniem
Do tej pory Basic Auth szedł siecią po zwykłym http, czyli hasło dało się podsłuchać (base64 to nie szyfrowanie). Poza tym http blokował dwie funkcje, bo przeglądarki udostępniają je wyłącznie w tzw. secure context: geolokalizacja („Tu i teraz") i kopiowanie do schowka. Certyfikat załatwia oba tematy naraz.
Certyfikat wystawia własne CA trzymane w klastrze (cert-manager). Let's Encrypt odpada, bo klaster stoi w LAN — walidacja HTTP-01 nie ma jak do niego dojść z internetu, a DNS-01 wymagałby trzymania tu tokena API do domeny.
Kolejność ma znaczenie — oba kroki PRZED synchronizacją ArgoCD.
1. cert-manager (jednorazowo, cały klaster)
kubectl apply -f https://github.com/cert-manager/cert-manager/releases/download/v1.21.0/cert-manager.yaml
kubectl -n cert-manager rollout status deploy/cert-manager deploy/cert-manager-webhook
Bez tego API odrzuci Certificate i Issuer jako nieznane rodzaje zasobów,
a ArgoCD pokaże aplikację jako niezsynchronizowaną.
2. DNS: astrololo.czernobog.pl → adres Traefika
kubectl -n kube-system get svc traefik -o jsonpath='{.status.loadBalancer.ingress[*].ip}'; echo
Wpis w routerze / lokalnym DNS-ie (albo doraźnie /etc/hosts). Service
presentation jest teraz ClusterIP — NodePort był drugą, nieszyfrowaną drogą
do aplikacji, więc został zdjęty. Zanim DNS zacznie odpowiadać, jedyne wejście to:
kubectl -n astrololo port-forward svc/presentation 8000:8000 # awaryjnie, http://localhost:8000
3. Zaufanie do własnego CA (raz na urządzenie)
Bez tego przeglądarka pokaże ostrzeżenie o certyfikacie. Korzeń ważny 10 lat, więc import robisz raz:
kubectl -n astrololo get secret astrololo-ca -o jsonpath='{.data.ca\.crt}' \
| base64 -d > astrololo-ca.crt
# macOS — do systemowego zaufania (poprosi o hasło administratora)
sudo security add-trusted-cert -d -r trustRoot \
-k /Library/Keychains/System.keychain astrololo-ca.crt
# Linux (Debian/Ubuntu)
sudo cp astrololo-ca.crt /usr/local/share/ca-certificates/ && sudo update-ca-certificates
Firefox ma własny magazyn — tam import przez Ustawienia → Prywatność → Wyświetl certyfikaty → Organy certyfikacji.
Sprawdzenie samego TLS
# certyfikat wystawiony i gotowy
kubectl -n astrololo get certificate
# oczekiwane: astrololo-ca True, astrololo-tls True
# http odsyła na https
curl -sI http://astrololo.czernobog.pl/ | head -2 # 301 + Location: https://...
# HSTS obecny
curl -skI -u astrololo:'<hasło>' https://astrololo.czernobog.pl/ | grep -i strict-transport
Odnawianie
Certyfikat serwera żyje 90 dni i odnawia się sam 30 dni przed końcem. Termin jest 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.
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
OpenAI lub Anthropic, potrzebne są ich klucze. Ten sekret jest opcjonalny
(optional: true) — bez niego pody startują normalnie, tylko chmura jest niedostępna.
read -rs -p "OPENAI_API_KEY (Enter = pomiń): " OPENAI_KEY; echo
read -rs -p "ANTHROPIC_API_KEY (Enter = pomiń): " ANTHROPIC_KEY; echo
kubectl -n astrololo create secret generic astrololo-llm \
--from-literal=OPENAI_API_KEY="$OPENAI_KEY" \
--from-literal=ANTHROPIC_API_KEY="$ANTHROPIC_KEY"
unset OPENAI_KEY ANTHROPIC_KEY
kubectl -n astrololo rollout restart deploy/logic # klucze wstrzykują się przy starcie
Podmiana pojedynczego klucza (bez kasowania drugiego):
kubectl -n astrololo create secret generic astrololo-llm \
--from-literal=OPENAI_API_KEY="$(kubectl -n astrololo get secret astrololo-llm \
-o jsonpath='{.data.OPENAI_API_KEY}' | base64 -d)" \
--from-literal=ANTHROPIC_API_KEY='NOWY-KLUCZ' \
--dry-run=client -o yaml | kubectl apply -f -
kubectl -n astrololo rollout restart deploy/logic
Adres modelu lokalnego ustawia LOCAL_BASE_URL w logic.yaml — dopasuj do miejsca,
gdzie faktycznie stoi Ollama/vLLM. Konfiguracja jest per dostawca
(LOCAL_*, OPENAI_*, ANTHROPIC_*), więc ustawienia lokalnego modelu nie przejmują
żądań do chmury.
Wybór dostawcy w chmurze oznacza, że oryginalne opisy z baz opuszczają naszą sieć. Prompt można obejrzeć przed wysłaniem. Warto zawnioskować u dostawcy o Zero Data Retention — patrz LOG-32.
Czego to nie załatwia
- 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.
- 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/.
Dziedziczy powyższe zmienne z tej bazy, więc też wymaga sekretu astrololo-auth.