Commit graph

4 commits

Author SHA1 Message Date
oskar bb3792d219 docs(kb-site): przekaz ACCESS_TOKEN generatorowi w procedurze publikacji
Poprzedni commit nauczyl gen_pages.py dopisywac token do linkow, ale nic
go nie podawalo — publikacja poszlaby stara sciezka i dalaby build
z golymi linkami, czyli stan sprzed fiksa.

kb-site nie ma skryptu deployu: generator wolany jest wylacznie recznie
z runbooka (kroki 2 i 7), wiec to tam token musi wejsc.

Zrodlo tokenu: /opt/homelab/config/kb-site/.env na wezle GENERUJACYM
(SATURN/SOLARIA), nie na PIHA. To swiadome odstepstwo od konwencji
config/<serwis>/ z CLAUDE.md — plik trzyma zwykle sekrety wezla, ktory
serwis uruchamia, a ten token jest potrzebny tam, gdzie serwis sie
generuje. Kontener nginx dalej nie ma zadnej konfiguracji ani sekretow;
odnotowane w service.yaml i env.example, zeby nikt nie szukal .env na PIHA.

Token idzie zmienna srodowiskowa (set -a; . plik; set +a), nie flaga
--access-token: argument z linii polecen laduje w historii shella i jest
widoczny w ps dla kazdego uzytkownika wezla.

Lancuch publikacji z kroku 7 dostal dwa nowe ogniwa przed scp: test -n
"$ACCESS_TOKEN" (pusty token = build nieklikalny) oraz grep -q 'key='
w index.html (token byl, ale nie dojechal do generatora). Oba zatrzymuja
publikacje tak samo jak --check.

Krok 6 weryfikuje teraz wlasciwa rzecz: wyciaga href ze spisu i pobiera
GO, zamiast recznie sklejac URL — czyli testuje to, co faktycznie bylo
zepsute. Doszedl tez negatywny test bramki (bez tokenu ma NIE byc 200).

Tabela problemow: "index sie otwiera, ale klikniecie daje 403" (build bez
tokenu) i "403 takze z tokenem" (rotacja tokenu w NPM rozjechana z plikiem
— stary build zostaje z martwym tokenem w kazdym linku).

kb/services/kb-site.md (public) — sekcja Access: token siedzi teraz
w tresci kazdej serwowanej strony, wiec jedna zapisana strona wydaje go
w calosci. Model zagrozen bez zmian (URL wejsciowy zawsze go niosl), ale
warto, zeby dokument mowil to wprost obok zdania "to obscurity, not
access control".

Test: sekwencje z krokow 2 i 7 przepuszczone na symulowanym pliku tokenu
(prod /opt/homelab nietkniety) — token obecny: Token: TAK, 4x key=
w index.html, lancuch dochodzi do tar; token pusty: staje na pierwszym
ogniwie, brak tgz; build bez tokenu przy ustawionej zmiennej: staje na
grep, brak tgz. check_okf.py exit 0, gen_pages --check exit 0,
service.yaml parsuje sie.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-05 22:26:48 +02:00
oskar 19548d888e fix(kb-site): wycofaj robots.txt — blokowal legalny fetch z tokenem
"User-agent: * / Disallow: /" odbijalo nie tylko wyszukiwarki, ale kazdego
klienta respektujacego robots.txt (m.in. fetch asystentow AI) — takze takiego,
ktory znal token dostepu. Token mial WPUSZCZAC znajacych go, a robots.txt ich
WYPYCHAL.

Dzialalo tez przeciwko wlasnemu celowi: crawler zablokowany przed pobraniem
strony nigdy nie widzi meta noindex, a wyszukiwarka i tak potrafi wylistowac
sam URL, ktorego nie wolno jej bylo pobrac.

Ochrona przed indeksowaniem zostaje bez zmian: <meta name="robots"
content="noindex, nofollow"> w <head> kazdej strony (scripts/kb/gen_pages.py).

Plik mial jedna linie polityki, wiec bez niej tracil sens — usuniety razem
z bind-mountem w compose (katalog static/ zniknal jako pusty).

Test: docker compose config (exit 0, zostaje tylko named volume);
gen_pages.py --check — CZYSTO, 0 trafien.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-05 13:11:43 +02:00
oskar db81cb15d3 feat(kb-site): noindex + robots.txt + obscure subdomain jako domyslny base-url
Warstwa "nie daj sie przypadkiem znalezc" dla publicznej wystawki KB:

- gen_pages.py: <meta name="robots" content="noindex, nofollow"> w <head>
  kazdej generowanej strony (page_shell, wiec takze index).
- gen_pages.py: DEFAULT_BASE_URL -> https://kb-e2a24af3.okit.pl. Slug musi
  zgadzac sie z rekordem DNS i vhostem w npm@PIHA (runbook kb-site-deploy).
- services/kb-site: static/robots.txt (Disallow: /) montowany ro na
  /usr/share/nginx/html/robots.txt. Plik nie jest dokumentem KB, wiec jedzie
  z repo, a nie z wolumenu podmienianego przy kazdej publikacji.
- kb/services/kb-site.md: sekcja "Access" — token w query paramie na warstwie
  nginx/NPM (sekret zyje tylko w NPM, nie w repo) + obscure subdomain +
  noindex. Explicit: to obscurity, nie kontrola dostepu — token w URL laduje
  w access logach, historii przegladarki i naglowku Referer.

Bramka publikacji bez zmian: gen_pages.py --check exit 0 (22 wyciszone
whitelista, jak dotad).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-05 13:08:13 +02:00
oskar 41d3543082 docs(kb): kb-site — dokument serwisu (public) + runbook deployu (private)
kb/services/kb-site.md (type: service, visibility: public) — opis samej
wystawki: co jest publikowane (regula fail-closed na `visibility`), jak
renderowane sa odnosniki do dokumentow nieopublikowanych, struktura builda,
stopka z commitem, bramka --check. Napisany tak, zeby sam przechodzil --check:
zero adresow, portow i sciezek hosta — szczegoly operacyjne siedza w runbooku.

kb/runbooks/kb-site-deploy.md (type: runbook, visibility: private) — pelna
procedura: deploy na PIHA, generacja + kontrola wyciekow jako bramka
publikacji, podmiana tresci w wolumenie helperem alpine (wzorzec z
services/narty27/README.md, rozszerzony z pliku na drzewo), rekord A w
Cloudflare + Pi-hole local DNS (split-horizon), proxy host i cert przez
scripts/npm/npm_api.py, weryfikacja, rutynowa aktualizacja jednym lancuchem
&&, tabela rollback/typowe problemy.

Dwie rzeczy zapisane wprost, bo latwo je przeoczyc:
- kolejnosc DNS -> NPM host -> cert (HTTP-01 wymaga dzialajacego vhosta),
- `rm -rf /content/*` przed rozpakowaniem — bez tego dokument przelaczony z
  public na private zostaje w wolumenie i dalej jest serwowany.

Runbook notuje tez aktualny wynik --check (22 trafienia /opt/homelab w
dokumentach public) jako decyzje do podjecia przed pierwsza publikacja:
wyczyscic zrodla albo swiadomie wpisac wyjatek do whitelisty.
2026-08-04 18:01:56 +02:00