Compare commits
2 commits
7282a5e1d5
...
67e49a0952
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
67e49a0952 | ||
|
|
db81cb15d3 |
|
|
@ -168,7 +168,7 @@ services:
|
||||||
kb-site:
|
kb-site:
|
||||||
role: static-html-host # public KB slice generated by scripts/kb/gen_pages.py
|
role: static-html-host # public KB slice generated by scripts/kb/gen_pages.py
|
||||||
deployment_model: docker-compose
|
deployment_model: docker-compose
|
||||||
exposure: public # via npm@PIHA vhost kb.okit.pl; container binds LAN :8250
|
exposure: public # via npm@PIHA vhost kb-e2a24af3.okit.pl; container binds LAN :8250
|
||||||
offline_required: false
|
offline_required: false
|
||||||
depends_on:
|
depends_on:
|
||||||
local: []
|
local: []
|
||||||
|
|
|
||||||
|
|
@ -3,17 +3,17 @@ okf: "0.1"
|
||||||
type: runbook
|
type: runbook
|
||||||
visibility: private
|
visibility: private
|
||||||
status: active
|
status: active
|
||||||
updated: 2026-08-04
|
updated: 2026-08-05
|
||||||
links:
|
links:
|
||||||
- ../services/kb-site.md
|
- ../services/kb-site.md
|
||||||
- ./npm-api.md
|
- ./npm-api.md
|
||||||
- ../phases/okit-cloudflare.md
|
- ../phases/okit-cloudflare.md
|
||||||
---
|
---
|
||||||
|
|
||||||
# kb-site — deploy, publikacja treści, ingress (kb.okit.pl)
|
# kb-site — deploy, publikacja treści, ingress (kb-e2a24af3.okit.pl)
|
||||||
|
|
||||||
Serwis: `services/kb-site/` (nginx:alpine na PIHA, host port **8250**).
|
Serwis: `services/kb-site/` (nginx:alpine na PIHA, host port **8250**).
|
||||||
Generator: `scripts/kb/gen_pages.py`. Publiczny adres: `https://kb.okit.pl`.
|
Generator: `scripts/kb/gen_pages.py`. Publiczny adres: `https://kb-e2a24af3.okit.pl`.
|
||||||
|
|
||||||
Kolejność ma znaczenie: **DNS → NPM host → cert**. Certyfikat Let's Encrypt
|
Kolejność ma znaczenie: **DNS → NPM host → cert**. Certyfikat Let's Encrypt
|
||||||
leci challenge'em HTTP-01, więc rekord A musi już wskazywać na łącze domowe,
|
leci challenge'em HTTP-01, więc rekord A musi już wskazywać na łącze domowe,
|
||||||
|
|
@ -57,7 +57,7 @@ docker volume ls | grep kb-site # kb-site_kb-site_content
|
||||||
Generujemy w checkoucie repo, na węźle z którego pracujesz — nie na PIHA.
|
Generujemy w checkoucie repo, na węźle z którego pracujesz — nie na PIHA.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
python3 scripts/kb/gen_pages.py --base-url https://kb.okit.pl
|
python3 scripts/kb/gen_pages.py --base-url https://kb-e2a24af3.okit.pl
|
||||||
python3 scripts/kb/gen_pages.py --check
|
python3 scripts/kb/gen_pages.py --check
|
||||||
echo $? # 0 = czysto, 1 = trafienia
|
echo $? # 0 = czysto, 1 = trafienia
|
||||||
```
|
```
|
||||||
|
|
@ -130,7 +130,7 @@ curl -sI http://192.168.31.5:8250/index.html | head -1
|
||||||
|
|
||||||
```
|
```
|
||||||
Type: A
|
Type: A
|
||||||
Name: kb
|
Name: kb-e2a24af3
|
||||||
Content: <publiczny IP łącza domowego> # ten sam, co ma vikunja.okit.pl
|
Content: <publiczny IP łącza domowego> # ten sam, co ma vikunja.okit.pl
|
||||||
Proxy: DNS only (szara chmurka)
|
Proxy: DNS only (szara chmurka)
|
||||||
TTL: Auto
|
TTL: Auto
|
||||||
|
|
@ -144,13 +144,13 @@ a nie na krawędź Cloudflare. Włączenie proxy to osobna decyzja, po tym jak c
|
||||||
już działa.
|
już działa.
|
||||||
|
|
||||||
Split-horizon (patrz `kb/phases/okit-cloudflare.md`): w Pi-hole na PIHA dodaj
|
Split-horizon (patrz `kb/phases/okit-cloudflare.md`): w Pi-hole na PIHA dodaj
|
||||||
Local DNS Record `kb.okit.pl → 192.168.31.5`, żeby klienci w LAN szli prosto do
|
Local DNS Record `kb-e2a24af3.okit.pl → 192.168.31.5`, żeby klienci w LAN szli prosto do
|
||||||
npm, a nie przez hairpin NAT na routerze.
|
npm, a nie przez hairpin NAT na routerze.
|
||||||
|
|
||||||
Sprawdzenie propagacji (z hosta poza LAN albo przez publiczny resolver):
|
Sprawdzenie propagacji (z hosta poza LAN albo przez publiczny resolver):
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
dig +short kb.okit.pl @1.1.1.1
|
dig +short kb-e2a24af3.okit.pl @1.1.1.1
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
@ -161,15 +161,15 @@ Wszystkie komendy `npm_api.py` są **dry-run domyślnie**; realna zmiana dopiero
|
||||||
z `--apply`. Puść najpierw bez `--apply` i przeczytaj payload.
|
z `--apply`. Puść najpierw bez `--apply` i przeczytaj payload.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# 5a. host: kb.okit.pl -> PIHA:8250
|
# 5a. host: kb-e2a24af3.okit.pl -> PIHA:8250
|
||||||
python3 scripts/npm/npm_api.py --npm piha create-host \
|
python3 scripts/npm/npm_api.py --npm piha create-host \
|
||||||
--domain kb.okit.pl \
|
--domain kb-e2a24af3.okit.pl \
|
||||||
--forward-host 192.168.31.5 --forward-port 8250 \
|
--forward-host 192.168.31.5 --forward-port 8250 \
|
||||||
--block-exploits --http2-support
|
--block-exploits --http2-support
|
||||||
# ...i to samo z --apply
|
# ...i to samo z --apply
|
||||||
|
|
||||||
# 5b. cert Let's Encrypt (HTTP-01 — wymaga działającego kroku 4 i 5a)
|
# 5b. cert Let's Encrypt (HTTP-01 — wymaga działającego kroku 4 i 5a)
|
||||||
python3 scripts/npm/npm_api.py --npm piha create-cert --domain kb.okit.pl --apply
|
python3 scripts/npm/npm_api.py --npm piha create-cert --domain kb-e2a24af3.okit.pl --apply
|
||||||
|
|
||||||
# 5c. podepnij cert pod host i wymuś HTTPS
|
# 5c. podepnij cert pod host i wymuś HTTPS
|
||||||
python3 scripts/npm/npm_api.py --npm piha list-hosts # weź HOST_ID
|
python3 scripts/npm/npm_api.py --npm piha list-hosts # weź HOST_ID
|
||||||
|
|
@ -190,12 +190,12 @@ wiersz certyfikatu (szczegóły w `kb/runbooks/npm-api.md`).
|
||||||
## 6. Weryfikacja końcowa
|
## 6. Weryfikacja końcowa
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
curl -sI https://kb.okit.pl/ | head -1 # 200
|
curl -sI https://kb-e2a24af3.okit.pl/ | head -1 # 200
|
||||||
curl -s https://kb.okit.pl/ | grep -c 'class="cards"' # spis jest
|
curl -s https://kb-e2a24af3.okit.pl/ | grep -c 'class="cards"' # spis jest
|
||||||
curl -s https://kb.okit.pl/subsystems/observer.html | tail -5 # stopka: data + commit
|
curl -s https://kb-e2a24af3.okit.pl/subsystems/observer.html | tail -5 # stopka: data + commit
|
||||||
```
|
```
|
||||||
|
|
||||||
W przeglądarce: `https://kb.okit.pl` — spis pogrupowany per type, kłódka bez
|
W przeglądarce: `https://kb-e2a24af3.okit.pl` — spis pogrupowany per type, kłódka bez
|
||||||
ostrzeżeń, wejście w dowolną kartę, powrót linkiem „All documents".
|
ostrzeżeń, wejście w dowolną kartę, powrót linkiem „All documents".
|
||||||
|
|
||||||
Kontrola treści (ręczna, jednorazowa po pierwszej publikacji): przejrzyj spis
|
Kontrola treści (ręczna, jednorazowa po pierwszej publikacji): przejrzyj spis
|
||||||
|
|
@ -209,7 +209,7 @@ pilnuje pola `visibility`, ale to człowiek decyduje, co dostaje `public`.
|
||||||
Po każdej zmianie w `kb/**/*.md`, która dotyczy dokumentów `public`:
|
Po każdej zmianie w `kb/**/*.md`, która dotyczy dokumentów `public`:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
python3 scripts/kb/gen_pages.py --base-url https://kb.okit.pl \
|
python3 scripts/kb/gen_pages.py --base-url https://kb-e2a24af3.okit.pl \
|
||||||
&& python3 scripts/kb/gen_pages.py --check \
|
&& python3 scripts/kb/gen_pages.py --check \
|
||||||
&& tar -czf /tmp/kb-site.tgz -C build/kb-site . \
|
&& tar -czf /tmp/kb-site.tgz -C build/kb-site . \
|
||||||
&& scp /tmp/kb-site.tgz piha:/tmp/kb-site.tgz \
|
&& scp /tmp/kb-site.tgz piha:/tmp/kb-site.tgz \
|
||||||
|
|
|
||||||
|
|
@ -3,14 +3,15 @@ okf: "0.1"
|
||||||
type: service
|
type: service
|
||||||
visibility: public
|
visibility: public
|
||||||
status: active
|
status: active
|
||||||
updated: 2026-08-04
|
updated: 2026-08-05
|
||||||
links:
|
links:
|
||||||
- ../runbooks/kb-site-deploy.md
|
- ../runbooks/kb-site-deploy.md
|
||||||
---
|
---
|
||||||
|
|
||||||
# kb-site
|
# kb-site
|
||||||
|
|
||||||
Public slice of this knowledge base, served as static HTML at `kb.okit.pl`.
|
Public slice of this knowledge base, served as static HTML from a deliberately
|
||||||
|
non-obvious subdomain of `okit.pl`.
|
||||||
Plain `nginx:alpine` on the PIHA node reading one Docker named volume — no
|
Plain `nginx:alpine` on the PIHA node reading one Docker named volume — no
|
||||||
build step at runtime, no database, no dependencies.
|
build step at runtime, no database, no dependencies.
|
||||||
|
|
||||||
|
|
@ -58,6 +59,18 @@ out empty, so every exception is a recorded decision.
|
||||||
The check is a release gate: content is copied to the host only after it
|
The check is a release gate: content is copied to the host only after it
|
||||||
passes.
|
passes.
|
||||||
|
|
||||||
|
## Access
|
||||||
|
|
||||||
|
Three layers keep the site out of casual sight: a query-parameter token enforced
|
||||||
|
in the nginx/NPM layer (advanced config held in NPM only — the secret is not in
|
||||||
|
this repository), an unguessable subdomain, and `noindex, nofollow` on every page
|
||||||
|
alongside a blanket `robots.txt`.
|
||||||
|
|
||||||
|
This is obscurity, not access control. A URL token is written to access logs,
|
||||||
|
browser history and outbound `Referer` headers, so anyone who obtains a link
|
||||||
|
keeps it; nothing here resists a deliberate attacker. The leak gate above, not
|
||||||
|
this, is what keeps private material off the site.
|
||||||
|
|
||||||
## Operations
|
## Operations
|
||||||
|
|
||||||
Deployment, content refresh, reverse-proxy and DNS setup are described in the
|
Deployment, content refresh, reverse-proxy and DNS setup are described in the
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
"""Generator publicznej warstwy bazy wiedzy (kb.okit.pl) z dokumentów OKF v0.1.
|
"""Generator publicznej warstwy bazy wiedzy z dokumentów OKF v0.1.
|
||||||
|
|
||||||
Wzorzec: ~/narty-2027/saalbach-kb/gen_pages.py — renderer markdown na samej
|
Wzorzec: ~/narty-2027/saalbach-kb/gen_pages.py — renderer markdown na samej
|
||||||
bibliotece standardowej plus prosty, czytelny szablon HTML. Tutaj dochodzi
|
bibliotece standardowej plus prosty, czytelny szablon HTML. Tutaj dochodzi
|
||||||
|
|
@ -60,7 +60,7 @@ from check_okf import parse_yaml, split_frontmatter # noqa: E402
|
||||||
REPO_ROOT = Path(__file__).resolve().parents[2]
|
REPO_ROOT = Path(__file__).resolve().parents[2]
|
||||||
KB_DIR = REPO_ROOT / "kb"
|
KB_DIR = REPO_ROOT / "kb"
|
||||||
DEFAULT_OUT = REPO_ROOT / "build" / "kb-site"
|
DEFAULT_OUT = REPO_ROOT / "build" / "kb-site"
|
||||||
DEFAULT_BASE_URL = "https://kb.okit.pl"
|
DEFAULT_BASE_URL = "https://kb-e2a24af3.okit.pl"
|
||||||
DEFAULT_WHITELIST = Path(__file__).resolve().parent / "check_whitelist.txt"
|
DEFAULT_WHITELIST = Path(__file__).resolve().parent / "check_whitelist.txt"
|
||||||
|
|
||||||
SITE_NAME = "homelab-codex — knowledge base"
|
SITE_NAME = "homelab-codex — knowledge base"
|
||||||
|
|
@ -464,6 +464,7 @@ def page_shell(
|
||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<meta name="robots" content="noindex, nofollow">
|
||||||
<title>{html.escape(title)} — {html.escape(SITE_NAME)}</title>
|
<title>{html.escape(title)} — {html.escape(SITE_NAME)}</title>
|
||||||
<link rel="canonical" href="{html.escape(canonical, quote=True)}">
|
<link rel="canonical" href="{html.escape(canonical, quote=True)}">
|
||||||
<style>{CSS}</style>
|
<style>{CSS}</style>
|
||||||
|
|
@ -816,7 +817,7 @@ def check(out_dir: Path, whitelist_path: Path) -> int:
|
||||||
|
|
||||||
def main() -> int:
|
def main() -> int:
|
||||||
parser = argparse.ArgumentParser(
|
parser = argparse.ArgumentParser(
|
||||||
description="Generator publicznej warstwy KB (kb.okit.pl) z dokumentów OKF."
|
description="Generator publicznej warstwy KB z dokumentów OKF."
|
||||||
)
|
)
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
"--base-url",
|
"--base-url",
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
# kb-site
|
# kb-site
|
||||||
|
|
||||||
Public slice of the knowledge base (`kb.okit.pl`) — static HTML generated from `kb/**/*.md` by `scripts/kb/gen_pages.py`, served by nginx on PIHA.
|
Public slice of the knowledge base (`kb-e2a24af3.okit.pl`) — static HTML generated from `kb/**/*.md` by `scripts/kb/gen_pages.py`, served by nginx on PIHA.
|
||||||
|
|
||||||
## Sieć
|
## Sieć
|
||||||
|
|
||||||
|
|
@ -15,7 +15,7 @@ Warunek wstępny deployu — sieć musi już istnieć na hoście:
|
||||||
docker network ls | grep -w proxy # brak wyniku => docker network create proxy
|
docker network ls | grep -w proxy # brak wyniku => docker network create proxy
|
||||||
```
|
```
|
||||||
|
|
||||||
Ruch publiczny i tak nie idzie przez tę sieć: npm@PIHA (vhost `kb.okit.pl`) trafia
|
Ruch publiczny i tak nie idzie przez tę sieć: npm@PIHA (vhost `kb-e2a24af3.okit.pl`) trafia
|
||||||
do kontenera po opublikowanym porcie hosta `8250`.
|
do kontenera po opublikowanym porcie hosta `8250`.
|
||||||
|
|
||||||
Dokumentacja: [kb/services/kb-site.md](../../kb/services/kb-site.md)
|
Dokumentacja: [kb/services/kb-site.md](../../kb/services/kb-site.md)
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@ services:
|
||||||
# PIHA 82x0 static-HTTP block: 8210 paperless, 8220 nextcloud,
|
# PIHA 82x0 static-HTTP block: 8210 paperless, 8220 nextcloud,
|
||||||
# 8230 kb-query, 8240 narty27 -> 8250 is the next free slot.
|
# 8230 kb-query, 8240 narty27 -> 8250 is the next free slot.
|
||||||
# Publicly the site is reached only through the npm@PIHA vhost
|
# Publicly the site is reached only through the npm@PIHA vhost
|
||||||
# kb.okit.pl; this bind is the proxy's upstream.
|
# kb-e2a24af3.okit.pl; this bind is the proxy's upstream.
|
||||||
- "8250:80"
|
- "8250:80"
|
||||||
volumes:
|
volumes:
|
||||||
# Generated output of scripts/kb/gen_pages.py — never committed, never
|
# Generated output of scripts/kb/gen_pages.py — never committed, never
|
||||||
|
|
@ -15,6 +15,11 @@ services:
|
||||||
# through the helper-container procedure in kb/runbooks/kb-site-deploy.md
|
# through the helper-container procedure in kb/runbooks/kb-site-deploy.md
|
||||||
# (docker cp cannot write into a :ro mount).
|
# (docker cp cannot write into a :ro mount).
|
||||||
- kb-site_content:/usr/share/nginx/html:ro
|
- 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:
|
# Docker on PIHA has exhausted its default address pools (~30 live stacks:
|
||||||
# "all predefined address pools have been fully subnetted"), so this stack
|
# "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
|
# must not ask for a subnet of its own. Declaring a network here suppresses
|
||||||
|
|
|
||||||
|
|
@ -7,4 +7,4 @@
|
||||||
# copy to .env.
|
# copy to .env.
|
||||||
#
|
#
|
||||||
# The public address is a generator argument, not an env var:
|
# The public address is a generator argument, not an env var:
|
||||||
# python3 scripts/kb/gen_pages.py --base-url https://kb.okit.pl
|
# python3 scripts/kb/gen_pages.py --base-url https://kb-e2a24af3.okit.pl
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ service:
|
||||||
name: kb-site
|
name: kb-site
|
||||||
owner_node: piha
|
owner_node: piha
|
||||||
role: static-html-host # public slice of the KB, rendered by scripts/kb/gen_pages.py
|
role: static-html-host # public slice of the KB, rendered by scripts/kb/gen_pages.py
|
||||||
exposure: public # public via npm@PIHA (kb.okit.pl). The container itself binds
|
exposure: public # public via npm@PIHA (kb-e2a24af3.okit.pl). The container itself binds
|
||||||
# 8250 on the LAN; npm is the sole public entry point.
|
# 8250 on the LAN; npm is the sole public entry point.
|
||||||
dependencies: [] # nginx serving a local volume — nothing else required at runtime
|
dependencies: [] # nginx serving a local volume — nothing else required at runtime
|
||||||
ports:
|
ports:
|
||||||
|
|
|
||||||
2
services/kb-site/static/robots.txt
Normal file
2
services/kb-site/static/robots.txt
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
User-agent: *
|
||||||
|
Disallow: /
|
||||||
Loading…
Reference in a new issue