homelab-codex-ws/services/kb-site/docker-compose.yml
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

51 lines
2.3 KiB
YAML

services:
kb-site:
image: nginx:alpine
container_name: kb-site
restart: unless-stopped
ports:
# PIHA 82x0 static-HTTP block: 8210 paperless, 8220 nextcloud,
# 8230 kb-query, 8240 narty27 -> 8250 is the next free slot.
# Publicly the site is reached only through the npm@PIHA vhost
# kb.okit.pl; this bind is the proxy's upstream.
- "8250:80"
volumes:
# Generated output of scripts/kb/gen_pages.py — never committed, never
# bind-mounted from the repo. Read-only: nginx only serves it; writes go
# through the helper-container procedure in kb/runbooks/kb-site-deploy.md
# (docker cp cannot write into a :ro mount).
- kb-site_content:/usr/share/nginx/html:ro
# robots.txt nie pochodzi z generatora — nie jest dokumentem KB, więc
# jedzie z repo i nie ginie przy podmianie zawartości wolumenu. Mount
# pliku jest głębszy niż mount katalogu, więc Docker montuje go później
# i plik przykrywa (nieistniejące) robots.txt z wolumenu.
- ./static/robots.txt:/usr/share/nginx/html/robots.txt:ro
# Docker on PIHA has exhausted its default address pools (~30 live stacks:
# "all predefined address pools have been fully subnetted"), so this stack
# must not ask for a subnet of its own. Declaring a network here suppresses
# the implicit kb-site_default; we join the pre-existing shared "proxy"
# bridge instead. This is only about subnet economy — npm@PIHA still reaches
# this container over the published 8250 host port (npm itself lives on
# nginxproxymanager_default), not over this network.
networks:
- proxy
# busybox wget — nginx:alpine ships no curl. index.html is generated on
# every run, so it is the one file that must always be there.
healthcheck:
test: ["CMD", "wget", "-q", "-O", "/dev/null", "http://127.0.0.1/index.html"]
interval: 30s
timeout: 10s
retries: 5
start_period: 5s
networks:
# Created out-of-band on PIHA (docker network create proxy); never managed by
# this stack. `docker network ls | grep -w proxy` must return a row before
# deploy, otherwise compose fails with "network proxy declared as external,
# but could not be found".
proxy:
external: true
volumes:
kb-site_content: