commit 144347b0bff714c61677c16c15197d5e4477d67f Author: Lutz Finsterle Date: Thu Oct 22 13:56:44 2026 +0200 Stack unter Versionskontrolle genommen Das Repo war angelegt und hatte sogar schon ein Remote, aber keinen einzigen Commit - die Konfiguration lag faktisch nur auf der Platte. Vor dem Sprung 1.21.1 -> 1.25.1 (vier Minor-Versionen, gerbil muss mitziehen) ist genau das der fehlende Rueckweg. config/config.yml bleibt draussen, weil dort server.secret steht. Statt dessen liegt config/config.yml.example im Repo, in dem genau dieses Feld redigiert ist - dasselbe Verfahren wie bei TraefikConfig/crowdsec.yml.example. Ebenfalls draussen: config/key, config/db/ (SQLite mit Nutzern), config/letsencrypt/ (acme.json mit Account-Key und allen Zertifikatsschluesseln), Protokolle und .env. Co-Authored-By: Claude Opus 5 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..6ce7289 --- /dev/null +++ b/.gitignore @@ -0,0 +1,14 @@ +# Eingecheckt wird die Konfiguration, nicht der Zustand. +# +# config/config.yml bleibt DRAUSSEN, weil dort server.secret steht +# (openssl rand -hex 32). Statt dessen liegt config/config.yml.example im Repo, +# in dem genau dieses Feld redigiert ist - dasselbe Verfahren wie bei +# TraefikConfig/crowdsec.yml.example. +.env +config/config.yml +config/key +config/db/ +config/certs/ +config/letsencrypt/ +config/logs/ +config/traefik/logs/ diff --git a/config/config.yml.example b/config/config.yml.example new file mode 100644 index 0000000..1a6e016 --- /dev/null +++ b/config/config.yml.example @@ -0,0 +1,44 @@ +app: + # If HTTPS_PORT != 443 include the port in the URL, e.g. :8443 + dashboard_url: "https://pango.famfi.dyndns.org:8443" + log_level: "info" + log_file_path: "/app/config/logs/pangolin.log" + telemetry: + anonymous_usage: false + +domains: + famfi: + base_domain: "pango.famfi.dyndns.org" + cert_resolver: "letsencrypt" + +server: + external_port: 3000 # Pangolin's main API listen port (Traefik routes here) + internal_port: 3001 # Internal API (Gerbil + Traefik config polling) + next_port: 3002 # Next.js frontend + # CHANGE THIS: generate with: openssl rand -hex 32 + secret: "__SECRET__ # erzeugen mit: openssl rand -hex 32" + cors: + origins: + - "https://pango.famfi.dyndns.org:8443" + methods: + - "GET" + - "POST" + - "PUT" + - "DELETE" + - "PATCH" + allowed_headers: + - "X-CSRF-Token" + - "Content-Type" + credentials: false + +gerbil: + # Must match WG_PORT external mapping in .env + start_port: 51820 + base_endpoint: "pango.famfi.dyndns.org" + +flags: + require_email_verification: false + disable_signup_without_invite: false + disable_user_create_org: false + allow_raw_resources: true + allow_base_domain_resources: false diff --git a/config/traefik/dynamic_config.yml b/config/traefik/dynamic_config.yml new file mode 100644 index 0000000..b603a29 --- /dev/null +++ b/config/traefik/dynamic_config.yml @@ -0,0 +1,74 @@ +http: + middlewares: + badger: + plugin: + badger: + disableForwardAuth: true + redirect-to-https: + redirectScheme: + scheme: https + port: "8443" # must match HTTPS_PORT in .env + + routers: + # HTTP → HTTPS redirect for dashboard + main-app-router-redirect: + rule: "Host(`pango.famfi.dyndns.org`)" + service: next-service + entryPoints: + - web + middlewares: + - redirect-to-https + - badger + + # Frontend (Next.js dashboard) + next-router: + rule: "Host(`pango.famfi.dyndns.org`) && !PathPrefix(`/api/v1`)" + service: next-service + entryPoints: + - websecure + middlewares: + - badger + tls: + certResolver: letsencrypt + + # API + api-router: + rule: "Host(`pango.famfi.dyndns.org`) && PathPrefix(`/api/v1`)" + service: api-service + entryPoints: + - websecure + middlewares: + - badger + tls: + certResolver: letsencrypt + + # WebSocket + ws-router: + rule: "Host(`pango.famfi.dyndns.org`)" + service: api-service + entryPoints: + - websecure + middlewares: + - badger + tls: + certResolver: letsencrypt + + services: + next-service: + loadBalancer: + servers: + - url: "http://pangolin:3002" + + api-service: + loadBalancer: + servers: + - url: "http://pangolin:3000" + +tcp: + serversTransports: + pp-transport-v1: + proxyProtocol: + version: 1 + pp-transport-v2: + proxyProtocol: + version: 2 diff --git a/config/traefik/traefik_config.yml b/config/traefik/traefik_config.yml new file mode 100644 index 0000000..65c2d19 --- /dev/null +++ b/config/traefik/traefik_config.yml @@ -0,0 +1,48 @@ +api: + insecure: true + dashboard: true +providers: + http: + endpoint: http://pangolin:3001/api/v1/traefik-config + pollInterval: 5s + file: + filename: /etc/traefik/dynamic_config.yml +experimental: + plugins: + badger: + moduleName: github.com/fosrl/badger + version: v1.5.0 +log: + level: INFO + format: common + filePath: /var/log/traefik/traefik.log + maxSize: 100 + maxBackups: 3 + maxAge: 3 + compress: true +certificatesResolvers: + letsencrypt: + acme: + httpChallenge: + entryPoint: web + email: lutz.finsterle@t-online.de + storage: /letsencrypt/acme.json + caServer: https://acme-v02.api.letsencrypt.org/directory +entryPoints: + web: + address: ':80' + websecure: + address: ':443' + transport: + respondingTimeouts: + readTimeout: 30m + http: + tls: + certResolver: letsencrypt + encodedCharacters: + allowEncodedSlash: true + allowEncodedQuestionMark: true +serversTransport: + insecureSkipVerify: true +ping: + entryPoint: web diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..bf00ab4 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,54 @@ +services: + pangolin: + image: docker.io/fosrl/pangolin:1.21.1 + container_name: pangolin + restart: unless-stopped + volumes: + - ./config:/app/config + healthcheck: + test: ["CMD", "curl", "-f", "http://localhost:3001/api/v1/"] + interval: "10s" + timeout: "10s" + retries: 15 + + gerbil: + image: docker.io/fosrl/gerbil:1.4.3 + container_name: gerbil + restart: unless-stopped + depends_on: + pangolin: + condition: service_healthy + command: + - --reachableAt=http://gerbil:3004 + - --generateAndSaveKeyTo=/var/config/key + - --remoteConfig=http://pangolin:3001/api/v1/ + volumes: + - ./config/:/var/config + cap_add: + - NET_ADMIN + - SYS_MODULE + ports: + - "${WG_PORT:-51820}:51820/udp" # WireGuard site tunnels (Newt → Gerbil) + - "${WG_CLIENT_PORT:-21820}:21820/udp" # WireGuard client tunnels + - "${HTTPS_PORT:-8443}:443" # HTTPS (Traefik, via gerbil network) + - "8800:80" # HTTP — Let's Encrypt / redirect (router forwards 80→8800) + + traefik: + image: docker.io/traefik:v3.7.8 + container_name: traefik + restart: unless-stopped + network_mode: service:gerbil # shares gerbil's network namespace + depends_on: + pangolin: + condition: service_healthy + command: + - --configFile=/etc/traefik/traefik_config.yml + volumes: + - ./config/traefik:/etc/traefik:ro + - ./config/letsencrypt:/letsencrypt + - ./config/traefik/logs:/var/log/traefik + +networks: + default: + driver: bridge + name: pangolin