mirror of
https://github.com/oauth2-proxy/oauth2-proxy.git
synced 2026-10-04 05:31:23 +02:00
chore(deps): bump Go to 1.26 and migrate upstream reverse proxies to Rewrite
Signed-off-by: Jan Larwig <jan@larwig.com>
This commit is contained in:
+57
-18
@@ -123,7 +123,7 @@ func (t *unixRoundTripper) RoundTrip(req *http.Request) (*http.Response, error)
|
||||
// The proxy should render an error page if there are failures connecting to the
|
||||
// upstream server.
|
||||
func newReverseProxy(target *url.URL, upstream options.Upstream, errorHandler ProxyErrorHandler) http.Handler {
|
||||
proxy := httputil.NewSingleHostReverseProxy(target)
|
||||
proxy := newSingleHostReverseProxy(target)
|
||||
|
||||
// Inherit default transport options from Go's stdlib
|
||||
transport := http.DefaultTransport.(*http.Transport).Clone()
|
||||
@@ -155,7 +155,7 @@ func newReverseProxy(target *url.URL, upstream options.Upstream, errorHandler Pr
|
||||
}
|
||||
|
||||
// Ensure we always pass the original request path
|
||||
setProxyDirector(proxy)
|
||||
setProxyRewrite(proxy)
|
||||
|
||||
// TODO (@tuunit) - this should be inverted or get a better name in the future to set the upstream host header
|
||||
// only if PassHostHeader is explicitly set to true. Currently this would be a breaking change.
|
||||
@@ -179,32 +179,71 @@ func newReverseProxy(target *url.URL, upstream options.Upstream, errorHandler Pr
|
||||
return proxy
|
||||
}
|
||||
|
||||
// setProxyUpstreamHostHeader sets the proxy.Director so that upstream requests
|
||||
// receive a host header matching the target URL.
|
||||
func setProxyUpstreamHostHeader(proxy *httputil.ReverseProxy, target *url.URL) {
|
||||
director := proxy.Director
|
||||
proxy.Director = func(req *http.Request) {
|
||||
director(req)
|
||||
req.Host = target.Host
|
||||
func newSingleHostReverseProxy(target *url.URL) *httputil.ReverseProxy {
|
||||
return &httputil.ReverseProxy{
|
||||
Rewrite: func(proxyReq *httputil.ProxyRequest) {
|
||||
proxyReq.SetURL(target)
|
||||
proxyReq.Out.Host = proxyReq.In.Host
|
||||
setProxyForwardingHeaders(proxyReq)
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// setProxyDirector sets the proxy.Director so that request URIs are escaped
|
||||
// setProxyUpstreamHostHeader sets the proxy.Rewrite so that upstream requests
|
||||
// receive a host header matching the target URL.
|
||||
func setProxyUpstreamHostHeader(proxy *httputil.ReverseProxy, target *url.URL) {
|
||||
rewrite := proxy.Rewrite
|
||||
proxy.Rewrite = func(proxyReq *httputil.ProxyRequest) {
|
||||
rewrite(proxyReq)
|
||||
proxyReq.Out.Host = target.Host
|
||||
}
|
||||
}
|
||||
|
||||
// setProxyRewrite sets the proxy.Rewrite so that request URIs are escaped
|
||||
// when proxying to usptream servers.
|
||||
func setProxyDirector(proxy *httputil.ReverseProxy) {
|
||||
director := proxy.Director
|
||||
proxy.Director = func(req *http.Request) {
|
||||
director(req)
|
||||
func setProxyRewrite(proxy *httputil.ReverseProxy) {
|
||||
rewrite := proxy.Rewrite
|
||||
proxy.Rewrite = func(proxyReq *httputil.ProxyRequest) {
|
||||
rewrite(proxyReq)
|
||||
// use RequestURI so that we aren't unescaping encoded slashes in the request path
|
||||
req.URL.Opaque = req.RequestURI
|
||||
req.URL.RawQuery = ""
|
||||
req.URL.ForceQuery = false
|
||||
proxyReq.Out.URL.Opaque = proxyReq.In.RequestURI
|
||||
proxyReq.Out.URL.RawQuery = ""
|
||||
proxyReq.Out.URL.ForceQuery = false
|
||||
}
|
||||
}
|
||||
|
||||
func setProxyForwardingHeaders(proxyReq *httputil.ProxyRequest) {
|
||||
// TODO (@tuunit): Preserve the legacy Director-based forwarding header behavior
|
||||
// for backwards compatibility. Harden this with saner defaults and/or
|
||||
// explicit flags in the future.
|
||||
for _, header := range []string{"Forwarded", "X-Forwarded-Host", "X-Forwarded-Proto"} {
|
||||
if values, ok := proxyReq.In.Header[header]; ok {
|
||||
proxyReq.Out.Header[header] = append([]string(nil), values...)
|
||||
}
|
||||
}
|
||||
|
||||
prior, ok := proxyReq.In.Header["X-Forwarded-For"]
|
||||
if ok {
|
||||
proxyReq.Out.Header["X-Forwarded-For"] = append([]string(nil), prior...)
|
||||
}
|
||||
|
||||
clientIP, _, err := net.SplitHostPort(proxyReq.In.RemoteAddr)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
omit := ok && prior == nil
|
||||
if len(prior) > 0 {
|
||||
clientIP = strings.Join(prior, ", ") + ", " + clientIP
|
||||
}
|
||||
if !omit {
|
||||
proxyReq.Out.Header.Set("X-Forwarded-For", clientIP)
|
||||
}
|
||||
}
|
||||
|
||||
// newWebSocketReverseProxy creates a new reverse proxy for proxying websocket connections.
|
||||
func newWebSocketReverseProxy(u *url.URL, skipTLSVerify *bool, passHostHeader *bool) http.Handler {
|
||||
wsProxy := httputil.NewSingleHostReverseProxy(u)
|
||||
wsProxy := newSingleHostReverseProxy(u)
|
||||
|
||||
// Inherit default transport options from Go's stdlib
|
||||
transport := http.DefaultTransport.(*http.Transport).Clone()
|
||||
|
||||
@@ -361,7 +361,12 @@ var _ = Describe("HTTP Upstream Suite", func() {
|
||||
return http.HandlerFunc(func(_ http.ResponseWriter, req *http.Request) {
|
||||
proxy, ok := h.(*httputil.ReverseProxy)
|
||||
Expect(ok).To(BeTrue())
|
||||
proxy.Director(req)
|
||||
outReq := req.Clone(req.Context())
|
||||
proxy.Rewrite(&httputil.ProxyRequest{
|
||||
In: req,
|
||||
Out: outReq,
|
||||
})
|
||||
req.Host = outReq.Host
|
||||
})
|
||||
}
|
||||
httpUpstream.handler = requestInterceptor(httpUpstream.handler)
|
||||
@@ -370,6 +375,54 @@ var _ = Describe("HTTP Upstream Suite", func() {
|
||||
Expect(req.Host).To(Equal(strings.TrimPrefix(serverAddr, "http://")))
|
||||
})
|
||||
|
||||
It("ServeHTTP preserves forwarding headers when using Rewrite", func() {
|
||||
req := httptest.NewRequest("", "http://example.localhost/foo", nil)
|
||||
req.RemoteAddr = "192.0.2.10:1234"
|
||||
req.Header.Set("Forwarded", "for=192.0.2.1;proto=https;host=example.localhost")
|
||||
req.Header.Set("X-Forwarded-Host", "forwarded.example.localhost")
|
||||
req.Header.Set("X-Forwarded-Proto", "https")
|
||||
req.Header.Set("X-Forwarded-For", "192.0.2.1")
|
||||
req = middlewareapi.AddRequestScope(req, &middlewareapi.RequestScope{})
|
||||
rw := httptest.NewRecorder()
|
||||
|
||||
upstream := options.Upstream{
|
||||
ID: "preserveForwardedHeaders",
|
||||
PassHostHeader: ptr.To(true),
|
||||
ProxyWebSockets: ptr.To(false),
|
||||
InsecureSkipTLSVerify: ptr.To(false),
|
||||
FlushInterval: &defaultFlushInterval,
|
||||
Timeout: &defaultTimeout,
|
||||
}
|
||||
|
||||
u, err := url.Parse(serverAddr)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
handler := newHTTPUpstreamProxy(upstream, u, nil, nil)
|
||||
httpUpstream, ok := handler.(*httpUpstreamProxy)
|
||||
Expect(ok).To(BeTrue())
|
||||
|
||||
requestInterceptor := func(h http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(_ http.ResponseWriter, req *http.Request) {
|
||||
proxy, ok := h.(*httputil.ReverseProxy)
|
||||
Expect(ok).To(BeTrue())
|
||||
|
||||
outReq := req.Clone(req.Context())
|
||||
proxy.Rewrite(&httputil.ProxyRequest{
|
||||
In: req,
|
||||
Out: outReq,
|
||||
})
|
||||
|
||||
Expect(outReq.Header.Values("Forwarded")).To(Equal([]string{"for=192.0.2.1;proto=https;host=example.localhost"}))
|
||||
Expect(outReq.Header.Values("X-Forwarded-Host")).To(Equal([]string{"forwarded.example.localhost"}))
|
||||
Expect(outReq.Header.Values("X-Forwarded-Proto")).To(Equal([]string{"https"}))
|
||||
Expect(outReq.Header.Values("X-Forwarded-For")).To(Equal([]string{"192.0.2.1, 192.0.2.10"}))
|
||||
})
|
||||
}
|
||||
httpUpstream.handler = requestInterceptor(httpUpstream.handler)
|
||||
|
||||
httpUpstream.ServeHTTP(rw, req)
|
||||
})
|
||||
|
||||
type newUpstreamTableInput struct {
|
||||
proxyWebSockets bool
|
||||
flushInterval time.Duration
|
||||
@@ -405,6 +458,7 @@ var _ = Describe("HTTP Upstream Suite", func() {
|
||||
|
||||
proxy, ok := upstreamProxy.handler.(*httputil.ReverseProxy)
|
||||
Expect(ok).To(BeTrue())
|
||||
Expect(proxy.Rewrite).ToNot(BeNil())
|
||||
Expect(proxy.FlushInterval).To(Equal(in.flushInterval))
|
||||
transport, ok := proxy.Transport.(*http.Transport)
|
||||
Expect(ok).To(BeTrue())
|
||||
|
||||
@@ -79,7 +79,7 @@ var _ = Describe("Proxy Suite", func() {
|
||||
{
|
||||
ID: "bad-http-backend",
|
||||
Path: "/bad-http/",
|
||||
URI: "http://::1",
|
||||
URI: invalidServer,
|
||||
},
|
||||
{
|
||||
ID: "single-path-backend",
|
||||
|
||||
@@ -24,7 +24,7 @@ var (
|
||||
serverAddr string
|
||||
unixServer *httptest.Server
|
||||
unixServerAddr string
|
||||
invalidServer = "http://::1"
|
||||
invalidServer = "http://127.0.0.1:1"
|
||||
)
|
||||
|
||||
func TestUpstreamSuite(t *testing.T) {
|
||||
|
||||
@@ -193,8 +193,8 @@ var _ = Describe("Sessions", func() {
|
||||
unreachableRedisDelMsg = "unable to delete the redis initialization key: dial tcp 127.0.0.1:65535: connect: connection refused"
|
||||
unreachableSentinelSetMsg = "unable to set a redis initialization key: redis: all sentinels specified in configuration are unreachable: redis: nil"
|
||||
unrechableSentinelDelMsg = "unable to delete the redis initialization key: redis: all sentinels specified in configuration are unreachable: redis: nil"
|
||||
refusedSentinelSetMsg = "unable to set a redis initialization key: redis: all sentinels specified in configuration are unreachable: context deadline exceeded"
|
||||
refusedSentinelDelMsg = "unable to delete the redis initialization key: redis: all sentinels specified in configuration are unreachable: context deadline exceeded"
|
||||
refusedSentinelSetMsg = "unable to set a redis initialization key: redis: all sentinels specified in configuration are unreachable: dial tcp 127.0.0.1:65535: connect: connection refused"
|
||||
refusedSentinelDelMsg = "unable to delete the redis initialization key: redis: all sentinels specified in configuration are unreachable: dial tcp 127.0.0.1:65535: connect: connection refused"
|
||||
)
|
||||
|
||||
type redisStoreTableInput struct {
|
||||
|
||||
Reference in New Issue
Block a user