mirror of
https://github.com/tailscale/tailscale.git
synced 2025-04-20 21:51:42 +00:00
ipn/ipnlocal: fix proxy path that matches mount point (#10864)
Don't append a trailing slash to a request path to the reverse proxy that matches the mount point exactly. Updates tailscale/tailscale#10730 Signed-off-by: Irbe Krumina <irbe@tailscale.com>
This commit is contained in:
parent
8b47322acc
commit
6ee956333f
@ -605,7 +605,20 @@ func (rp *reverseProxy) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
p := &httputil.ReverseProxy{Rewrite: func(r *httputil.ProxyRequest) {
|
p := &httputil.ReverseProxy{Rewrite: func(r *httputil.ProxyRequest) {
|
||||||
|
oldOutPath := r.Out.URL.Path
|
||||||
r.SetURL(rp.url)
|
r.SetURL(rp.url)
|
||||||
|
|
||||||
|
// If mount point matches the request path exactly, the outbound
|
||||||
|
// request URL was set to empty string in serveWebHandler which
|
||||||
|
// would have resulted in the outbound path set to <proxy path>
|
||||||
|
// + '/' in SetURL. In that case, if the proxy path was set, we
|
||||||
|
// want to send the request to the <proxy path> (without the
|
||||||
|
// '/') .
|
||||||
|
if oldOutPath == "" && rp.url.Path != "" {
|
||||||
|
r.Out.URL.Path = rp.url.Path
|
||||||
|
r.Out.URL.RawPath = rp.url.RawPath
|
||||||
|
}
|
||||||
|
|
||||||
r.Out.Host = r.In.Host
|
r.Out.Host = r.In.Host
|
||||||
addProxyForwardedHeaders(r)
|
addProxyForwardedHeaders(r)
|
||||||
rp.lb.addTailscaleIdentityHeaders(r)
|
rp.lb.addTailscaleIdentityHeaders(r)
|
||||||
|
@ -348,7 +348,108 @@ func TestServeConfigETag(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestServeHTTPProxy(t *testing.T) {
|
func TestServeHTTPProxyPath(t *testing.T) {
|
||||||
|
b := newTestBackend(t)
|
||||||
|
// Start test serve endpoint.
|
||||||
|
testServ := httptest.NewServer(http.HandlerFunc(
|
||||||
|
func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
// Set the request URL path to a response header, so the
|
||||||
|
// requested URL path can be checked in tests.
|
||||||
|
t.Logf("adding path %s", r.URL.Path)
|
||||||
|
w.Header().Add("Path", r.URL.Path)
|
||||||
|
},
|
||||||
|
))
|
||||||
|
defer testServ.Close()
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
mountPoint string
|
||||||
|
proxyPath string
|
||||||
|
requestPath string
|
||||||
|
wantRequestPath string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "/foo -> /foo, with mount point and path /foo",
|
||||||
|
mountPoint: "/foo",
|
||||||
|
proxyPath: "/foo",
|
||||||
|
requestPath: "/foo",
|
||||||
|
wantRequestPath: "/foo",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "/foo/ -> /foo/, with mount point and path /foo",
|
||||||
|
mountPoint: "/foo",
|
||||||
|
proxyPath: "/foo",
|
||||||
|
requestPath: "/foo/",
|
||||||
|
wantRequestPath: "/foo/",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "/foo -> /foo/, with mount point and path /foo/",
|
||||||
|
mountPoint: "/foo/",
|
||||||
|
proxyPath: "/foo/",
|
||||||
|
requestPath: "/foo",
|
||||||
|
wantRequestPath: "/foo/",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "/-> /, with mount point and path /",
|
||||||
|
mountPoint: "/",
|
||||||
|
proxyPath: "/",
|
||||||
|
requestPath: "/",
|
||||||
|
wantRequestPath: "/",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "/foo -> /foo, with mount point and path /",
|
||||||
|
mountPoint: "/",
|
||||||
|
proxyPath: "/",
|
||||||
|
requestPath: "/foo",
|
||||||
|
wantRequestPath: "/foo",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "/foo/bar -> /foo/bar, with mount point and path /foo",
|
||||||
|
mountPoint: "/foo",
|
||||||
|
proxyPath: "/foo",
|
||||||
|
requestPath: "/foo/bar",
|
||||||
|
wantRequestPath: "/foo/bar",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "/foo/bar/baz -> /foo/bar/baz, with mount point and path /foo",
|
||||||
|
mountPoint: "/foo",
|
||||||
|
proxyPath: "/foo",
|
||||||
|
requestPath: "/foo/bar/baz",
|
||||||
|
wantRequestPath: "/foo/bar/baz",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
conf := &ipn.ServeConfig{
|
||||||
|
Web: map[ipn.HostPort]*ipn.WebServerConfig{
|
||||||
|
"example.ts.net:443": {Handlers: map[string]*ipn.HTTPHandler{
|
||||||
|
tt.mountPoint: {Proxy: testServ.URL + tt.proxyPath},
|
||||||
|
}},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
if err := b.SetServeConfig(conf, ""); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
req := &http.Request{
|
||||||
|
URL: &url.URL{Path: tt.requestPath},
|
||||||
|
TLS: &tls.ConnectionState{ServerName: "example.ts.net"},
|
||||||
|
}
|
||||||
|
req = req.WithContext(context.WithValue(req.Context(), serveHTTPContextKey{}, &serveHTTPContext{
|
||||||
|
DestPort: 443,
|
||||||
|
SrcAddr: netip.MustParseAddrPort("1.2.3.4:1234"), // random src
|
||||||
|
}))
|
||||||
|
|
||||||
|
w := httptest.NewRecorder()
|
||||||
|
b.serveWebHandler(w, req)
|
||||||
|
|
||||||
|
// Verify what path was requested
|
||||||
|
p := w.Result().Header.Get("Path")
|
||||||
|
if p != tt.wantRequestPath {
|
||||||
|
t.Errorf("wanted request path %s got %s", tt.wantRequestPath, p)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
func TestServeHTTPProxyHeaders(t *testing.T) {
|
||||||
b := newTestBackend(t)
|
b := newTestBackend(t)
|
||||||
|
|
||||||
// Start test serve endpoint.
|
// Start test serve endpoint.
|
||||||
|
Loading…
x
Reference in New Issue
Block a user