Previous attempt tried a separate internal-only :8080 entrypoint for pulsenode to reach the API directly - that turned out to 404 no matter what (--api.dashboard=true alone didn't register a working router there, and adding --api=true plus an explicit entrypoint still didn't produce a matching router; verified against a local traefik:v3 container before giving up on that path rather than keep guessing against the live one). Cleaner approach: expose it through the same web/web-secure entrypoints everything else already uses, via a new file-provider router (traefik/dynamic/dashboard.yaml) matching Traefik's own documented self-referencing pattern - Host(`traefik.pivoine.art`) && (PathPrefix(`/api`) || PathPrefix(`/dashboard`)) routed to the built-in api@internal service, gated by vpn-only same as pulsenode's own router. vpn-only's sourceRange now also includes falcon_network's own subnet (172.18.0.0/16) alongside the tailnet ranges - a container on our own internal docker network is as trusted as a tailnet peer, and without this pulsenode's own request to the API (a container-to-container call, not a VPN-sourced one) would get the same 403 a random internet visitor would. pulsenode's traefik widget now points at https://traefik.pivoine.art/api instead of the internal :8080 attempt.
60 lines
1.9 KiB
YAML
60 lines
1.9 KiB
YAML
tls:
|
|
options:
|
|
default:
|
|
minVersion: VersionTLS12
|
|
cipherSuites:
|
|
- TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
|
|
- TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384
|
|
- TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305
|
|
- TLS_AES_128_GCM_SHA256
|
|
- TLS_AES_256_GCM_SHA384
|
|
- TLS_CHACHA20_POLY1305_SHA256
|
|
curvePreferences:
|
|
- CurveP521
|
|
- CurveP384
|
|
sniStrict: true
|
|
|
|
http:
|
|
middlewares:
|
|
security-headers:
|
|
headers:
|
|
stsSeconds: 31536000
|
|
stsIncludeSubdomains: true
|
|
stsPreload: true
|
|
forceSTSHeader: true
|
|
customFrameOptionsValue: "SAMEORIGIN"
|
|
browserXssFilter: true
|
|
contentTypeNosniff: true
|
|
referrerPolicy: "strict-origin-when-cross-origin"
|
|
customResponseHeaders:
|
|
Permissions-Policy: "camera=(), microphone=(), geolocation=(), payment=(), usb=(), magnetometer=(), accelerometer=(), gyroscope=()"
|
|
X-Content-Type-Options: "nosniff"
|
|
X-Frame-Options: "SAMEORIGIN"
|
|
no-index:
|
|
headers:
|
|
customResponseHeaders:
|
|
X-Robots-Tag: "none,noarchive,nosnippet,notranslate,noimageindex"
|
|
rate-limit:
|
|
rateLimit:
|
|
average: 100
|
|
burst: 50
|
|
period: 1s
|
|
api-rate-limit:
|
|
rateLimit:
|
|
average: 30
|
|
burst: 15
|
|
period: 1s
|
|
# Only let traffic through whose source IP is inside the Tailscale/Headscale
|
|
# mesh (100.64.0.0/10, fd7a:115c:a1e0::/48) or on falcon_network itself
|
|
# (172.18.0.0/16) - the latter so containers on our own internal docker
|
|
# network (e.g. pulsenode calling this router directly to reach
|
|
# api@internal) count as trusted the same way a tailnet peer does. Public
|
|
# DNS can still resolve these hostnames, but anyone reaching them over the
|
|
# open internet gets a 403.
|
|
vpn-only:
|
|
ipAllowList:
|
|
sourceRange:
|
|
- "100.64.0.0/10"
|
|
- "fd7a:115c:a1e0::/48"
|
|
- "172.18.0.0/16"
|