Add externalTrafficPolicy to CaddyConfig for client IP preservation#216
Merged
Add externalTrafficPolicy to CaddyConfig for client IP preservation#216
Conversation
5dffeb3 to
4d623c2
Compare
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
4d623c2 to
2b8e11b
Compare
smecsia
approved these changes
Apr 9, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
GKE Autopilot L4 LoadBalancer with default
externalTrafficPolicy: Clusterperforms SNAT on incoming traffic. The real client IP is lost — Caddy sees only internal GKE node IPs inREMOTE_ADDR, andX-Forwarded-Forcontains10.x.x.xinstead of the actual user IP.The recently added
trustedProxiestells 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
externalTrafficPolicyfield toCaddyConfig. 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.go—ExternalTrafficPolicy *stringfield onCaddyConfigpkg/clouds/pulumi/kubernetes/deployment.go— pass throughArgs→SimpleContainerArgspkg/clouds/pulumi/kubernetes/caddy.go— read fromCaddyConfigand pass toArgspkg/clouds/pulumi/kubernetes/simple_container.go—serviceSpec()helper that setsExternalTrafficPolicyon the k8s Service when providedUsage
Both
trustedProxiesandexternalTrafficPolicy: Localare needed together:externalTrafficPolicy: Local— preserves real IP at L4 LB leveltrustedProxies— tells Caddy to trust XFF headers from those IPsNotes
LoadBalancer(default for Caddy)Localpolicy, traffic only routes to nodes with Caddy pods. GKE Autopilot handles this correctly with health checks.