Skip to content

[reverse-proxy] Add listener side proxy protocol support and enable it in traefik#5332

Merged
pascal-fischer merged 2 commits intomainfrom
reverse-proxt-proxy-protocol
Feb 16, 2026
Merged

[reverse-proxy] Add listener side proxy protocol support and enable it in traefik#5332
pascal-fischer merged 2 commits intomainfrom
reverse-proxt-proxy-protocol

Conversation

@lixmal
Copy link
Collaborator

@lixmal lixmal commented Feb 15, 2026

Describe your changes

Fixes #5324

Issue ticket number and link

Stack

Checklist

  • Is it a bug fix
  • Is a typo/documentation fix
  • Is a feature enhancement
  • It is a refactor
  • Created tests that fail without the change (if possible)

By submitting this pull request, you confirm that you have read and agree to the terms of the Contributor License Agreement.

Documentation

Select exactly one:

  • I added/updated documentation for this change
  • Documentation is not needed for this change (explain why)

Docs PR URL (required if "docs added" is checked)

Paste the PR link from https://github.com/netbirdio/docs here:

https://github.com/netbirdio/docs/pull/__

Summary by CodeRabbit

  • New Features

    • PROXY protocol support to preserve real client IPs when behind L4 proxies; configurable via flag/env.
    • Central TRAEFIK_IP and generated Traefik dynamic TCP config propagated into compose/YAML and proxy settings.
    • Default health and debug endpoints with improved startup behavior and metrics exposure.
  • Tests

    • Added tests for PROXY protocol integration, trusted-proxy policies, and header handling.
  • Chores

    • Added go-proxyproto dependency.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 15, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds optional PROXY protocol support (flag/env + Server field), listener wrapper and trusted-proxies policy, health/debug startup refactors, unit tests for PROXY behavior, a go-proxyproto dependency, and centralizes Traefik IP handling in infra scripts.

Changes

Cohort / File(s) Summary
Go Module
go.mod
Added direct dependency github.com/pires/go-proxyproto v0.11.0.
Infrastructure / Scripts
infrastructure_files/getting-started.sh
Introduced TRAEFIK_IP (default 172.30.0.10); replaced hardcoded Traefik IP usages; added render_traefik_dynamic() to generate traefik-dynamic.yaml; mount file when built-in Traefik enabled; set NB_PROXY_PROXY_PROTOCOL=true and NB_PROXY_TRUSTED_PROXIES=$TRAEFIK_IP in rendered proxy env; propagate TRAEFIK_IP into reverseProxy.trustedHTTPProxies and compose extra_hosts/labels.
CLI / Command
proxy/cmd/proxy/cmd/root.go
Added --proxy-protocol Cobra flag (env NB_PROXY_PROXY_PROTOCOL) and wired it into the Server configuration.
Core Server
proxy/server.go
Added ProxyProtocol bool field to Server; introduced initDefaults, startDebugEndpoint, startHealthServer, wrapProxyProtocol, and proxyProtocolPolicy; refactored ListenAndServe/ListenAndServeTLS flows to optionally wrap listeners with PROXY protocol handling; added default health/debug addresses and related defaulting.
Tests
proxy/proxyprotocol_test.go
New tests validating PROXY v2 integration: RemoteAddr override via PROXY header and policy behavior (REQUIRE/IGNORE/REJECT) for trusted/untrusted/invalid sources; uses go-proxyproto, logrus, testify.

Sequence Diagram(s)

sequenceDiagram
    autonumber
    actor Client
    participant Traefik as "L4 Proxy (Traefik)"
    participant Listener as "TCP Listener"
    participant Wrapper as "PROXY Wrapper"
    participant Policy as "TrustedProxies Policy"
    participant TLS as "TLS Server"

    Client->>Traefik: TCP connect
    Traefik->>Listener: Forward connection with PROXY v2 header
    Listener->>Wrapper: Accept connection
    Wrapper->>Policy: Evaluate source addr vs TrustedProxies
    alt Policy = REQUIRE
        Policy-->>Wrapper: REQUIRE
        Wrapper->>Wrapper: Parse PROXY v2 header, set RemoteAddr (client IP)
        Wrapper->>TLS: Pass connection (client IP preserved)
    else Policy = IGNORE
        Policy-->>Wrapper: IGNORE
        Wrapper->>TLS: Pass connection unchanged
    else Policy = REJECT
        Policy-->>Wrapper: REJECT
        Wrapper->>Listener: Close / reject connection
    end
    TLS->>Client: Proceed with TLS handshake
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Poem

🐰 I tunneled through code where headers play,
I fetched the true IP that tried to stray.
Traefik waved its little flag,
I parsed the PROXY in my bag,
Now client hops are shown the right way! 🥕

🚥 Pre-merge checks | ✅ 5 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 20.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and specifically describes the main change: adding PROXY protocol support on the listener side and enabling it in Traefik.
Description check ✅ Passed The PR description references issue #5324 and follows the template, with checklist completed and documentation marked as not needed.
Linked Issues check ✅ Passed The code changes directly address issue #5324 by implementing PROXY protocol support to capture actual client IPs instead of Traefik's IP in proxy event logs.
Out of Scope Changes check ✅ Passed All changes are directly related to implementing PROXY protocol support and configuring it in Traefik to fix the proxy event source IP issue.
Merge Conflict Detection ✅ Passed ✅ No merge conflicts detected when merging into main

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch reverse-proxt-proxy-protocol

