如何设置 X-Forwarded-For 和 httputil.ReverseProxy

How to set X-Forwarded-For with httputil.ReverseProxy

我为什么要这样做

这个问题我已经 运行 两次了。

第一次是使用位于仅 IPv6 服务器网络内的反向代理。客户端请求通过 NAT46 传入。请求的source-IP变为[固定96位前缀]+[32位客户端IPv4地址]。这意味着反向代理始终可以识别真实的客户端 IP。不过,我找不到将 X-Forwarded-For header 设置为该地址的方法。我通过修改后端服务器解决了这个问题。

这次我有一个反向代理,它将 运行 在 Google App Engine 上。请求首先到达 Google 的负载均衡器,它添加 X-Forwarded-For header 并将请求转发到我的应用程序。我想稍微修改一下请求,然后将其传递给后端服务器,我无法修改。 back-end 需要原始客户端 IP,并且可以通过 X-Forwarded-For 接受它(它已通过身份验证,不用担心)。在这种情况下,我想通过未修改的 Google 负载均衡器传递 X-Forwarded-For header。

问题

似乎无法将 X-Forwarded-For 设置为我在使用 httputil.ReverseProxy 时选择的值。如果我设置它(下面的选项 1),将附加来自 TCP 连接的客户端地址。如果我将它设置为 nil(下面的选项 2),它会像文档中建议的那样被省略,但这也不是我想要的。

package main

import (
    "log"
    "net/http"
    "net/http/httputil"
)

func director(req *http.Request) {
    // currently running a PHP script that just records headers
    host := "bsweb02.bentonvillek12.org"

    // who we dial
    req.URL.Scheme = "https"
    req.URL.Host = host

    // host header
    req.Host = host

    // proof that most headers can be passed through fine
    req.Header["Foo"] = []string{"Bar"}

    req.Header["X-Forwarded-For"] = []string{"1.2.3.4"} // option 1
    //req.Header["X-Forwarded-For"] = nil               // option 2
}

func main() {
    http.ListenAndServe(":80", &httputil.ReverseProxy{
        Director: director,
    })
}

选项 1

array(9) {
  ["Content-Type"]=>
  string(0) ""
  ["Content-Length"]=>
  string(1) "0"
  ["Foo"]=>
  string(3) "Bar"
  ["X-Forwarded-For"]=>
  string(18) "1.2.3.4, 127.0.0.1"
  ["User-Agent"]=>
  string(11) "curl/7.81.0"
  ["Host"]=>
  string(26) "bsweb02.bentonvillek12.org"
  ["Accept-Encoding"]=>
  string(4) "gzip"
  ["Accept"]=>
  string(3) "*/*"
  ["Connection"]=>
  string(5) "close"
}

选项 2

array(8) {
  ["Content-Type"]=>
  string(0) ""
  ["Content-Length"]=>
  string(1) "0"
  ["Foo"]=>
  string(3) "Bar"
  ["User-Agent"]=>
  string(11) "curl/7.81.0"
  ["Host"]=>
  string(26) "bsweb02.bentonvillek12.org"
  ["Accept-Encoding"]=>
  string(4) "gzip"
  ["Accept"]=>
  string(3) "*/*"
  ["Connection"]=>
  string(5) "close"
}

我相信你有两个选择。

1。实施 http.RoundTripper

您可以在其中实现自己的 RoundTripper 和 re-set X-Forwarded-For。 (demonstration)

type MyRoundTripper struct{}

func (t *MyRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) {
    req.Header["X-Forwarded-For"] = []string{"1.2.3.4"}
    return http.DefaultTransport.RoundTrip(req)
}

func main() {
    http.ListenAndServe(":80", &httputil.ReverseProxy{
        Director: director,
        Transport: &MyRoundTripper{},
    })
}

Transport 字段未在 httputil.ReverseProxy 上设置时,它会回退到 http.DefaultTransport,因此您也可以在自定义代码后回退到它。

2。取消设置 req.RemoteAddr

您在调用反向代理之前重置了原始请求的 RemoteAddr 字段。此字段由 HTTP 服务器设置,如果存在,将触发反向代理实现中的 X-Forwarded-For 替换。 (demonstration)

func main() {
    http.ListenAndServe(":80", func(w http.ResponseWriter, r *http.Request) {
        r.RemoteAddr = ""
        proxy := &httputil.ReverseProxy{ Director: director } 
        proxy.ServeHTTP(w, r)    
    })
}

但是,此行为依赖于实现细节,将来可能会或可能不会更改。我建议改为使用选项 1。