From 65735be4da673abeb1f8dacc59de3286ba2ddc44 Mon Sep 17 00:00:00 2001 From: Nick Craig-Wood Date: Wed, 12 Aug 2026 11:56:07 +0100 Subject: [PATCH] serve sftp: fix auth proxy configured via rc being silently ignored GHSA-p569-5gjg-9cmj CVE-PENDING From v1.70.0, an SFTP 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: the server either failed to start with "no authorization found" or authenticated against the local authorized_keys file instead of routing each login through the proxy the operator configured. The serve Provider refactor (f425f8d46) fixed the constructor by building the provider from the supplied proxyOpt, but the authorized-keys handling in configure() still consulted the global option. Make it depend on whether proxy mode is actually active, and add a regression test for the per-server option. --- cmd/serve/sftp/server.go | 4 ++-- cmd/serve/sftp/sftp_test.go | 26 ++++++++++++++++++++++++++ 2 files changed, 28 insertions(+), 2 deletions(-) diff --git a/cmd/serve/sftp/server.go b/cmd/serve/sftp/server.go index cc8d65676..81f3790e4 100644 --- a/cmd/serve/sftp/server.go +++ b/cmd/serve/sftp/server.go @@ -143,12 +143,12 @@ func (s *server) configure() (err error) { var authorizedKeysMap map[string]struct{} // ensure the user isn't trying to use conflicting flags - if proxy.Opt.AuthProxy != "" && s.opt.AuthorizedKeys != "" && s.opt.AuthorizedKeys != Opt.AuthorizedKeys { + if s.provider.IsProxy() && s.opt.AuthorizedKeys != "" && s.opt.AuthorizedKeys != Opt.AuthorizedKeys { return errors.New("--auth-proxy and --authorized-keys cannot be used at the same time") } // Load the authorized keys - if s.opt.AuthorizedKeys != "" && proxy.Opt.AuthProxy == "" { + if s.opt.AuthorizedKeys != "" && !s.provider.IsProxy() { authKeysFile := env.ShellExpand(s.opt.AuthorizedKeys) authorizedKeysMap, err = loadAuthorizedKeys(authKeysFile) // If user set the flag away from the default then report an error diff --git a/cmd/serve/sftp/sftp_test.go b/cmd/serve/sftp/sftp_test.go index ecbe500cb..de6c89d8e 100644 --- a/cmd/serve/sftp/sftp_test.go +++ b/cmd/serve/sftp/sftp_test.go @@ -78,6 +78,32 @@ func TestSftp(t *testing.T) { servetest.Run(t, "sftp", start) } +// 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) { + // Ensure the global is empty so we only test the per-server option. + assert.Equal(t, "", proxy.Opt.AuthProxy) + + opt := Opt + opt.ListenAddr = testBindAddress + + proxyOpt := proxy.Opt + proxyOpt.AuthProxy = "/path/to/auth/proxy" + + w, err := newServer(context.Background(), nil, &opt, &vfscommon.Opt, &proxyOpt) + require.NoError(t, err) + // Shutdown waits for Serve to finish, so Serve must be running first. + go func() { + assert.NoError(t, w.Serve()) + }() + 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": "sftp",