Permalink
Browse files

Refactor RealRemoteIP implementation

  • Loading branch information...
dsamarin committed May 17, 2018
1 parent 83a42a9 commit 5b304f7f527e94c747cff0e62a4fa798c246c701
Showing with 69 additions and 29 deletions.
  1. +37 −29 util.go
  2. +32 −0 util_test.go
66 util.go
@@ -45,43 +45,51 @@ func getCloudflareSet() *ipcat.IntervalSet {

var cloudflareSet = getCloudflareSet()

// RealRemoteIP returns the value of the X-Real-IP header,
// or the RemoteAddr property if the header does not exist.
func RealRemoteIP(r *http.Request) net.IP {
var ip net.IP

// When local, RemoteAddr is empty.
func peelRemoteIP(r *http.Request) net.IP {
log.Printf("peelRemoteIP %s", r.RemoteAddr)
host, _, err := net.SplitHostPort(r.RemoteAddr)
if err == nil {
ip = net.ParseIP(host)
if ip == nil {
return nil
}
if err != nil {
// We could not parse the host and port.
// Assume this means local.
return net.IPv6loopback
}
return net.ParseIP(host)
}

if cloudflareSet != nil {
record, err := cloudflareSet.Contains(ip.String())
if err == nil && record != nil {
// We are being served by Cloudflare
connectingIP := r.Header.Get("CF-Connecting-IP")
log.Printf("Cloudflare IP %s detected: forwarding %s", ip, connectingIP)
if connectingIP != "" {
return net.ParseIP(connectingIP)
func peelLocalProxy(ip net.IP, r *http.Request) net.IP {
log.Printf("peelLocalProxy %s", ip)
if ip != nil {
// Local proxies only!
if ip.IsLoopback() {
if real := r.Header.Get("X-Real-IP"); real != "" {
headerip := net.ParseIP(real)
if headerip != nil {
return headerip
}
}
}

if !ip.IsLoopback() {
return ip
}
}
return ip
}

// Now we are guaranteed that we are being accessed by local.
if real := r.Header.Get("X-Real-IP"); real != "" {
headerip := net.ParseIP(real)
if headerip != nil {
return headerip
func peelCloudflare(ip net.IP, r *http.Request) net.IP {
log.Printf("peelCloudflare %s", ip)
if cloudflareSet != nil {
// Cloudflare proxy only!
record, err := cloudflareSet.Contains(ip.String())
if err == nil && record != nil {
// We are being served by Cloudflare
connectingIP := r.Header.Get("CF-Connecting-IP")
if connectingIP != "" {
return net.ParseIP(connectingIP)
}
}
}

return ip
}

// RealRemoteIP returns the value of the X-Real-IP header,
// or the RemoteAddr property if the header does not exist.
func RealRemoteIP(r *http.Request) net.IP {
return peelCloudflare(peelLocalProxy(peelRemoteIP(r), r), r)
}
@@ -0,0 +1,32 @@
package main

import (
"net"
"net/http"
"testing"
)

func TestRemoteAddr(t *testing.T) {
tests := []struct {
RemoteAddr string
XRealIP string
ConnectingIP string
Result string
}{{"162.158.246.25:83789", "", "8.8.8.8", "8.8.8.8"}}

for _, test := range tests {
header := make(http.Header)
header.Add("CF-Connecting-IP", test.ConnectingIP)
header.Add("X-Real-IP", test.XRealIP)

actual := RealRemoteIP(&http.Request{
RemoteAddr: test.RemoteAddr,
Header: header,
})

expected := net.ParseIP(test.Result)
if !expected.Equal(actual) {
t.Errorf("For %#v got %s, expected %s", test, actual, expected)
}
}
}

0 comments on commit 5b304f7

Please sign in to comment.