diff --git a/fs/rc/rcserver/rcserver.go b/fs/rc/rcserver/rcserver.go index c32aa0ea6..ba7e957ba 100644 --- a/fs/rc/rcserver/rcserver.go +++ b/fs/rc/rcserver/rcserver.go @@ -329,6 +329,12 @@ func (s *Server) handleOptions(w http.ResponseWriter, r *http.Request, path stri } func (s *Server) serveRoot(w http.ResponseWriter, r *http.Request) { + // Listing the configured remotes discloses their names so + // require auth like the rest of the rc endpoints + if !s.noAuth && !s.server.UsingAuth() { + writeError(r.URL.Path, nil, w, errors.New("listing the remotes requires authentication to be set up on the rc server or the --rc-no-auth flag"), http.StatusForbidden) + return + } remoteNames := config.GetRemoteNames() sort.Strings(remoteNames) directory := serve.NewDirectory("", s.server.HTMLTemplate()) diff --git a/fs/rc/rcserver/rcserver_test.go b/fs/rc/rcserver/rcserver_test.go index 11cacf964..533dbcb28 100644 --- a/fs/rc/rcserver/rcserver_test.go +++ b/fs/rc/rcserver/rcserver_test.go @@ -787,6 +787,7 @@ func TestServingRoot(t *testing.T) { opt := newTestOpt() opt.Serve = true opt.Files = testFs + opt.NoAuth = true testServer(t, tests, &opt) } @@ -800,6 +801,53 @@ func TestServingRootNoFiles(t *testing.T) { opt := newTestOpt() opt.Serve = true opt.Files = "" + opt.NoAuth = true + testServer(t, tests, &opt) +} + +// On a server with no authentication configured and without --rc-no-auth the +// remote listing must not disclose the configured remote names. +func TestServingRootFailClosed(t *testing.T) { + forbidden := regexp.MustCompile(`"status": 403`) + tests := []testRun{{ + Name: "rootlist-star", + URL: "*", + Status: http.StatusForbidden, + Contains: forbidden, + }, { + Name: "rootlist-empty", + URL: "", + Status: http.StatusForbidden, + Contains: forbidden, + }} + opt := newTestOpt() + opt.Serve = true + opt.Files = "" + testServer(t, tests, &opt) +} + +// With authentication configured the remote listing is available to +// authenticated requests only. +func TestServingRootWithAuth(t *testing.T) { + const user, pass = "user", "pass" + tests := []testRun{{ + Name: "rootlist-authed", + URL: "", + User: user, + Pass: pass, + Status: http.StatusOK, + Contains: matchRemoteDirListing, + }, { + Name: "rootlist-unauthed", + URL: "", + Status: http.StatusUnauthorized, + Contains: regexp.MustCompile(`Unauthorized`), + }} + opt := newTestOpt() + opt.Serve = true + opt.Files = "" + opt.Auth.BasicUser = user + opt.Auth.BasicPass = pass testServer(t, tests, &opt) }