From 318f48036d5bbc1740ae96ca1cc794f8dde4dd16 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Kr=C3=BCger?= Date: Mon, 17 Aug 2026 15:54:40 +0200 Subject: [PATCH] refactor: move vpn-only middleware from falcon.yaml to security.yaml falcon-vpn-only was defined inline in falcon.yaml even though it's a generic ipAllowList restriction with nothing falcon-specific about it - security.yaml is where the other reusable middlewares (security- headers, no-index, rate-limit) already live. Renamed to vpn-only to match their bare naming style now that it's not tied to one file, and updated falcon.yaml's two router references to vpn-only@file (same cross-file pattern already used there for security-headers@file). Also applies it to the pulsenode stack: it's a dashboard surfacing container names/health for every service here, and there's no auth in front of it yet, so gating it to the tailnet is a real mitigation for that gap rather than leaving it open on the public hostname. --- pulsenode/compose.yml | 4 ++-- traefik/dynamic/falcon.yaml | 13 ++----------- traefik/dynamic/security.yaml | 9 +++++++++ 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/pulsenode/compose.yml b/pulsenode/compose.yml index c5f0eee..4db2668 100644 --- a/pulsenode/compose.yml +++ b/pulsenode/compose.yml @@ -30,12 +30,12 @@ services: - "traefik.enable=true" - "traefik.http.routers.pulsenode-web.rule=Host(`${TRAEFIK_HOST}`)" - "traefik.http.routers.pulsenode-web.entrypoints=web" - - "traefik.http.routers.pulsenode-web.middlewares=pulsenode-redirect-web-secure" + - "traefik.http.routers.pulsenode-web.middlewares=vpn-only@file,pulsenode-redirect-web-secure" - "traefik.http.middlewares.pulsenode-redirect-web-secure.redirectscheme.scheme=https" - "traefik.http.routers.pulsenode-web-secure.rule=Host(`${TRAEFIK_HOST}`)" - "traefik.http.routers.pulsenode-web-secure.entrypoints=web-secure" - "traefik.http.routers.pulsenode-web-secure.tls.certresolver=resolver" - - "traefik.http.routers.pulsenode-web-secure.middlewares=security-headers@file,no-index@file" + - "traefik.http.routers.pulsenode-web-secure.middlewares=vpn-only@file,security-headers@file,no-index@file" - "traefik.http.services.pulsenode-web-secure.loadbalancer.server.port=3000" - "traefik.docker.network=${NETWORK_NAME}" networks: diff --git a/traefik/dynamic/falcon.yaml b/traefik/dynamic/falcon.yaml index e8d77ee..6561bd8 100644 --- a/traefik/dynamic/falcon.yaml +++ b/traefik/dynamic/falcon.yaml @@ -11,15 +11,6 @@ http: triggershell-redirect-web-secure: redirectScheme: scheme: https - # Only let traffic through whose source IP is inside the Tailscale/Headscale - # mesh (100.64.0.0/10, fd7a:115c:a1e0::/48). Public DNS still resolves these - # hostnames, but anyone reaching them over the open internet gets a 403 — - # this range is only reachable by actually being a peer on the tailnet. - falcon-vpn-only: - ipAllowList: - sourceRange: - - "100.64.0.0/10" - - "fd7a:115c:a1e0::/48" routers: triggershell-web: @@ -27,7 +18,7 @@ http: entrypoints: - web middlewares: - - falcon-vpn-only + - vpn-only@file - triggershell-redirect-web-secure service: triggershell @@ -38,7 +29,7 @@ http: tls: certResolver: resolver middlewares: - - falcon-vpn-only + - vpn-only@file - security-headers@file service: triggershell diff --git a/traefik/dynamic/security.yaml b/traefik/dynamic/security.yaml index b5897f4..8b95bf5 100644 --- a/traefik/dynamic/security.yaml +++ b/traefik/dynamic/security.yaml @@ -44,3 +44,12 @@ http: 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). Public DNS can still resolve + # these hostnames, but anyone reaching them over the open internet gets a + # 403 — this range is only reachable by actually being a peer on the tailnet. + vpn-only: + ipAllowList: + sourceRange: + - "100.64.0.0/10" + - "fd7a:115c:a1e0::/48"