Tip

Issue Planner is now in beta. Read the docs and try it out! Share your feedback on Discord.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Fix all issues with AI agents
In `@go.mod`:
- Line 250: The go.mod lists github.com/pires/go-proxyproto as indirect while
proxy/server.go imports it directly; update go.mod to make
github.com/pires/go-proxyproto a direct dependency (or run module cleanup) so CI
recognizes it as required: add the dependency to the main require block at
v0.11.0 (remove it from the indirect section) or run go module tidy/resolve so
the direct import in proxy/server.go is reflected in go.mod.

In `@proxy/proxyprotocol_test.go`:
- Line 96: Update the assertion message string to fix the typo: change
"unparseable address should be rejected" to "unparsable address should be
rejected" in the test where assert.Equal(t, proxyproto.REJECT, policy, ...) is
called (the assertion referencing variable policy and using assert.Equal) so the
message uses the correct spelling "unparsable".
🧹 Nitpick comments (1)
proxy/server.go (1)

267-280: Consider warning when ProxyProtocol is enabled without TrustedProxies.

When TrustedProxies is empty, no policy is set on the listener, so go-proxyproto defaults to accepting PROXY headers from any source. This allows untrusted clients to spoof their source IP. While this may be intentional for simple setups, a log warning would help operators notice the misconfiguration.

Suggested addition
 func (s *Server) wrapProxyProtocol(ln net.Listener) net.Listener {
 	ppListener := &proxyproto.Listener{
 		Listener:          ln,
 		ReadHeaderTimeout: 5 * time.Second,
 	}
 	if len(s.TrustedProxies) > 0 {
 		ppListener.Policy = s.proxyProtocolPolicy
+	} else {
+		s.Logger.Warn("PROXY protocol enabled without trusted proxies; any source may send PROXY headers")
 	}
 	s.Logger.Info("PROXY protocol enabled on listener")
 	return ppListener
 }

@lixmal lixmal force-pushed the reverse-proxt-proxy-protocol branch 2 times, most recently from 2f72544 to cf99f22 Compare February 15, 2026 09:43
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@proxy/server.go`:
- Around line 267-280: The code currently enables proxyproto.Listener in
wrapProxyProtocol which defaults to trusting all senders if s.TrustedProxies is
empty; enforce a safety check in ListenAndServe before calling
wrapProxyProtocol: if Proxy protocol is enabled and len(s.TrustedProxies) == 0
then return an error (or log fatal) requiring TrustedProxies to be configured
(fail startup) so proxyproto.Listener is never created in the insecure default
mode; reference s.wrapProxyProtocol, s.TrustedProxies, ListenAndServe and
proxyproto.Listener when making the change.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@infrastructure_files/getting-started.sh`:
- Around line 588-589: The label
traefik.tcp.services.proxy-tls.loadbalancer.proxyProtocol.version=2 is invalid
in Traefik v3.6; instead create a TCP ServersTransport dynamic resource (e.g.,
in dynamic.yaml) that sets proxyProtocol.version: 2 and then replace the invalid
label with the service reference
traefik.tcp.services.proxy-tls.loadbalancer.serverstransport=<transport-name>@<provider>
so the proxy protocol v2 is applied via the ServersTransport.

@lixmal lixmal force-pushed the reverse-proxt-proxy-protocol branch from cf99f22 to bbb89f5 Compare February 15, 2026 10:07
@lixmal lixmal changed the title [reverse-proxy] Add listner side proxy protocol support and enable it in traefik [reverse-proxy] Add listener side proxy protocol support and enable it in traefik Feb 15, 2026
# Conflicts:
#	infrastructure_files/getting-started.sh
@sonarqubecloud
Copy link

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (2)
infrastructure_files/getting-started.sh (2)

766-768: trustedHTTPProxies is rendered for all reverse proxy types, but only applies to built-in Traefik.

render_combined_yaml is called for every REVERSE_PROXY_TYPE, so non-Traefik setups (types 2–5) and external-Traefik setups (type 1, where Traefik has a different IP) will get a trustedHTTPProxies entry pointing to 172.30.0.10, which is meaningless or incorrect in those contexts. Consider conditionally emitting this block only when REVERSE_PROXY_TYPE == "0".

Proposed fix
+  local reverse_proxy_config=""
+  if [[ "$REVERSE_PROXY_TYPE" == "0" ]]; then
+    reverse_proxy_config="
+  reverseProxy:
+    trustedHTTPProxies:
+      - \"$TRAEFIK_IP/32\""
+  fi
+
   cat <<EOF
 ...
-  reverseProxy:
-    trustedHTTPProxies:
-      - "$TRAEFIK_IP/32"
-
+${reverse_proxy_config}
   store:

646-656: Blank lines emitted in docker-compose when proxy is disabled — cosmetic only.

When ENABLE_PROXY is false, $traefik_file_provider (line 649) and $traefik_dynamic_volume (line 656) expand to empty strings, leaving blank lines in the command: and volumes: blocks. This is valid YAML and won't cause issues — just noting for awareness.

@pascal-fischer pascal-fischer merged commit 0146e39 into main Feb 16, 2026
45 of 46 checks passed
@pascal-fischer pascal-fischer deleted the reverse-proxt-proxy-protocol branch February 16, 2026 22:40
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.

Proxy events showing IP of Traefik proxy as source

3 participants