# heather — Caddy как единый edge (TLS termination, reverse proxy). # # Заменяет Traefik (k3s built-in, отключён) и старые Caddy на iscg-dev/jul11. # Паттерн как на iscg-dev: system Caddy, конфиг декларативно в nix. # # Caddy терминирует TLS на :80/:443 и роутит по Host на: # - host-сервисы (syncthing GUI на localhost:8384) — напрямую # - docker-контейнеры (forgejo на localhost:3030 и т.д.) — проброшенные порты # # ACME: пока staging (чтобы не забанили Let's Encrypt пока DNS не переведён на # heather). Когда A-записи iscg.dev → 51.250.45.111 — переключим на production # (убрать acme_ca, Caddy использует production LE по умолчанию). { config, pkgs, lib, ... }: let # caddy/basic-auth под паролем. hash сгенерить: # caddy hash-password --plaintext '' # (на heather: nix-shell -p caddy --run 'caddy hash-password ...') # Защищает syncthing GUI снаружи (GUI слушает только 127.0.0.1:8384, # Caddy проксирует sync.iscg.dev + basicauth). # pi-web basicauth. Пароль в pass iscg.dev/pi-web/auth (plaintext). # Hash сгенерить на heather: nix-shell -p caddy --run 'caddy hash-password --plaintext ...' piBasicAuth = '' basicauth { mikl $2a$14$yqYiMGLvtPs0sLRZhu1/VOfiRO.CARIBcGGrFpo4iwOd9s4o4IVCy } ''; syncBasicAuth = '' basicauth { mikl $2a$14$ozHN9/aa4VhPX43NN8MKdODKvOoJ5MBpOUFhzkxwvb58ZI5b6eqEK } ''; in { services.caddy = { enable = true; # DNS переведён на heather (2026-07-12). Production ACME. # Если нужен staging обратно: раскомментировать acmeCA ниже. # acmeCA = "https://acme-staging-v02.api.letsencrypt.org/directory"; email = "mikl@iscg.dev"; virtualHosts = { # --- Syncthing GUI (host-сервис) --- # header_up Host 127.0.0.1:8384 — обходим syncthing host-check (DNS # rebinding protection): syncthing сравнивает Host header с адресом # прослушивания (127.0.0.1:8384) и отдаёт 403 "Host check error" для # внешних доменов. Подменяем Host на бэкенде. "sync.iscg.dev".extraConfig = syncBasicAuth + '' reverse_proxy 127.0.0.1:8384 { header_up Host 127.0.0.1:8384 } ''; # --- Контейнеры (arion, см. arion-compose.nix) --- # Forgejo "git.iscg.dev".extraConfig = '' handle /robots.txt { header Content-Type text/plain respond "User-agent: * Allow: /" 200 } reverse_proxy 127.0.0.1:3030 ''; # Matrix tuwunel (homeserver) "iscg.dev".extraConfig = '' # Static file sharing (https://iscg.dev/files/). # handle_path stripает /files/ prefix → file_server ищет в root. # Не конфликтует с matrix (использует /_matrix/*, /.well-known/matrix). handle_path /files/* { root * /var/www/files file_server } reverse_proxy 127.0.0.1:8008 encode zstd gzip ''; # Matrix federation "iscg.dev:8448".extraConfig = '' reverse_proxy 127.0.0.1:8008 ''; # tuwunel-admin "admin.iscg.dev".extraConfig = '' reverse_proxy 127.0.0.1:8009 ''; # Cinny (matrix web client) "chat.iscg.dev".extraConfig = '' reverse_proxy 127.0.0.1:8080 encode zstd gzip ''; # Jitsi Meet (Caddy v2 auto-handles WebSocket upgrade) # Docs: https://jitsi.github.io/handbook/docs/devops-guide/devops-guide-docker#running-behind-a-reverse-proxy "jitsi.iscg.dev".extraConfig = '' reverse_proxy 127.0.0.1:8000 ''; # Teable "teable.iscg.dev".extraConfig = '' reverse_proxy 127.0.0.1:2345 ''; # Silverbullet "note.iscg.dev".extraConfig = '' reverse_proxy 127.0.0.1:3001 ''; # Homepage (dashboard со всеми сервисами heather) "home.iscg.dev".extraConfig = '' reverse_proxy 127.0.0.1:3002 ''; # pi-web (web UI для pi coding agent) # basicauth защищает весь vhost включая SSE /api/agent — leak нет. "pi.iscg.dev".extraConfig = piBasicAuth + '' reverse_proxy 127.0.0.1:30141 ''; # File explorer (directory listing) для раздачи файлов через браузер. # browse включает Caddy's built-in directory listing. Корень /var/www/pub. # Без auth — класть только публичные файлы. "files.iscg.dev".extraConfig = '' root * /var/www/pub file_server browse ''; }; }; }