From bcef98db3f58f6dfdad3e7c5c30f29fdd5675b0a Mon Sep 17 00:00:00 2001 From: Nick Craig-Wood Date: Wed, 12 Aug 2026 11:55:17 +0100 Subject: [PATCH] serve s3: fix misleading anonymous access log and add test for auth proxy via rc GHSA-p569-5gjg-9cmj CVE-PENDING From v1.70.0 until the serve Provider refactor (f425f8d46), an S3 server started through the rc serve/start API with a per-server proxyOpt.AuthProxy decided whether to enable proxy authentication by checking the process-global proxy.Opt.AuthProxy instead of the supplied proxyOpt.AuthProxy. In the normal rc case the global is empty, so the auth proxy was silently ignored and the server served the fixed filesystem supplied to serve/start rather than routing each access key to the backend chosen by the proxy, bypassing the operator's intended per-key authorization. The Provider refactor fixed this incidentally by building the provider from the proxyOpt passed to the constructor. This adds a regression test so the per-server option cannot silently stop working again, and only logs "allowing anonymous access" when neither an auth key nor an auth proxy is configured so the log reflects the effective mode. (cherry picked from commit 1e8134e83bbdcededfdc3c48f00e52544a775342) --- cmd/serve/s3/s3_test.go | 28 ++++++++++++++++++++++++++++ cmd/serve/s3/server.go | 4 ++-- 2 files changed, 30 insertions(+), 2 deletions(-) diff --git a/cmd/serve/s3/s3_test.go b/cmd/serve/s3/s3_test.go index 2baf487f3..1eea39916 100644 --- a/cmd/serve/s3/s3_test.go +++ b/cmd/serve/s3/s3_test.go @@ -308,6 +308,34 @@ func TestListBucketsAuthProxy(t *testing.T) { testListBuckets(t, cases, true) } +// TestNewServerPerServerAuthProxy checks that a per-server proxyOpt.AuthProxy +// enables proxy mode even when the process-global proxy.Opt.AuthProxy is empty, +// which is the normal case when the server is configured via serve/start. +func TestNewServerPerServerAuthProxy(t *testing.T) { + fstest.Initialise() + + // Ensure the global is empty so we only test the per-server option. + assert.Equal(t, "", proxy.Opt.AuthProxy) + + f, err := fs.NewFs(context.Background(), "testdata") + require.NoError(t, err) + + opt := Opt + opt.AuthKey = []string{"access-key,secret-key"} + opt.HTTP.ListenAddr = []string{endpoint} + + proxyOpt := proxy.Opt + proxyOpt.AuthProxy = "/path/to/auth/proxy" + + w, err := newServer(context.Background(), f, &opt, &vfscommon.Opt, &proxyOpt) + require.NoError(t, err) + defer func() { + assert.NoError(t, w.Shutdown()) + }() + assert.True(t, w.provider.IsProxy(), "expected auth proxy to be enabled by per-server option") + assert.Nil(t, w.provider.VFS(), "expected no fixed VFS when auth proxy is in use") +} + func TestRc(t *testing.T) { servetest.TestRc(t, rc.Params{ "type": "s3", diff --git a/cmd/serve/s3/server.go b/cmd/serve/s3/server.go index 1f10839cc..7082384d8 100644 --- a/cmd/serve/s3/server.go +++ b/cmd/serve/s3/server.go @@ -71,9 +71,9 @@ func newServer(ctx context.Context, f fs.Fs, opt *Options, vfsOpt *vfscommon.Opt fs.Debugf(f, "Using hash %v for ETag", w.etagHashType) } - if len(opt.AuthKey) == 0 { + if len(opt.AuthKey) == 0 && !w.provider.IsProxy() { fs.Logf("serve s3", "No auth provided so allowing anonymous access") - } else { + } else if len(opt.AuthKey) > 0 { w.s3Secret = getAuthSecret(opt.AuthKey) }