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

ClientIP middleware proposal, intended to replace RealIP #967

Open
wants to merge 1 commit into
base: master
Choose a base branch
from

Conversation

VojtechVitek
Copy link
Contributor

@VojtechVitek VojtechVitek commented Dec 15, 2024

A proposed solution for the RealIP middleware issues outlined at:
#708
https://adam-p.ca/blog/2022/03/x-forwarded-for/
#711
#453
#908

Summary of differences:

  • Unlike RealIP, ClientIP middleware doesn't change the value of req.RemoteAddr
  • Instead, it parses and saves the client IP to a context and provides .GetClientIP() getter
  • Makes users choose what to trust explicitly, e.g. a custom header / XFF header / req.RemoteAddr
  • XFF algorithm now selects the rightmost trusted IP address from all X-Forwarded-For headers
  • Additionally, XFF accepts a list of trusted IP prefixes, so users can opt-in to trust IP range
    belonging to their infrastructure, such as proxies or Load Balances, depending on their setup
  • Should prevent client IP spoofing, which could affect rate-limiting in https://github.com/go-chi/httprate

Kudos to Adam Pritchard, Jonathan Yu, Yuya Okumura, Liam Stanley, and everyone else involved in the detailed reports and discussion.

I'm seeking early feedback and reviews on the proposed API.

CC @jawnsy @adam-p @convto @Dirbaio @pkieltyka @mfridman @lrstanley @n33pm

P.S.: This PR uses the "net/netip" package and assumes that we'll accept proposal to drop support for Go 1.17 and older. Please have a look!


Example usage:

// Trust Cloudflare header:
r.Use(middleware.ClientIPFromHeader("CF-Connecting-IP"))
r.Use(middleware.ClientIPFromHeader("True-Client-IP")) // alternative in Cloudflare Enterprise
// Trust Azure header:
r.Use(middleware.ClientIPFromHeader("X-Azure-ClientIP"))
// Trust header from Nginx with ngx_http_realip_module:
r.Use(middleware.ClientIPFromHeader("X-Real-IP"))
// Trust header from Apache with mod_remoteip:
r.Use(middleware.ClientIPFromHeader("X-Client-IP"))
// Trust X-Forwarded-For header set by reverse-proxy in a private network:
r.Use(middleware.ClientIPFromXFFHeader("203.0.113.0/24"))
// Trust X-Forwarded-For header set by AWS CloudFront:
r.Use(middleware.ClientIPFromXFFHeader(
    "13.32.0.0/15",   // CloudFront IPv4
    "52.46.0.0/18",   // CloudFront IPv4
    "2600:9000::/28", // CloudFront IPv6
))
// Go server serving the Internet traffic directly:
r.Use(middleware.ClientIPFromRemoteAddr)

Get the client IP address value in HTTP handler:

clientIP := middleware.GetClientIP(r.Context())
// log the clientIP .. or use it for rate-limiting

Copy link

@convto convto left a comment

Choose a reason for hiding this comment

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

I’ve checked the implementation, and I completely agree with the approach.
It addresses the concerns beautifully, and I’m very grateful for the effort put into this!

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