# Traefik dynamic configuration. # # This file is SECRET-FREE and is committed to git. The CrowdSec bouncer, which # is the only middleware needing a credential, is defined as a Docker label in # docker-compose.yml so its key stays in .env (gitignored). http: routers: # Router for Gogs (if it's running outside Docker or on different network) # Disabled - now using Gitea with Docker labels # gogs: # rule: "Host(`git.pkartchner.com`)" # entryPoints: # - https # service: gogs # middlewares: # - geoblock # - secure-headers # - crowdsec-bouncer # tls: # certResolver: letsencrypt # Pi-hole admin UI - internal only, hosted on 10.20.10.3 pihole: rule: "Host(`dns.pkartchner.com`)" entryPoints: - https service: pihole middlewares: - internal-whitelist - secure-headers tls: certResolver: letsencrypt # Technitium DNS UI - internal only, hosted on 10.20.10.3 technitium: rule: "Host(`technitium.pkartchner.com`)" entryPoints: - https service: technitium middlewares: - internal-whitelist - secure-headers tls: certResolver: letsencrypt services: # Service for Gogs # Disabled - now using Gitea with Docker labels # gogs: # loadBalancer: # servers: # - url: "http://gogs.pkartchner.com:3000" # Pi-hole on 10.20.10.3 pihole: loadBalancer: servers: - url: "http://10.20.10.3:80" # Technitium on 10.20.10.3 technitium: loadBalancer: servers: - url: "http://10.20.10.3:5380" middlewares: # Security headers — applied to every public site router. secure-headers: headers: forceSTSHeader: true stsIncludeSubdomains: true stsPreload: true stsSeconds: 31536000 customFrameOptionsValue: "SAMEORIGIN" contentTypeNosniff: true browserXssFilter: true referrerPolicy: "same-origin" # Restrict a router to the internal LAN only. internal-whitelist: ipAllowList: sourceRange: - "10.20.10.0/24" - "10.20.140.0/24" - "127.0.0.1/32" # NOTE: crowdsec-bouncer is deliberately NOT defined here. # # It carries an API key, and Traefik does not interpolate environment # variables in this file — so defining it here would force a secret into # version control, or force this whole file to be gitignored (which is what # previously happened, costing the repo its "config lives in git" property). # # It is defined as a Docker label on the traefik container instead, where # compose substitutes ${CROWDSEC_LAPI_KEY} from .env. # # Reference it as `crowdsec-bouncer@docker`, NOT `@file`. # This file is therefore secret-free and safe to commit. # GeoIP blocking — allow US traffic only. geoblock: plugin: geoblock: silentStartUp: false allowLocalRequests: true logLocalRequests: false logAllowedRequests: true logApiRequests: true api: https://get.geojs.io/v1/ip/country/{ip} apiTimeoutMs: 750 # 25 was the upstream default and is far too small: only 25 IPs are # remembered, so nearly every visitor triggers a fresh lookup against # a free third-party API, adding up to 750ms before WordPress starts # and risking rate limits. A few thousand entries is a trivial amount # of memory and turns the API call into a rare event. cacheSize: 5000 forceMonthlyUpdate: true # FAIL OPEN. With this false, a geojs.io outage or slow response makes # every lookup "unknown" and therefore blocked — taking the site down # for US visitors too, because of a third-party service we do not # control sitting in the path of every request. Allowing unknowns # means an outage degrades to "geoblocking temporarily ineffective" # instead of "site is down". CrowdSec still blocks actual attackers. allowUnknownCountries: true unknownCountryApiResponse: nil countries: - US # Rate limiting, used by Harbor on prod. harbor-ratelimit: rateLimit: average: 100 burst: 50 period: 1m