Skip to content

Add externalTrafficPolicy to CaddyConfig for client IP preservation#216

Merged
Cre-eD merged 1 commit intostagingfrom
feat/caddy-external-traffic-policy
Apr 9, 2026
Merged

Add externalTrafficPolicy to CaddyConfig for client IP preservation#216
Cre-eD merged 1 commit intostagingfrom
feat/caddy-external-traffic-policy

Conversation

@Cre-eD
Copy link
Copy Markdown
Contributor

@Cre-eD Cre-eD commented Apr 9, 2026

Problem

GKE Autopilot L4 LoadBalancer with default externalTrafficPolicy: Cluster performs SNAT on incoming traffic. The real client IP is lost — Caddy sees only internal GKE node IPs in REMOTE_ADDR, and X-Forwarded-For contains 10.x.x.x instead of the actual user IP.

The recently added trustedProxies tells Caddy to trust XFF from known proxies, but it doesn't help when the L4 LB strips the real IP before it reaches Caddy.

Fix

Adds externalTrafficPolicy field to CaddyConfig. When set to "Local", the L4 LoadBalancer routes traffic directly to the node running the Caddy pod, preserving the original source IP.

Changes

  • pkg/clouds/k8s/types.goExternalTrafficPolicy *string field on CaddyConfig
  • pkg/clouds/pulumi/kubernetes/deployment.go — pass through ArgsSimpleContainerArgs
  • pkg/clouds/pulumi/kubernetes/caddy.go — read from CaddyConfig and pass to Args
  • pkg/clouds/pulumi/kubernetes/simple_container.goserviceSpec() helper that sets ExternalTrafficPolicy on the k8s Service when provided

Usage

# server.yaml
caddy:
  enable: true
  externalTrafficPolicy: "Local"
  trustedProxies:
    - "10.0.0.0/8"
    - "173.245.48.0/20"

Both trustedProxies and externalTrafficPolicy: Local are needed together:

  • externalTrafficPolicy: Local — preserves real IP at L4 LB level
  • trustedProxies — tells Caddy to trust XFF headers from those IPs

Notes

  • Only applies when service type is LoadBalancer (default for Caddy)
  • With Local policy, traffic only routes to nodes with Caddy pods. GKE Autopilot handles this correctly with health checks.
  • Manually tested on PAY-SPACE production — client IPs now correctly appear in application logs

@Cre-eD Cre-eD force-pushed the feat/caddy-external-traffic-policy branch 2 times, most recently from 5dffeb3 to 4d623c2 Compare April 9, 2026 09:24
@Cre-eD Cre-eD requested a review from smecsia April 9, 2026 09:31
When set to 'Local', the L4 LoadBalancer skips SNAT and preserves the
real client source IP. Without this, X-Forwarded-For only contains
internal GKE node IPs regardless of trustedProxies configuration.

Required for correct client IP detection in applications behind Caddy
on GKE Autopilot with L4 LoadBalancer (which is the default service type).

Usage in server.yaml:
  caddy:
    enable: true
    externalTrafficPolicy: Local
    trustedProxies:
      - 10.0.0.0/8
      - 173.245.48.0/20
@Cre-eD Cre-eD force-pushed the feat/caddy-external-traffic-policy branch from 4d623c2 to 2b8e11b Compare April 9, 2026 12:43
@Cre-eD Cre-eD merged commit a0f4ec6 into staging Apr 9, 2026
2 checks passed
@Cre-eD Cre-eD deleted the feat/caddy-external-traffic-policy branch April 9, 2026 13:04
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants