Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Support forwarded IPs using a configurable header #178

Draft
wants to merge 5 commits into
base: main
Choose a base branch
from

Conversation

Leobaillard
Copy link

Closes #84

Overview

This PR implements a new configuration option for the rules that allows specifying an HTTP header that contains the real remote IP in cases where Traefik is behind a reverse proxy or a CDN (like Cloudflare for instance).

Features

  • Optional configuration of the HTTP header on each middleware instance configuration
  • Configurable header with no default value
  • Uses the first valid IP found in the specified header
  • Fallback to the standard remote IP if an error occurs

Implementation details

The rules object that hosts the new parameter was not passed to the chain when handling requests. I have chosen to add a parameter to the chain constructor to be able to pass the source IP header that is then used in the data package to get the real IP.
This adds a coupling between the rules and the chain but I thought that the tradeoff was acceptable. It ensures a decoupling with the multiple handlers.

Testing

I have updated the tests to take into account the new argument passed to several functions.

Documentation

I have updated the README to explain the new feature and how it can be used.

Checklist

  • Implementation follows project structure and coding standards
  • All new code is covered by tests
  • Documentation has been updated
  • No breaking changes introduced
  • Code has been reviewed for security considerations

Improvements

  • Add a depth parameter to be able to chose the depth at which an IP should be pulled from an HTTP header that contains multiple IPs in case of multiple reverse proxies, just like Traefik does.

Related issues

@tomMoulard tomMoulard self-requested a review January 16, 2025 17:16
pkg/data/data.go Outdated
const contextDataKey key = iota

// GetRemoteIP extracts the remote IP from the request, optionally using a custom header.
func GetRemoteIP(r *http.Request, sourceIPHeader string) (string, error) {
Copy link
Owner

Choose a reason for hiding this comment

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

For this I had invision to have a configuration more like Traefik's source criterion which allows choosing which IP in the header is considered.
This will not ban your firewall, for example !

WDYT ?

Copy link
Author

Choose a reason for hiding this comment

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

Yes, I agree. I listed this as an improvement but I guess I could try to implement it.

Copy link
Author

Choose a reason for hiding this comment

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

@tomMoulard I just pushed a proposal that more closely matches the source criterion from Traefik. Let me know what you think!

pkg/data/data.go Outdated Show resolved Hide resolved
}

// New creates a new chain.
func New(final http.Handler, handlers ...ChainHandler) Chain {
func New(final http.Handler, sourceIPHeader string, handlers ...ChainHandler) Chain {
Copy link
Owner

Choose a reason for hiding this comment

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

For this, I think it would be better to have a ip extractor interface where:

  • if there is a header configured, the ip extractor will extract the IP from the headers
  • otherwise, the request's ip will be used.

WDYT ?

Copy link
Author

Choose a reason for hiding this comment

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

Where would you see this interface going? By interface there, you mean an actual code interface that would be implemented by only one class and used once or an "interface" as a decoupled way of getting the IP? For the latter, would you put this outside the chain handler?

I'm open to streamline this more but I guess I need a bit more context on what you think for the IP extractor.

pkg/rules/rules.go Outdated Show resolved Hide resolved
@tomMoulard tomMoulard self-requested a review January 28, 2025 10:15
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.

Banning based on forwarded IP
2 participants