user@homelab:~/docs$ cat 14-public.md
Veřejný web (Cloudflare Tunnel)
Vyčištění obsahu, výběr domény, tunel z clusteru do Cloudflare a svejklab.dev online
# obsah
Výsledek
Web je veřejně na https://svejklab.dev:
- žádný otevřený port na routeru, žádná veřejná IP adresa a žádný port forwarding,
- HTTPS zajišťuje Cloudflare (certifikát i obnova),
- z clusteru se ven dostane jen HTTP přes Traefik, SSH ani Kubernetes API přes tunel nevedou.
návštěvník ─HTTPS─▶ Cloudflare (prg/vie) ◀─tunel, odchozí QUIC─ cloudflared (pod)
│ HTTP
▼
Traefik (kube-system) ─▶ homelab-docsKlíčové je, že spojení navazuje cloudflared z clusteru směrem ven. Router ho vidí jako běžný odchozí provoz, stejně jako když si notebook otevře web. Nevadí proto ani dvojitý NAT (router poskytovatele → vlastní router), ani chybějící veřejná IPv4.
1. Vyčištění obsahu
Než šel web ven, odstranili jsme z něj všechno, co by prozrazovalo detaily o domácí síti:
| Co | Nahrazeno |
|---|---|
| IP adresa serveru | <ip-serveru> |
| IP adresy routerů | <ip-routeru> |
| uživatelské jméno na serveru | <uživatel>, prompt v záhlaví user@homelab |
| konkrétní modely routerů | obecně „router poskytovatele“ a „vlastní Wi-Fi router“ |
| MAC adresy, SSID, hesla, tokeny | na webu nikdy nebyly |
Kontrola na sestaveném webu, ne jen ve zdrojácích:
cd homelab-docs/site && hugo --destination /tmp/public
grep -rhoE "192\.168\.[0-9.]+" /tmp/public | sort -u # musí vrátit nic2. Výběr domény
Doména je koupená přímo u Cloudflare. Ten prodává za nákupní cenu bez přirážky, takže cena za prodloužení je stejná jako ceník registru.
Při výběru rozhodovala cena prodloužení, ne cena prvního roku:
| Skupina koncovek | Cena 1. rok | Prodloužení | Verdikt |
|---|---|---|---|
.bid, .win, .download, .loan… | ~$4 | ~$5 | ❌ pověst spamových koncovek, firemní filtry je často blokují |
.fun, .online, .site, .store, .tech | ~$5 | $25–49 | ❌ levný jen první rok |
.fyi | ~$5 | ~$5 | ✅ nejlevnější rozumná volba |
.dev | ~$8 | ~$12 | ✅ vybráno: technická koncovka, vynucuje HTTPS (HSTS preload) |
Jméno svejklab = příjmení + lab. Web běží přímo na hlavní doméně a další služby dostanou subdomény: grafana.svejklab.dev, mcp.svejklab.dev…
3. Tunel v Cloudflare
Zero Trust → Networks → Tunnels → Create a tunnel:
- typ Cloudflared, název
homelab, - obrazovka Install and Run nabízí instalaci pro Windows, macOS nebo Debian. Nic z toho se nespouští, konektor poběží v Kubernetes. Ikonou kopírování se jen zkopíruje celý příkaz, protože obsahuje token,
- Continue se odemkne až po připojení konektoru, takže nejdřív kroky 4 a 5 níže.
4. Token do clusteru jako Secret
kubectl create namespace cloudflared --dry-run=client -o yaml | kubectl apply -f -Teď samostatně jen tento řádek. Pak vlož zkopírovaný příkaz z Cloudflare (Cmd+V) a Enter:
read -rsp "Vlož celý příkaz z Cloudflare: " LINE; echoPak:
TOKEN="${LINE##* }"; unset LINE
echo "Délka tokenu: ${#TOKEN}"
kubectl -n cloudflared create secret generic tunnel-token --from-literal=token="$TOKEN"
unset TOKENread -rspnačte vstup bez zobrazení a bez zápisu do historie (-s= silent),${LINE##* }je bash expanze, která z textu vezme poslední slovo, tedy samotný token,${#TOKEN}vypíše jen délku tokenu (u nás 184). Tím se ověří vložení, aniž by se token ukázal,unsetsmaže proměnnou z paměti shellu.
Kontrola délky uloženého tokenu, opět bez jeho vypsání:
kubectl -n cloudflared get secret tunnel-token -o jsonpath='{.data.token}' | base64 -d | wc -c5. Konektor cloudflared
Manifest cloudflared/cloudflared.yaml obsahuje Namespace a Deployment:
| Nastavení | Proč |
|---|---|
image cloudflare/cloudflared:2026.7.3 (pevná verze) | žádné nečekané změny; aktualizace vědomě změnou tagu |
--no-autoupdate | v kontejneru se binárka nemá sama měnit, verzi určuje image |
TUNNEL_TOKEN ze Secretu (secretKeyRef) | token není v manifestu ani v gitu |
--metrics 0.0.0.0:2000 + liveness /ready | Kubernetes restartuje konektor, když ztratí spojení |
namespace restricted, uid 65532, read-only FS, drop ALL | stejně přísné jako web |
| limit 128 Mi | bez swapu je limit nutnost |
kubectl apply -f cloudflared/cloudflared.yaml
kubectl -n cloudflared logs deploy/cloudflared -fÚspěšný start v logu:
INF Registered tunnel connection connIndex=0 ... location=prg01 protocol=quic
INF Registered tunnel connection connIndex=1 ... location=vie05 protocol=quic
INF Registered tunnel connection connIndex=2 ... location=prg03 protocol=quic
INF Registered tunnel connection connIndex=3 ... location=vie05 protocol=quic
SUMMARY: Environment is healthy. cloudflared will use 'quic' as primary protocol.- 4 spojení do 2 lokalit (Praha, Vídeň): výpadek jednoho datacentra Cloudflare tunel neshodí,
- QUIC běží přes UDP 7844 ven a firewall ho pustil (pody mají povolený forward, viz k3s),
- token se v logu zobrazuje jen jako
TUNNEL_TOKEN:*****.
V Cloudflare se tunel přepne na Healthy.
6. Trasa (Published application)
Detail tunelu → Add route → Published application:
| Pole | Hodnota |
|---|---|
| Subdomain | prázdné (hlavní doména) |
| Domain | svejklab.dev |
| Path | prázdné |
| Service URL | http://traefik.kube-system.svc.cluster.local:80 |
Proč tahle adresa: cloudflared běží uvnitř clusteru, takže používá vnitřní DNS jméno služby ve tvaru <service>.<namespace>.svc.cluster.local. Provoz jde do Traefiku, ne přímo na web. Ingress pak rozhodne, kam patří. Ingress webu má pravidlo bez hostu (catch-all), takže zachytí i svejklab.dev.
Další aplikaci tak stačí přidat novou trasou (grafana → stejná URL) a Ingressem s host: grafana.svejklab.dev.
DNS záznam (CNAME svejklab.dev → <tunnel-id>.cfargotunnel.com, na hlavní doméně díky CNAME flattening) vytvořil Cloudflare sám.
7. Nasazení vyčištěné verze a test
cd ~/homelab-docs
./deploy.sh 0.2.0Test z mobilu přes mobilní data, ne přes domácí Wi-Fi. Jen tak je jisté, že web jde opravdu z internetu. V záhlaví musí být user@homelab, podle toho se pozná vyčištěná verze.
Bezpečnostní shrnutí
| Riziko | Opatření |
|---|---|
| útok na domácí síť přes otevřený port | žádný port není otevřený, tunel je odchozí |
| únik tokenu | Secret, read -s, nikde v souborech; při úniku Rotate token |
| kompromitace webu | statické HTML, nginx bez root práv, read-only FS, CSP hlavičky |
| útok z kompromitovaného podu na cluster | automountServiceAccountToken: false, PSS restricted |
| přetížení / DDoS | stojí před tím Cloudflare |
| prozrazení topologie | vyčištěný obsah |
Co dál
- Cloudflare Access (Zero Trust → Access → Applications) pro budoucí admin rozhraní, například Grafanu: přístup jen po přihlášení e-mailem,
- Private hostname / WARP pro SSH k homelabu odkudkoli bez otevřených portů, jako alternativa k Tailscale.