mirror of
https://gitee.com/IrisVega/frp.git
synced 2024-11-01 22:31:29 +08:00
fix x-forwarded-for header (#4111)
This commit is contained in:
parent
86f90f4d27
commit
590ccda677
@ -1 +1,7 @@
|
|||||||
|
### Features
|
||||||
|
|
||||||
|
* `https2http` and `https2https` plugin now supports `X-Forwared-For` header.
|
||||||
|
|
||||||
|
### Fixes
|
||||||
|
|
||||||
|
* `X-Forwared-For` header is now correctly set in the request to the backend server for proxy type http.
|
||||||
|
@ -158,33 +158,35 @@ func (pxy *BaseProxy) HandleTCPWorkConnection(workConn net.Conn, m *msg.StartWor
|
|||||||
|
|
||||||
// check if we need to send proxy protocol info
|
// check if we need to send proxy protocol info
|
||||||
var extraInfo plugin.ExtraInfo
|
var extraInfo plugin.ExtraInfo
|
||||||
if baseCfg.Transport.ProxyProtocolVersion != "" {
|
if m.SrcAddr != "" && m.SrcPort != 0 {
|
||||||
if m.SrcAddr != "" && m.SrcPort != 0 {
|
if m.DstAddr == "" {
|
||||||
if m.DstAddr == "" {
|
m.DstAddr = "127.0.0.1"
|
||||||
m.DstAddr = "127.0.0.1"
|
|
||||||
}
|
|
||||||
srcAddr, _ := net.ResolveTCPAddr("tcp", net.JoinHostPort(m.SrcAddr, strconv.Itoa(int(m.SrcPort))))
|
|
||||||
dstAddr, _ := net.ResolveTCPAddr("tcp", net.JoinHostPort(m.DstAddr, strconv.Itoa(int(m.DstPort))))
|
|
||||||
h := &pp.Header{
|
|
||||||
Command: pp.PROXY,
|
|
||||||
SourceAddr: srcAddr,
|
|
||||||
DestinationAddr: dstAddr,
|
|
||||||
}
|
|
||||||
|
|
||||||
if strings.Contains(m.SrcAddr, ".") {
|
|
||||||
h.TransportProtocol = pp.TCPv4
|
|
||||||
} else {
|
|
||||||
h.TransportProtocol = pp.TCPv6
|
|
||||||
}
|
|
||||||
|
|
||||||
if baseCfg.Transport.ProxyProtocolVersion == "v1" {
|
|
||||||
h.Version = 1
|
|
||||||
} else if baseCfg.Transport.ProxyProtocolVersion == "v2" {
|
|
||||||
h.Version = 2
|
|
||||||
}
|
|
||||||
|
|
||||||
extraInfo.ProxyProtocolHeader = h
|
|
||||||
}
|
}
|
||||||
|
srcAddr, _ := net.ResolveTCPAddr("tcp", net.JoinHostPort(m.SrcAddr, strconv.Itoa(int(m.SrcPort))))
|
||||||
|
dstAddr, _ := net.ResolveTCPAddr("tcp", net.JoinHostPort(m.DstAddr, strconv.Itoa(int(m.DstPort))))
|
||||||
|
extraInfo.SrcAddr = srcAddr
|
||||||
|
extraInfo.DstAddr = dstAddr
|
||||||
|
}
|
||||||
|
|
||||||
|
if baseCfg.Transport.ProxyProtocolVersion != "" && m.SrcAddr != "" && m.SrcPort != 0 {
|
||||||
|
h := &pp.Header{
|
||||||
|
Command: pp.PROXY,
|
||||||
|
SourceAddr: extraInfo.SrcAddr,
|
||||||
|
DestinationAddr: extraInfo.DstAddr,
|
||||||
|
}
|
||||||
|
|
||||||
|
if strings.Contains(m.SrcAddr, ".") {
|
||||||
|
h.TransportProtocol = pp.TCPv4
|
||||||
|
} else {
|
||||||
|
h.TransportProtocol = pp.TCPv6
|
||||||
|
}
|
||||||
|
|
||||||
|
if baseCfg.Transport.ProxyProtocolVersion == "v1" {
|
||||||
|
h.Version = 1
|
||||||
|
} else if baseCfg.Transport.ProxyProtocolVersion == "v2" {
|
||||||
|
h.Version = 2
|
||||||
|
}
|
||||||
|
extraInfo.ProxyProtocolHeader = h
|
||||||
}
|
}
|
||||||
|
|
||||||
if pxy.proxyPlugin != nil {
|
if pxy.proxyPlugin != nil {
|
||||||
|
@ -54,6 +54,9 @@ func NewHTTP2HTTPSPlugin(options v1.ClientPluginOptions) (Plugin, error) {
|
|||||||
|
|
||||||
rp := &httputil.ReverseProxy{
|
rp := &httputil.ReverseProxy{
|
||||||
Rewrite: func(r *httputil.ProxyRequest) {
|
Rewrite: func(r *httputil.ProxyRequest) {
|
||||||
|
r.Out.Header["X-Forwarded-For"] = r.In.Header["X-Forwarded-For"]
|
||||||
|
r.Out.Header["X-Forwarded-Host"] = r.In.Header["X-Forwarded-Host"]
|
||||||
|
r.Out.Header["X-Forwarded-Proto"] = r.In.Header["X-Forwarded-Proto"]
|
||||||
req := r.Out
|
req := r.Out
|
||||||
req.URL.Scheme = "https"
|
req.URL.Scheme = "https"
|
||||||
req.URL.Host = p.opts.LocalAddr
|
req.URL.Host = p.opts.LocalAddr
|
||||||
|
@ -51,6 +51,8 @@ func NewHTTPS2HTTPPlugin(options v1.ClientPluginOptions) (Plugin, error) {
|
|||||||
|
|
||||||
rp := &httputil.ReverseProxy{
|
rp := &httputil.ReverseProxy{
|
||||||
Rewrite: func(r *httputil.ProxyRequest) {
|
Rewrite: func(r *httputil.ProxyRequest) {
|
||||||
|
r.Out.Header["X-Forwarded-For"] = r.In.Header["X-Forwarded-For"]
|
||||||
|
r.SetXForwarded()
|
||||||
req := r.Out
|
req := r.Out
|
||||||
req.URL.Scheme = "http"
|
req.URL.Scheme = "http"
|
||||||
req.URL.Host = p.opts.LocalAddr
|
req.URL.Host = p.opts.LocalAddr
|
||||||
@ -98,8 +100,11 @@ func (p *HTTPS2HTTPPlugin) genTLSConfig() (*tls.Config, error) {
|
|||||||
return config, nil
|
return config, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *HTTPS2HTTPPlugin) Handle(conn io.ReadWriteCloser, realConn net.Conn, _ *ExtraInfo) {
|
func (p *HTTPS2HTTPPlugin) Handle(conn io.ReadWriteCloser, realConn net.Conn, extra *ExtraInfo) {
|
||||||
wrapConn := netpkg.WrapReadWriteCloserToConn(conn, realConn)
|
wrapConn := netpkg.WrapReadWriteCloserToConn(conn, realConn)
|
||||||
|
if extra.SrcAddr != nil {
|
||||||
|
wrapConn.SetRemoteAddr(extra.SrcAddr)
|
||||||
|
}
|
||||||
_ = p.l.PutConn(wrapConn)
|
_ = p.l.PutConn(wrapConn)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -56,6 +56,8 @@ func NewHTTPS2HTTPSPlugin(options v1.ClientPluginOptions) (Plugin, error) {
|
|||||||
|
|
||||||
rp := &httputil.ReverseProxy{
|
rp := &httputil.ReverseProxy{
|
||||||
Rewrite: func(r *httputil.ProxyRequest) {
|
Rewrite: func(r *httputil.ProxyRequest) {
|
||||||
|
r.Out.Header["X-Forwarded-For"] = r.In.Header["X-Forwarded-For"]
|
||||||
|
r.SetXForwarded()
|
||||||
req := r.Out
|
req := r.Out
|
||||||
req.URL.Scheme = "https"
|
req.URL.Scheme = "https"
|
||||||
req.URL.Host = p.opts.LocalAddr
|
req.URL.Host = p.opts.LocalAddr
|
||||||
@ -104,8 +106,11 @@ func (p *HTTPS2HTTPSPlugin) genTLSConfig() (*tls.Config, error) {
|
|||||||
return config, nil
|
return config, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *HTTPS2HTTPSPlugin) Handle(conn io.ReadWriteCloser, realConn net.Conn, _ *ExtraInfo) {
|
func (p *HTTPS2HTTPSPlugin) Handle(conn io.ReadWriteCloser, realConn net.Conn, extra *ExtraInfo) {
|
||||||
wrapConn := netpkg.WrapReadWriteCloserToConn(conn, realConn)
|
wrapConn := netpkg.WrapReadWriteCloserToConn(conn, realConn)
|
||||||
|
if extra.SrcAddr != nil {
|
||||||
|
wrapConn.SetRemoteAddr(extra.SrcAddr)
|
||||||
|
}
|
||||||
_ = p.l.PutConn(wrapConn)
|
_ = p.l.PutConn(wrapConn)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -50,6 +50,8 @@ func Create(name string, options v1.ClientPluginOptions) (p Plugin, err error) {
|
|||||||
|
|
||||||
type ExtraInfo struct {
|
type ExtraInfo struct {
|
||||||
ProxyProtocolHeader *pp.Header
|
ProxyProtocolHeader *pp.Header
|
||||||
|
SrcAddr net.Addr
|
||||||
|
DstAddr net.Addr
|
||||||
}
|
}
|
||||||
|
|
||||||
type Plugin interface {
|
type Plugin interface {
|
||||||
|
@ -76,9 +76,11 @@ type WrapReadWriteCloserConn struct {
|
|||||||
io.ReadWriteCloser
|
io.ReadWriteCloser
|
||||||
|
|
||||||
underConn net.Conn
|
underConn net.Conn
|
||||||
|
|
||||||
|
remoteAddr net.Addr
|
||||||
}
|
}
|
||||||
|
|
||||||
func WrapReadWriteCloserToConn(rwc io.ReadWriteCloser, underConn net.Conn) net.Conn {
|
func WrapReadWriteCloserToConn(rwc io.ReadWriteCloser, underConn net.Conn) *WrapReadWriteCloserConn {
|
||||||
return &WrapReadWriteCloserConn{
|
return &WrapReadWriteCloserConn{
|
||||||
ReadWriteCloser: rwc,
|
ReadWriteCloser: rwc,
|
||||||
underConn: underConn,
|
underConn: underConn,
|
||||||
@ -92,7 +94,14 @@ func (conn *WrapReadWriteCloserConn) LocalAddr() net.Addr {
|
|||||||
return (*net.TCPAddr)(nil)
|
return (*net.TCPAddr)(nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (conn *WrapReadWriteCloserConn) SetRemoteAddr(addr net.Addr) {
|
||||||
|
conn.remoteAddr = addr
|
||||||
|
}
|
||||||
|
|
||||||
func (conn *WrapReadWriteCloserConn) RemoteAddr() net.Addr {
|
func (conn *WrapReadWriteCloserConn) RemoteAddr() net.Addr {
|
||||||
|
if conn.remoteAddr != nil {
|
||||||
|
return conn.remoteAddr
|
||||||
|
}
|
||||||
if conn.underConn != nil {
|
if conn.underConn != nil {
|
||||||
return conn.underConn.RemoteAddr()
|
return conn.underConn.RemoteAddr()
|
||||||
}
|
}
|
||||||
|
@ -59,6 +59,7 @@ func NewHTTPReverseProxy(option HTTPReverseProxyOptions, vhostRouter *Routers) *
|
|||||||
proxy := &httputil.ReverseProxy{
|
proxy := &httputil.ReverseProxy{
|
||||||
// Modify incoming requests by route policies.
|
// Modify incoming requests by route policies.
|
||||||
Rewrite: func(r *httputil.ProxyRequest) {
|
Rewrite: func(r *httputil.ProxyRequest) {
|
||||||
|
r.Out.Header["X-Forwarded-For"] = r.In.Header["X-Forwarded-For"]
|
||||||
r.SetXForwarded()
|
r.SetXForwarded()
|
||||||
req := r.Out
|
req := r.Out
|
||||||
req.URL.Scheme = "http"
|
req.URL.Scheme = "http"
|
||||||
|
@ -113,7 +113,7 @@ func (e *RequestExpect) Ensure(fns ...EnsureFunc) {
|
|||||||
if !bytes.Equal(e.expectResp, ret.Content) {
|
if !bytes.Equal(e.expectResp, ret.Content) {
|
||||||
flog.Tracef("Response info: %+v", ret)
|
flog.Tracef("Response info: %+v", ret)
|
||||||
}
|
}
|
||||||
ExpectEqualValuesWithOffset(1, ret.Content, e.expectResp, e.explain...)
|
ExpectEqualValuesWithOffset(1, string(ret.Content), string(e.expectResp), e.explain...)
|
||||||
} else {
|
} else {
|
||||||
for _, fn := range fns {
|
for _, fn := range fns {
|
||||||
ok := fn(ret)
|
ok := fn(ret)
|
||||||
|
@ -2,6 +2,7 @@ package features
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bufio"
|
"bufio"
|
||||||
|
"crypto/tls"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
@ -9,6 +10,7 @@ import (
|
|||||||
"github.com/onsi/ginkgo/v2"
|
"github.com/onsi/ginkgo/v2"
|
||||||
pp "github.com/pires/go-proxyproto"
|
pp "github.com/pires/go-proxyproto"
|
||||||
|
|
||||||
|
"github.com/fatedier/frp/pkg/transport"
|
||||||
"github.com/fatedier/frp/pkg/util/log"
|
"github.com/fatedier/frp/pkg/util/log"
|
||||||
"github.com/fatedier/frp/test/e2e/framework"
|
"github.com/fatedier/frp/test/e2e/framework"
|
||||||
"github.com/fatedier/frp/test/e2e/framework/consts"
|
"github.com/fatedier/frp/test/e2e/framework/consts"
|
||||||
@ -21,23 +23,24 @@ import (
|
|||||||
var _ = ginkgo.Describe("[Feature: Real IP]", func() {
|
var _ = ginkgo.Describe("[Feature: Real IP]", func() {
|
||||||
f := framework.NewDefaultFramework()
|
f := framework.NewDefaultFramework()
|
||||||
|
|
||||||
ginkgo.It("HTTP X-Forwarded-For", func() {
|
ginkgo.Describe("HTTP X-forwarded-For", func() {
|
||||||
vhostHTTPPort := f.AllocPort()
|
ginkgo.It("Client Without Header", func() {
|
||||||
serverConf := consts.DefaultServerConfig + fmt.Sprintf(`
|
vhostHTTPPort := f.AllocPort()
|
||||||
|
serverConf := consts.DefaultServerConfig + fmt.Sprintf(`
|
||||||
vhostHTTPPort = %d
|
vhostHTTPPort = %d
|
||||||
`, vhostHTTPPort)
|
`, vhostHTTPPort)
|
||||||
|
|
||||||
localPort := f.AllocPort()
|
localPort := f.AllocPort()
|
||||||
localServer := httpserver.New(
|
localServer := httpserver.New(
|
||||||
httpserver.WithBindPort(localPort),
|
httpserver.WithBindPort(localPort),
|
||||||
httpserver.WithHandler(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
|
httpserver.WithHandler(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
|
||||||
_, _ = w.Write([]byte(req.Header.Get("X-Forwarded-For")))
|
_, _ = w.Write([]byte(req.Header.Get("X-Forwarded-For")))
|
||||||
})),
|
})),
|
||||||
)
|
)
|
||||||
f.RunServer("", localServer)
|
f.RunServer("", localServer)
|
||||||
|
|
||||||
clientConf := consts.DefaultClientConfig
|
clientConf := consts.DefaultClientConfig
|
||||||
clientConf += fmt.Sprintf(`
|
clientConf += fmt.Sprintf(`
|
||||||
[[proxies]]
|
[[proxies]]
|
||||||
name = "test"
|
name = "test"
|
||||||
type = "http"
|
type = "http"
|
||||||
@ -45,14 +48,131 @@ var _ = ginkgo.Describe("[Feature: Real IP]", func() {
|
|||||||
customDomains = ["normal.example.com"]
|
customDomains = ["normal.example.com"]
|
||||||
`, localPort)
|
`, localPort)
|
||||||
|
|
||||||
f.RunProcesses([]string{serverConf}, []string{clientConf})
|
f.RunProcesses([]string{serverConf}, []string{clientConf})
|
||||||
|
|
||||||
framework.NewRequestExpect(f).Port(vhostHTTPPort).
|
framework.NewRequestExpect(f).Port(vhostHTTPPort).
|
||||||
RequestModify(func(r *request.Request) {
|
RequestModify(func(r *request.Request) {
|
||||||
r.HTTP().HTTPHost("normal.example.com")
|
r.HTTP().HTTPHost("normal.example.com")
|
||||||
}).
|
}).
|
||||||
ExpectResp([]byte("127.0.0.1")).
|
ExpectResp([]byte("127.0.0.1")).
|
||||||
Ensure()
|
Ensure()
|
||||||
|
})
|
||||||
|
|
||||||
|
ginkgo.It("Client With Header", func() {
|
||||||
|
vhostHTTPPort := f.AllocPort()
|
||||||
|
serverConf := consts.DefaultServerConfig + fmt.Sprintf(`
|
||||||
|
vhostHTTPPort = %d
|
||||||
|
`, vhostHTTPPort)
|
||||||
|
|
||||||
|
localPort := f.AllocPort()
|
||||||
|
localServer := httpserver.New(
|
||||||
|
httpserver.WithBindPort(localPort),
|
||||||
|
httpserver.WithHandler(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
|
||||||
|
_, _ = w.Write([]byte(req.Header.Get("X-Forwarded-For")))
|
||||||
|
})),
|
||||||
|
)
|
||||||
|
f.RunServer("", localServer)
|
||||||
|
|
||||||
|
clientConf := consts.DefaultClientConfig
|
||||||
|
clientConf += fmt.Sprintf(`
|
||||||
|
[[proxies]]
|
||||||
|
name = "test"
|
||||||
|
type = "http"
|
||||||
|
localPort = %d
|
||||||
|
customDomains = ["normal.example.com"]
|
||||||
|
`, localPort)
|
||||||
|
|
||||||
|
f.RunProcesses([]string{serverConf}, []string{clientConf})
|
||||||
|
|
||||||
|
framework.NewRequestExpect(f).Port(vhostHTTPPort).
|
||||||
|
RequestModify(func(r *request.Request) {
|
||||||
|
r.HTTP().HTTPHost("normal.example.com")
|
||||||
|
r.HTTP().HTTPHeaders(map[string]string{"x-forwarded-for": "2.2.2.2"})
|
||||||
|
}).
|
||||||
|
ExpectResp([]byte("2.2.2.2, 127.0.0.1")).
|
||||||
|
Ensure()
|
||||||
|
})
|
||||||
|
|
||||||
|
ginkgo.It("http2https plugin", func() {
|
||||||
|
vhostHTTPPort := f.AllocPort()
|
||||||
|
serverConf := consts.DefaultServerConfig + fmt.Sprintf(`
|
||||||
|
vhostHTTPPort = %d
|
||||||
|
`, vhostHTTPPort)
|
||||||
|
|
||||||
|
localPort := f.AllocPort()
|
||||||
|
|
||||||
|
clientConf := consts.DefaultClientConfig
|
||||||
|
clientConf += fmt.Sprintf(`
|
||||||
|
[[proxies]]
|
||||||
|
name = "test"
|
||||||
|
type = "http"
|
||||||
|
customDomains = ["normal.example.com"]
|
||||||
|
[proxies.plugin]
|
||||||
|
type = "http2https"
|
||||||
|
localAddr = "127.0.0.1:%d"
|
||||||
|
`, localPort)
|
||||||
|
|
||||||
|
f.RunProcesses([]string{serverConf}, []string{clientConf})
|
||||||
|
|
||||||
|
tlsConfig, err := transport.NewServerTLSConfig("", "", "")
|
||||||
|
framework.ExpectNoError(err)
|
||||||
|
|
||||||
|
localServer := httpserver.New(
|
||||||
|
httpserver.WithBindPort(localPort),
|
||||||
|
httpserver.WithHandler(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
|
||||||
|
_, _ = w.Write([]byte(req.Header.Get("X-Forwarded-For")))
|
||||||
|
})),
|
||||||
|
httpserver.WithTLSConfig(tlsConfig),
|
||||||
|
)
|
||||||
|
f.RunServer("", localServer)
|
||||||
|
|
||||||
|
framework.NewRequestExpect(f).Port(vhostHTTPPort).
|
||||||
|
RequestModify(func(r *request.Request) {
|
||||||
|
r.HTTP().HTTPHost("normal.example.com")
|
||||||
|
r.HTTP().HTTPHeaders(map[string]string{"x-forwarded-for": "2.2.2.2, 3.3.3.3"})
|
||||||
|
}).
|
||||||
|
ExpectResp([]byte("2.2.2.2, 3.3.3.3, 127.0.0.1")).
|
||||||
|
Ensure()
|
||||||
|
})
|
||||||
|
|
||||||
|
ginkgo.It("https2http plugin", func() {
|
||||||
|
vhostHTTPSPort := f.AllocPort()
|
||||||
|
serverConf := consts.DefaultServerConfig + fmt.Sprintf(`
|
||||||
|
vhostHTTPSPort = %d
|
||||||
|
`, vhostHTTPSPort)
|
||||||
|
|
||||||
|
localPort := f.AllocPort()
|
||||||
|
|
||||||
|
clientConf := consts.DefaultClientConfig
|
||||||
|
clientConf += fmt.Sprintf(`
|
||||||
|
[[proxies]]
|
||||||
|
name = "test"
|
||||||
|
type = "https"
|
||||||
|
customDomains = ["normal.example.com"]
|
||||||
|
[proxies.plugin]
|
||||||
|
type = "https2http"
|
||||||
|
localAddr = "127.0.0.1:%d"
|
||||||
|
`, localPort)
|
||||||
|
|
||||||
|
f.RunProcesses([]string{serverConf}, []string{clientConf})
|
||||||
|
|
||||||
|
localServer := httpserver.New(
|
||||||
|
httpserver.WithBindPort(localPort),
|
||||||
|
httpserver.WithHandler(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
|
||||||
|
_, _ = w.Write([]byte(req.Header.Get("X-Forwarded-For")))
|
||||||
|
})),
|
||||||
|
)
|
||||||
|
f.RunServer("", localServer)
|
||||||
|
|
||||||
|
framework.NewRequestExpect(f).Port(vhostHTTPSPort).
|
||||||
|
RequestModify(func(r *request.Request) {
|
||||||
|
r.HTTPS().HTTPHost("normal.example.com").
|
||||||
|
HTTPHeaders(map[string]string{"x-forwarded-for": "2.2.2.2"}).
|
||||||
|
TLSConfig(&tls.Config{ServerName: "normal.example.com", InsecureSkipVerify: true})
|
||||||
|
}).
|
||||||
|
ExpectResp([]byte("2.2.2.2, 127.0.0.1")).
|
||||||
|
Ensure()
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
ginkgo.Describe("Proxy Protocol", func() {
|
ginkgo.Describe("Proxy Protocol", func() {
|
||||||
|
Loading…
Reference in New Issue
Block a user