gui: respect explicit --rc-allow-origin instead of always deriving it from the bind address
Fixes #9640 The auto-derived CORS origin logic (added in #9603 for the wildcard-bind case) ran unconditionally, so it clobbered an explicitly-passed --rc-allow-origin too - the flag was silently ignored. That breaks the documented Docker/remote-browser setup where the bind address and the address the browser actually reaches the GUI at are different. Now it only kicks in when the user hasn't set --rc-allow-origin themselves.
This commit is contained in:
+24
-14
@@ -175,20 +175,8 @@ For more help see [the GUI docs](/gui/).
|
||||
}
|
||||
}
|
||||
|
||||
// When the GUI is bound to a wildcard address the bound origin
|
||||
// (e.g. http://[::]:5522) is never what a browser sends in its
|
||||
// Origin header, and the GUI may be reached via any number of
|
||||
// hosts (localhost, a LAN IP, a Docker host), so no single
|
||||
// origin can match them all.
|
||||
switch addr, _ := guiServer.Addr().(*net.TCPAddr); {
|
||||
case addr == nil || !addr.IP.IsUnspecified():
|
||||
opt.HTTP.AllowOrigin = guiOrigin
|
||||
case !opt.NoAuth:
|
||||
opt.HTTP.AllowOrigin = "*"
|
||||
default:
|
||||
opt.HTTP.AllowOrigin = guiOrigin
|
||||
fs.Logf(nil, "GUI bound to a wildcard address with --no-auth: browsers can only use the API from %s. Enable auth or bind --addr to a specific host.", guiOrigin)
|
||||
}
|
||||
addr, _ := guiServer.Addr().(*net.TCPAddr)
|
||||
opt.HTTP.AllowOrigin = resolveAllowOrigin(command.Flags().Changed("rc-allow-origin"), opt.HTTP.AllowOrigin, guiOrigin, addr, opt.NoAuth)
|
||||
|
||||
// Start the RC server (unchanged rcserver.Start)
|
||||
rcServer, err := rcserver.Start(ctx, &opt)
|
||||
@@ -253,6 +241,28 @@ func originFromURL(rawURL string) string {
|
||||
return u.Scheme + "://" + u.Host
|
||||
}
|
||||
|
||||
// resolveAllowOrigin picks the Access-Control-Allow-Origin value for the RC
|
||||
// API server. An explicit --rc-allow-origin always wins. Otherwise a value is
|
||||
// derived from how the GUI is bound: the bound origin (e.g. http://[::]:5522)
|
||||
// is never what a browser sends in its Origin header when the GUI is bound to
|
||||
// a wildcard address, and the GUI may be reached via any number of hosts
|
||||
// (localhost, a LAN IP, a Docker host), so no single origin can match them
|
||||
// all.
|
||||
func resolveAllowOrigin(explicitAllowOrigin bool, currentAllowOrigin, guiOrigin string, addr *net.TCPAddr, noAuth bool) string {
|
||||
if explicitAllowOrigin {
|
||||
return currentAllowOrigin
|
||||
}
|
||||
switch {
|
||||
case addr == nil || !addr.IP.IsUnspecified():
|
||||
return guiOrigin
|
||||
case !noAuth:
|
||||
return "*"
|
||||
default:
|
||||
fs.Logf(nil, "GUI bound to a wildcard address with --no-auth: browsers can only use the API from %s. Enable auth or bind --addr to a specific host.", guiOrigin)
|
||||
return guiOrigin
|
||||
}
|
||||
}
|
||||
|
||||
// guiSourceFS opens the GUI bundle at the given path. An empty path
|
||||
// returns the embedded bundle (read from the zip embedded in the binary).
|
||||
// The returned cleanup func must be called on shutdown (no-op for the
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"compress/gzip"
|
||||
"io"
|
||||
iofs "io/fs"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"os"
|
||||
@@ -99,6 +100,66 @@ func TestOriginFromURL(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestResolveAllowOrigin(t *testing.T) {
|
||||
wildcard := &net.TCPAddr{IP: net.IPv4zero, Port: 5533}
|
||||
specific := &net.TCPAddr{IP: net.ParseIP("127.0.0.1"), Port: 5533}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
explicitAllowOrigin bool
|
||||
currentAllowOrigin string
|
||||
guiOrigin string
|
||||
addr *net.TCPAddr
|
||||
noAuth bool
|
||||
want string
|
||||
}{
|
||||
{
|
||||
name: "explicit flag wins even on a wildcard bind",
|
||||
explicitAllowOrigin: true,
|
||||
currentAllowOrigin: "http://192.0.2.10:5522",
|
||||
guiOrigin: "http://[::]:5580",
|
||||
addr: wildcard,
|
||||
noAuth: false,
|
||||
want: "http://192.0.2.10:5522",
|
||||
},
|
||||
{
|
||||
name: "explicit flag wins on a specific bind too",
|
||||
explicitAllowOrigin: true,
|
||||
currentAllowOrigin: "http://192.0.2.10:5522",
|
||||
guiOrigin: "http://127.0.0.1:5580",
|
||||
addr: specific,
|
||||
noAuth: false,
|
||||
want: "http://192.0.2.10:5522",
|
||||
},
|
||||
{
|
||||
name: "specific bind falls back to the gui origin",
|
||||
guiOrigin: "http://127.0.0.1:5580",
|
||||
addr: specific,
|
||||
want: "http://127.0.0.1:5580",
|
||||
},
|
||||
{
|
||||
name: "wildcard bind with auth falls back to *",
|
||||
guiOrigin: "http://[::]:5580",
|
||||
addr: wildcard,
|
||||
noAuth: false,
|
||||
want: "*",
|
||||
},
|
||||
{
|
||||
name: "wildcard bind without auth falls back to the gui origin",
|
||||
guiOrigin: "http://[::]:5580",
|
||||
addr: wildcard,
|
||||
noAuth: true,
|
||||
want: "http://[::]:5580",
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got := resolveAllowOrigin(tt.explicitAllowOrigin, tt.currentAllowOrigin, tt.guiOrigin, tt.addr, tt.noAuth)
|
||||
assert.Equal(t, tt.want, got)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// newTestHandler returns a guiHandler backed by the embedded GUI
|
||||
// bundle, or skips the test if it is not present (i.e. `make fetch-gui`
|
||||
// has not been run).
|
||||
|
||||
Reference in New Issue
Block a user