diff --git a/internal/common/common.go b/internal/common/common.go index 3ef02819..062aacf3 100644 --- a/internal/common/common.go +++ b/internal/common/common.go @@ -651,7 +651,7 @@ func (c *Configuration) initializeProxyProtocol() error { // GetProxyListener returns a wrapper for the given listener that supports the // HAProxy Proxy Protocol -func (c *Configuration) GetProxyListener(listener net.Listener) (*proxyproto.Listener, error) { +func (c *Configuration) GetProxyListener(listener net.Listener) (net.Listener, error) { if c.ProxyProtocol > 0 { defaultPolicy := proxyproto.REQUIRE if c.ProxyProtocol == 1 { diff --git a/internal/common/common_test.go b/internal/common/common_test.go index 0b2df639..27ae0de5 100644 --- a/internal/common/common_test.go +++ b/internal/common/common_test.go @@ -1090,13 +1090,17 @@ func TestProxyProtocolVersion(t *testing.T) { assert.Contains(t, err.Error(), "proxy protocol not configured") } c.ProxyProtocol = 1 - proxyListener, err := c.GetProxyListener(nil) + listener, err := c.GetProxyListener(nil) assert.NoError(t, err) + proxyListener, ok := listener.(*proxyproto.Listener) + require.True(t, ok) assert.NotNil(t, proxyListener.Policy) c.ProxyProtocol = 2 - proxyListener, err = c.GetProxyListener(nil) + listener, err = c.GetProxyListener(nil) assert.NoError(t, err) + proxyListener, ok = listener.(*proxyproto.Listener) + require.True(t, ok) assert.NotNil(t, proxyListener.Policy) } diff --git a/internal/config/config.go b/internal/config/config.go index db83d551..34ecb840 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -92,6 +92,7 @@ var ( TLSCipherSuites: nil, Protocols: nil, Prefix: "", + ProxyMode: 0, ProxyAllowed: nil, ClientIPProxyHeader: "", ClientIPHeaderDepth: 0, @@ -111,6 +112,7 @@ var ( ClientAuthType: 0, TLSCipherSuites: nil, Protocols: nil, + ProxyMode: 0, ProxyAllowed: nil, ClientIPProxyHeader: "", ClientIPHeaderDepth: 0, @@ -875,13 +877,13 @@ func getRateLimitersFromEnv(idx int) { isSet = true } - burst, ok := lookupIntFromEnv(fmt.Sprintf("SFTPGO_COMMON__RATE_LIMITERS__%v__BURST", idx), 0) + burst, ok := lookupIntFromEnv(fmt.Sprintf("SFTPGO_COMMON__RATE_LIMITERS__%v__BURST", idx), 32) if ok { rtlConfig.Burst = int(burst) isSet = true } - rtlType, ok := lookupIntFromEnv(fmt.Sprintf("SFTPGO_COMMON__RATE_LIMITERS__%v__TYPE", idx), 0) + rtlType, ok := lookupIntFromEnv(fmt.Sprintf("SFTPGO_COMMON__RATE_LIMITERS__%v__TYPE", idx), 32) if ok { rtlConfig.Type = int(rtlType) isSet = true @@ -899,13 +901,13 @@ func getRateLimitersFromEnv(idx int) { isSet = true } - softLimit, ok := lookupIntFromEnv(fmt.Sprintf("SFTPGO_COMMON__RATE_LIMITERS__%v__ENTRIES_SOFT_LIMIT", idx), 0) + softLimit, ok := lookupIntFromEnv(fmt.Sprintf("SFTPGO_COMMON__RATE_LIMITERS__%v__ENTRIES_SOFT_LIMIT", idx), 32) if ok { rtlConfig.EntriesSoftLimit = int(softLimit) isSet = true } - hardLimit, ok := lookupIntFromEnv(fmt.Sprintf("SFTPGO_COMMON__RATE_LIMITERS__%v__ENTRIES_HARD_LIMIT", idx), 0) + hardLimit, ok := lookupIntFromEnv(fmt.Sprintf("SFTPGO_COMMON__RATE_LIMITERS__%v__ENTRIES_HARD_LIMIT", idx), 32) if ok { rtlConfig.EntriesHardLimit = int(hardLimit) isSet = true @@ -941,7 +943,7 @@ func getKMSPluginFromEnv(idx int, pluginConfig *plugin.Config) bool { func getAuthPluginFromEnv(idx int, pluginConfig *plugin.Config) bool { isSet := false - authScope, ok := lookupIntFromEnv(fmt.Sprintf("SFTPGO_PLUGINS__%v__AUTH_OPTIONS__SCOPE", idx), 0) + authScope, ok := lookupIntFromEnv(fmt.Sprintf("SFTPGO_PLUGINS__%v__AUTH_OPTIONS__SCOPE", idx), 32) if ok { pluginConfig.AuthOptions.Scope = int(authScope) isSet = true @@ -986,13 +988,13 @@ func getNotifierPluginFromEnv(idx int, pluginConfig *plugin.Config) bool { } } - notifierRetryMaxTime, ok := lookupIntFromEnv(fmt.Sprintf("SFTPGO_PLUGINS__%v__NOTIFIER_OPTIONS__RETRY_MAX_TIME", idx), 0) + notifierRetryMaxTime, ok := lookupIntFromEnv(fmt.Sprintf("SFTPGO_PLUGINS__%v__NOTIFIER_OPTIONS__RETRY_MAX_TIME", idx), 32) if ok { pluginConfig.NotifierOptions.RetryMaxTime = int(notifierRetryMaxTime) isSet = true } - notifierRetryQueueMaxSize, ok := lookupIntFromEnv(fmt.Sprintf("SFTPGO_PLUGINS__%v__NOTIFIER_OPTIONS__RETRY_QUEUE_MAX_SIZE", idx), 0) + notifierRetryQueueMaxSize, ok := lookupIntFromEnv(fmt.Sprintf("SFTPGO_PLUGINS__%v__NOTIFIER_OPTIONS__RETRY_QUEUE_MAX_SIZE", idx), 32) if ok { pluginConfig.NotifierOptions.RetryQueueMaxSize = int(notifierRetryQueueMaxSize) isSet = true @@ -1080,7 +1082,7 @@ func getSFTPDBindindFromEnv(idx int) { isSet := false - port, ok := lookupIntFromEnv(fmt.Sprintf("SFTPGO_SFTPD__BINDINGS__%v__PORT", idx), 0) + port, ok := lookupIntFromEnv(fmt.Sprintf("SFTPGO_SFTPD__BINDINGS__%v__PORT", idx), 32) if ok { binding.Port = int(port) isSet = true @@ -1167,19 +1169,19 @@ func getFTPDBindingSecurityFromEnv(idx int, binding *ftpd.Binding) bool { isSet = true } - tlsMode, ok := lookupIntFromEnv(fmt.Sprintf("SFTPGO_FTPD__BINDINGS__%v__TLS_MODE", idx), 0) + tlsMode, ok := lookupIntFromEnv(fmt.Sprintf("SFTPGO_FTPD__BINDINGS__%v__TLS_MODE", idx), 32) if ok { binding.TLSMode = int(tlsMode) isSet = true } - tlsSessionReuse, ok := lookupIntFromEnv(fmt.Sprintf("SFTPGO_FTPD__BINDINGS__%v__TLS_SESSION_REUSE", idx), 0) + tlsSessionReuse, ok := lookupIntFromEnv(fmt.Sprintf("SFTPGO_FTPD__BINDINGS__%v__TLS_SESSION_REUSE", idx), 32) if ok { binding.TLSSessionReuse = int(tlsSessionReuse) isSet = true } - tlsVer, ok := lookupIntFromEnv(fmt.Sprintf("SFTPGO_FTPD__BINDINGS__%v__MIN_TLS_VERSION", idx), 0) + tlsVer, ok := lookupIntFromEnv(fmt.Sprintf("SFTPGO_FTPD__BINDINGS__%v__MIN_TLS_VERSION", idx), 32) if ok { binding.MinTLSVersion = int(tlsVer) isSet = true @@ -1191,25 +1193,25 @@ func getFTPDBindingSecurityFromEnv(idx int, binding *ftpd.Binding) bool { isSet = true } - clientAuthType, ok := lookupIntFromEnv(fmt.Sprintf("SFTPGO_FTPD__BINDINGS__%v__CLIENT_AUTH_TYPE", idx), 0) + clientAuthType, ok := lookupIntFromEnv(fmt.Sprintf("SFTPGO_FTPD__BINDINGS__%v__CLIENT_AUTH_TYPE", idx), 32) if ok { binding.ClientAuthType = int(clientAuthType) isSet = true } - pasvSecurity, ok := lookupIntFromEnv(fmt.Sprintf("SFTPGO_FTPD__BINDINGS__%v__PASSIVE_CONNECTIONS_SECURITY", idx), 0) + pasvSecurity, ok := lookupIntFromEnv(fmt.Sprintf("SFTPGO_FTPD__BINDINGS__%v__PASSIVE_CONNECTIONS_SECURITY", idx), 32) if ok { binding.PassiveConnectionsSecurity = int(pasvSecurity) isSet = true } - activeSecurity, ok := lookupIntFromEnv(fmt.Sprintf("SFTPGO_FTPD__BINDINGS__%v__ACTIVE_CONNECTIONS_SECURITY", idx), 0) + activeSecurity, ok := lookupIntFromEnv(fmt.Sprintf("SFTPGO_FTPD__BINDINGS__%v__ACTIVE_CONNECTIONS_SECURITY", idx), 32) if ok { binding.ActiveConnectionsSecurity = int(activeSecurity) isSet = true } - ignoreASCIITransferType, ok := lookupIntFromEnv(fmt.Sprintf("SFTPGO_FTPD__BINDINGS__%d__IGNORE_ASCII_TRANSFER_TYPE", idx), 0) + ignoreASCIITransferType, ok := lookupIntFromEnv(fmt.Sprintf("SFTPGO_FTPD__BINDINGS__%d__IGNORE_ASCII_TRANSFER_TYPE", idx), 32) if ok { binding.IgnoreASCIITransferType = int(ignoreASCIITransferType) isSet = true @@ -1222,7 +1224,7 @@ func getFTPDBindingFromEnv(idx int) { binding := getDefaultFTPDBinding(idx) isSet := false - port, ok := lookupIntFromEnv(fmt.Sprintf("SFTPGO_FTPD__BINDINGS__%v__PORT", idx), 0) + port, ok := lookupIntFromEnv(fmt.Sprintf("SFTPGO_FTPD__BINDINGS__%v__PORT", idx), 32) if ok { binding.Port = int(port) isSet = true @@ -1302,13 +1304,13 @@ func getWebDAVBindingHTTPSConfigsFromEnv(idx int, binding *webdavd.Binding) bool isSet = true } - tlsVer, ok := lookupIntFromEnv(fmt.Sprintf("SFTPGO_WEBDAVD__BINDINGS__%v__MIN_TLS_VERSION", idx), 0) + tlsVer, ok := lookupIntFromEnv(fmt.Sprintf("SFTPGO_WEBDAVD__BINDINGS__%v__MIN_TLS_VERSION", idx), 32) if ok { binding.MinTLSVersion = int(tlsVer) isSet = true } - clientAuthType, ok := lookupIntFromEnv(fmt.Sprintf("SFTPGO_WEBDAVD__BINDINGS__%v__CLIENT_AUTH_TYPE", idx), 0) + clientAuthType, ok := lookupIntFromEnv(fmt.Sprintf("SFTPGO_WEBDAVD__BINDINGS__%v__CLIENT_AUTH_TYPE", idx), 32) if ok { binding.ClientAuthType = int(clientAuthType) isSet = true @@ -1332,6 +1334,12 @@ func getWebDAVBindingHTTPSConfigsFromEnv(idx int, binding *webdavd.Binding) bool func getWebDAVDBindingProxyConfigsFromEnv(idx int, binding *webdavd.Binding) bool { isSet := false + proxyMode, ok := lookupIntFromEnv(fmt.Sprintf("SFTPGO_WEBDAVD__BINDINGS__%v__PROXY_MODE", idx), 32) + if ok { + binding.ProxyMode = int(proxyMode) + isSet = true + } + proxyAllowed, ok := lookupStringListFromEnv(fmt.Sprintf("SFTPGO_WEBDAVD__BINDINGS__%v__PROXY_ALLOWED", idx)) if ok { binding.ProxyAllowed = proxyAllowed @@ -1344,7 +1352,7 @@ func getWebDAVDBindingProxyConfigsFromEnv(idx int, binding *webdavd.Binding) boo isSet = true } - clientIPHeaderDepth, ok := lookupIntFromEnv(fmt.Sprintf("SFTPGO_WEBDAVD__BINDINGS__%v__CLIENT_IP_HEADER_DEPTH", idx), 0) + clientIPHeaderDepth, ok := lookupIntFromEnv(fmt.Sprintf("SFTPGO_WEBDAVD__BINDINGS__%v__CLIENT_IP_HEADER_DEPTH", idx), 32) if ok { binding.ClientIPHeaderDepth = int(clientIPHeaderDepth) isSet = true @@ -1382,7 +1390,7 @@ func getWebDAVDBindingFromEnv(idx int) { isSet := false - port, ok := lookupIntFromEnv(fmt.Sprintf("SFTPGO_WEBDAVD__BINDINGS__%v__PORT", idx), 0) + port, ok := lookupIntFromEnv(fmt.Sprintf("SFTPGO_WEBDAVD__BINDINGS__%v__PORT", idx), 32) if ok { binding.Port = int(port) isSet = true @@ -1747,6 +1755,12 @@ func getHTTPDNestedObjectsFromEnv(idx int, binding *httpd.Binding) bool { func getHTTPDBindingProxyConfigsFromEnv(idx int, binding *httpd.Binding) bool { isSet := false + proxyMode, ok := lookupIntFromEnv(fmt.Sprintf("SFTPGO_HTTPD__BINDINGS__%v__PROXY_MODE", idx), 32) + if ok { + binding.ProxyMode = int(proxyMode) + isSet = true + } + proxyAllowed, ok := lookupStringListFromEnv(fmt.Sprintf("SFTPGO_HTTPD__BINDINGS__%v__PROXY_ALLOWED", idx)) if ok { binding.ProxyAllowed = proxyAllowed @@ -1759,7 +1773,7 @@ func getHTTPDBindingProxyConfigsFromEnv(idx int, binding *httpd.Binding) bool { isSet = true } - clientIPHeaderDepth, ok := lookupIntFromEnv(fmt.Sprintf("SFTPGO_HTTPD__BINDINGS__%v__CLIENT_IP_HEADER_DEPTH", idx), 0) + clientIPHeaderDepth, ok := lookupIntFromEnv(fmt.Sprintf("SFTPGO_HTTPD__BINDINGS__%v__CLIENT_IP_HEADER_DEPTH", idx), 32) if ok { binding.ClientIPHeaderDepth = int(clientIPHeaderDepth) isSet = true @@ -1772,7 +1786,7 @@ func getHTTPDBindingFromEnv(idx int) { //nolint:gocyclo binding := getDefaultHTTPBinding(idx) isSet := false - port, ok := lookupIntFromEnv(fmt.Sprintf("SFTPGO_HTTPD__BINDINGS__%v__PORT", idx), 0) + port, ok := lookupIntFromEnv(fmt.Sprintf("SFTPGO_HTTPD__BINDINGS__%v__PORT", idx), 32) if ok { binding.Port = int(port) isSet = true @@ -1814,7 +1828,7 @@ func getHTTPDBindingFromEnv(idx int) { //nolint:gocyclo isSet = true } - enabledLoginMethods, ok := lookupIntFromEnv(fmt.Sprintf("SFTPGO_HTTPD__BINDINGS__%v__ENABLED_LOGIN_METHODS", idx), 0) + enabledLoginMethods, ok := lookupIntFromEnv(fmt.Sprintf("SFTPGO_HTTPD__BINDINGS__%v__ENABLED_LOGIN_METHODS", idx), 32) if ok { binding.EnabledLoginMethods = int(enabledLoginMethods) isSet = true @@ -1832,13 +1846,13 @@ func getHTTPDBindingFromEnv(idx int) { //nolint:gocyclo isSet = true } - tlsVer, ok := lookupIntFromEnv(fmt.Sprintf("SFTPGO_HTTPD__BINDINGS__%v__MIN_TLS_VERSION", idx), 0) + tlsVer, ok := lookupIntFromEnv(fmt.Sprintf("SFTPGO_HTTPD__BINDINGS__%v__MIN_TLS_VERSION", idx), 32) if ok { binding.MinTLSVersion = int(tlsVer) isSet = true } - clientAuthType, ok := lookupIntFromEnv(fmt.Sprintf("SFTPGO_HTTPD__BINDINGS__%v__CLIENT_AUTH_TYPE", idx), 0) + clientAuthType, ok := lookupIntFromEnv(fmt.Sprintf("SFTPGO_HTTPD__BINDINGS__%v__CLIENT_AUTH_TYPE", idx), 32) if ok { binding.ClientAuthType = int(clientAuthType) isSet = true @@ -1860,7 +1874,7 @@ func getHTTPDBindingFromEnv(idx int) { //nolint:gocyclo isSet = true } - hideLoginURL, ok := lookupIntFromEnv(fmt.Sprintf("SFTPGO_HTTPD__BINDINGS__%v__HIDE_LOGIN_URL", idx), 0) + hideLoginURL, ok := lookupIntFromEnv(fmt.Sprintf("SFTPGO_HTTPD__BINDINGS__%v__HIDE_LOGIN_URL", idx), 32) if ok { binding.HideLoginURL = int(hideLoginURL) isSet = true @@ -1949,7 +1963,7 @@ func getCommandConfigsFromEnv(idx int) { cfg.Path = path } - timeout, ok := lookupIntFromEnv(fmt.Sprintf("SFTPGO_COMMAND__COMMANDS__%v__TIMEOUT", idx), 0) + timeout, ok := lookupIntFromEnv(fmt.Sprintf("SFTPGO_COMMAND__COMMANDS__%v__TIMEOUT", idx), 32) if ok { cfg.Timeout = int(timeout) } diff --git a/internal/config/config_test.go b/internal/config/config_test.go index ced41bd5..8c433569 100644 --- a/internal/config/config_test.go +++ b/internal/config/config_test.go @@ -1074,6 +1074,7 @@ func TestWebDAVBindingsFromEnv(t *testing.T) { os.Setenv("SFTPGO_WEBDAVD__BINDINGS__1__ENABLE_HTTPS", "0") os.Setenv("SFTPGO_WEBDAVD__BINDINGS__1__TLS_CIPHER_SUITES", "TLS_RSA_WITH_AES_128_CBC_SHA ") os.Setenv("SFTPGO_WEBDAVD__BINDINGS__1__TLS_PROTOCOLS", "http/1.1 ") + os.Setenv("SFTPGO_WEBDAVD__BINDINGS__1__PROXY_MODE", "1") os.Setenv("SFTPGO_WEBDAVD__BINDINGS__1__PROXY_ALLOWED", "192.168.10.1") os.Setenv("SFTPGO_WEBDAVD__BINDINGS__1__CLIENT_IP_PROXY_HEADER", "X-Forwarded-For") os.Setenv("SFTPGO_WEBDAVD__BINDINGS__1__CLIENT_IP_HEADER_DEPTH", "2") @@ -1093,6 +1094,7 @@ func TestWebDAVBindingsFromEnv(t *testing.T) { os.Unsetenv("SFTPGO_WEBDAVD__BINDINGS__1__ENABLE_HTTPS") os.Unsetenv("SFTPGO_WEBDAVD__BINDINGS__1__TLS_CIPHER_SUITES") os.Unsetenv("SFTPGO_WEBDAVD__BINDINGS__1__TLS_PROTOCOLS") + os.Unsetenv("SFTPGO_WEBDAVD__BINDINGS__1__PROXY_MODE") os.Unsetenv("SFTPGO_WEBDAVD__BINDINGS__1__PROXY_ALLOWED") os.Unsetenv("SFTPGO_WEBDAVD__BINDINGS__1__CLIENT_IP_PROXY_HEADER") os.Unsetenv("SFTPGO_WEBDAVD__BINDINGS__1__CLIENT_IP_HEADER_DEPTH") @@ -1117,6 +1119,7 @@ func TestWebDAVBindingsFromEnv(t *testing.T) { require.Equal(t, 12, bindings[0].MinTLSVersion) require.Len(t, bindings[0].TLSCipherSuites, 0) require.Len(t, bindings[0].Protocols, 0) + require.Equal(t, 0, bindings[0].ProxyMode) require.Empty(t, bindings[0].Prefix) require.Equal(t, 0, bindings[0].ClientIPHeaderDepth) require.False(t, bindings[0].DisableWWWAuthHeader) @@ -1129,6 +1132,7 @@ func TestWebDAVBindingsFromEnv(t *testing.T) { require.Equal(t, "TLS_RSA_WITH_AES_128_CBC_SHA", bindings[1].TLSCipherSuites[0]) require.Len(t, bindings[1].Protocols, 1) assert.Equal(t, "http/1.1", bindings[1].Protocols[0]) + require.Equal(t, 1, bindings[1].ProxyMode) require.Equal(t, "192.168.10.1", bindings[1].ProxyAllowed[0]) require.Equal(t, "X-Forwarded-For", bindings[1].ClientIPProxyHeader) require.Equal(t, 2, bindings[1].ClientIPHeaderDepth) @@ -1139,6 +1143,7 @@ func TestWebDAVBindingsFromEnv(t *testing.T) { require.True(t, bindings[2].EnableHTTPS) require.Equal(t, 13, bindings[2].MinTLSVersion) require.Equal(t, 1, bindings[2].ClientAuthType) + require.Equal(t, 0, bindings[2].ProxyMode) require.Nil(t, bindings[2].TLSCipherSuites) require.Equal(t, "/dav2", bindings[2].Prefix) require.Equal(t, "webdav.crt", bindings[2].CertificateFile) @@ -1173,6 +1178,7 @@ func TestHTTPDBindingsFromEnv(t *testing.T) { os.Setenv("SFTPGO_HTTPD__BINDINGS__2__CLIENT_AUTH_TYPE", "1") os.Setenv("SFTPGO_HTTPD__BINDINGS__2__TLS_CIPHER_SUITES", " TLS_AES_256_GCM_SHA384 , TLS_CHACHA20_POLY1305_SHA256") os.Setenv("SFTPGO_HTTPD__BINDINGS__2__TLS_PROTOCOLS", "h2, http/1.1") + os.Setenv("SFTPGO_HTTPD__BINDINGS__2__PROXY_MODE", "1") os.Setenv("SFTPGO_HTTPD__BINDINGS__2__PROXY_ALLOWED", " 192.168.9.1 , 172.16.25.0/24") os.Setenv("SFTPGO_HTTPD__BINDINGS__2__CLIENT_IP_PROXY_HEADER", "X-Real-IP") os.Setenv("SFTPGO_HTTPD__BINDINGS__2__CLIENT_IP_HEADER_DEPTH", "2") @@ -1237,6 +1243,7 @@ func TestHTTPDBindingsFromEnv(t *testing.T) { os.Unsetenv("SFTPGO_HTTPD__BINDINGS__2__CLIENT_AUTH_TYPE") os.Unsetenv("SFTPGO_HTTPD__BINDINGS__2__TLS_CIPHER_SUITES") os.Unsetenv("SFTPGO_HTTPD__BINDINGS__2__TLS_PROTOCOLS") + os.Unsetenv("SFTPGO_HTTPD__BINDINGS__2__PROXY_MODE") os.Unsetenv("SFTPGO_HTTPD__BINDINGS__2__PROXY_ALLOWED") os.Unsetenv("SFTPGO_HTTPD__BINDINGS__2__CLIENT_IP_PROXY_HEADER") os.Unsetenv("SFTPGO_HTTPD__BINDINGS__2__CLIENT_IP_HEADER_DEPTH") @@ -1294,6 +1301,7 @@ func TestHTTPDBindingsFromEnv(t *testing.T) { require.Equal(t, 0, bindings[0].EnabledLoginMethods) require.True(t, bindings[0].RenderOpenAPI) require.Len(t, bindings[0].TLSCipherSuites, 1) + require.Equal(t, 0, bindings[0].ProxyMode) require.Empty(t, bindings[0].OIDC.ConfigURL) require.Equal(t, "TLS_AES_128_GCM_SHA256", bindings[0].TLSCipherSuites[0]) require.Equal(t, 0, bindings[0].HideLoginURL) @@ -1320,6 +1328,7 @@ func TestHTTPDBindingsFromEnv(t *testing.T) { require.False(t, bindings[1].Security.Enabled) require.Equal(t, "Web Admin", bindings[1].Branding.WebAdmin.Name) require.Equal(t, "WebClient", bindings[1].Branding.WebClient.ShortName) + require.Equal(t, 0, bindings[1].ProxyMode) require.Equal(t, 0, bindings[1].ClientIPHeaderDepth) require.Equal(t, 9000, bindings[2].Port) require.Equal(t, "127.0.1.1", bindings[2].Address) @@ -1337,6 +1346,7 @@ func TestHTTPDBindingsFromEnv(t *testing.T) { require.Len(t, bindings[2].Protocols, 2) require.Equal(t, "h2", bindings[2].Protocols[0]) require.Equal(t, "http/1.1", bindings[2].Protocols[1]) + require.Equal(t, 1, bindings[2].ProxyMode) require.Len(t, bindings[2].ProxyAllowed, 2) require.Equal(t, "192.168.9.1", bindings[2].ProxyAllowed[0]) require.Equal(t, "172.16.25.0/24", bindings[2].ProxyAllowed[1]) diff --git a/internal/httpd/httpd.go b/internal/httpd/httpd.go index cefabb3b..6872dc69 100644 --- a/internal/httpd/httpd.go +++ b/internal/httpd/httpd.go @@ -584,6 +584,9 @@ type Binding struct { TLSCipherSuites []string `json:"tls_cipher_suites" mapstructure:"tls_cipher_suites"` // HTTP protocols in preference order. Supported values: http/1.1, h2 Protocols []string `json:"tls_protocols" mapstructure:"tls_protocols"` + // Defines whether to use the common proxy protocol configuration or the + // binding-specific proxy header configuration. + ProxyMode int `json:"proxy_mode" mapstructure:"proxy_mode"` // List of IP addresses and IP ranges allowed to set client IP proxy headers and // X-Forwarded-Proto header. ProxyAllowed []string `json:"proxy_allowed" mapstructure:"proxy_allowed"` @@ -754,6 +757,13 @@ func (b *Binding) isMutualTLSEnabled() bool { return b.ClientAuthType == 1 } +func (b *Binding) listenerWrapper() func(net.Listener) (net.Listener, error) { + if b.ProxyMode == 1 { + return common.Config.GetProxyListener + } + return nil +} + type defenderStatus struct { IsActive bool `json:"is_active"` } diff --git a/internal/httpd/internal_test.go b/internal/httpd/internal_test.go index aee400f1..5e4f5950 100644 --- a/internal/httpd/internal_test.go +++ b/internal/httpd/internal_test.go @@ -2199,6 +2199,15 @@ func TestAllowedProxyUnixDomainSocket(t *testing.T) { } } +func TestProxyListenerWrapper(t *testing.T) { + b := Binding{ + ProxyMode: 0, + } + require.Nil(t, b.listenerWrapper()) + b.ProxyMode = 1 + require.NotNil(t, b.listenerWrapper()) +} + func TestProxyHeaders(t *testing.T) { username := "adminTest" password := "testPwd" diff --git a/internal/httpd/server.go b/internal/httpd/server.go index 22de7e63..839239c8 100644 --- a/internal/httpd/server.go +++ b/internal/httpd/server.go @@ -128,9 +128,11 @@ func (s *httpdServer) listenAndServe() error { httpServer.TLSConfig.ClientAuth = tls.RequireAndVerifyClientCert httpServer.TLSConfig.VerifyConnection = s.verifyTLSConnection } - return util.HTTPListenAndServe(httpServer, s.binding.Address, s.binding.Port, true, logSender) + return util.HTTPListenAndServe(httpServer, s.binding.Address, s.binding.Port, true, + s.binding.listenerWrapper(), logSender) } - return util.HTTPListenAndServe(httpServer, s.binding.Address, s.binding.Port, false, logSender) + return util.HTTPListenAndServe(httpServer, s.binding.Address, s.binding.Port, false, + s.binding.listenerWrapper(), logSender) } func (s *httpdServer) verifyTLSConnection(state tls.ConnectionState) error { diff --git a/internal/telemetry/telemetry.go b/internal/telemetry/telemetry.go index dad7e869..233636aa 100644 --- a/internal/telemetry/telemetry.go +++ b/internal/telemetry/telemetry.go @@ -135,9 +135,9 @@ func (c Conf) Initialize(configDir string) error { } logger.Debug(logSender, "", "configured TLS cipher suites: %v", config.CipherSuites) httpServer.TLSConfig = config - return util.HTTPListenAndServe(httpServer, c.BindAddress, c.BindPort, true, logSender) + return util.HTTPListenAndServe(httpServer, c.BindAddress, c.BindPort, true, nil, logSender) } - return util.HTTPListenAndServe(httpServer, c.BindAddress, c.BindPort, false, logSender) + return util.HTTPListenAndServe(httpServer, c.BindAddress, c.BindPort, false, nil, logSender) } // ReloadCertificateMgr reloads the certificate manager diff --git a/internal/util/util.go b/internal/util/util.go index 03e68914..3de95b1f 100644 --- a/internal/util/util.go +++ b/internal/util/util.go @@ -589,7 +589,10 @@ func GenerateUniqueID() string { // HTTPListenAndServe is a wrapper for ListenAndServe that support both tcp // and Unix-domain sockets -func HTTPListenAndServe(srv *http.Server, address string, port int, isTLS bool, logSender string) error { +func HTTPListenAndServe(srv *http.Server, address string, port int, isTLS bool, + listenerWrapper func(net.Listener) (net.Listener, error), + logSender string, +) error { var listener net.Listener var err error @@ -617,7 +620,12 @@ func HTTPListenAndServe(srv *http.Server, address string, port int, isTLS bool, if err != nil { return err } - + if listenerWrapper != nil { + listener, err = listenerWrapper(listener) + if err != nil { + return err + } + } logger.Info(logSender, "", "server listener registered, address: %s TLS enabled: %t", listener.Addr().String(), isTLS) defer listener.Close() diff --git a/internal/webdavd/internal_test.go b/internal/webdavd/internal_test.go index 61e8a31b..57cfeb93 100644 --- a/internal/webdavd/internal_test.go +++ b/internal/webdavd/internal_test.go @@ -378,6 +378,15 @@ func TestAllowedProxyUnixDomainSocket(t *testing.T) { } } +func TestProxyListenerWrapper(t *testing.T) { + b := Binding{ + ProxyMode: 0, + } + require.Nil(t, b.listenerWrapper()) + b.ProxyMode = 1 + require.NotNil(t, b.listenerWrapper()) +} + func TestRemoteAddress(t *testing.T) { remoteAddr1 := "100.100.100.100" remoteAddr2 := "172.172.172.172" diff --git a/internal/webdavd/server.go b/internal/webdavd/server.go index a1b9248e..4cd849fe 100644 --- a/internal/webdavd/server.go +++ b/internal/webdavd/server.go @@ -99,11 +99,13 @@ func (s *webDavServer) listenAndServe(compressor *middleware.Compressor) error { httpServer.TLSConfig.ClientAuth = tls.VerifyClientCertIfGiven } } - return util.HTTPListenAndServe(httpServer, s.binding.Address, s.binding.Port, true, logSender) + return util.HTTPListenAndServe(httpServer, s.binding.Address, s.binding.Port, true, + s.binding.listenerWrapper(), logSender) } s.binding.EnableHTTPS = false serviceStatus.Bindings = append(serviceStatus.Bindings, s.binding) - return util.HTTPListenAndServe(httpServer, s.binding.Address, s.binding.Port, false, logSender) + return util.HTTPListenAndServe(httpServer, s.binding.Address, s.binding.Port, false, + s.binding.listenerWrapper(), logSender) } func (s *webDavServer) verifyTLSConnection(state tls.ConnectionState) error { diff --git a/internal/webdavd/webdavd.go b/internal/webdavd/webdavd.go index 62d37568..bd59f759 100644 --- a/internal/webdavd/webdavd.go +++ b/internal/webdavd/webdavd.go @@ -138,6 +138,9 @@ type Binding struct { // Prefix for WebDAV resources, if empty WebDAV resources will be available at the // root ("/") URI. If defined it must be an absolute URI. Prefix string `json:"prefix" mapstructure:"prefix"` + // Defines whether to use the common proxy protocol configuration or the + // binding-specific proxy header configuration. + ProxyMode int `json:"proxy_mode" mapstructure:"proxy_mode"` // List of IP addresses and IP ranges allowed to set client IP proxy headers ProxyAllowed []string `json:"proxy_allowed" mapstructure:"proxy_allowed"` // Allowed client IP proxy header such as "X-Forwarded-For", "X-Real-IP" @@ -181,6 +184,13 @@ func (b *Binding) IsValid() bool { return b.Port > 0 } +func (b *Binding) listenerWrapper() func(net.Listener) (net.Listener, error) { + if b.ProxyMode == 1 { + return common.Config.GetProxyListener + } + return nil +} + // Configuration defines the configuration for the WevDAV server type Configuration struct { // Addresses and ports to bind to diff --git a/sftpgo.json b/sftpgo.json index d66d8951..74d2dbef 100644 --- a/sftpgo.json +++ b/sftpgo.json @@ -163,6 +163,7 @@ "tls_cipher_suites": [], "tls_protocols": [], "prefix": "", + "proxy_mode": 0, "proxy_allowed": [], "client_ip_proxy_header": "", "client_ip_header_depth": 0, @@ -275,6 +276,7 @@ "client_auth_type": 0, "tls_cipher_suites": [], "tls_protocols": [], + "proxy_mode": 0, "proxy_allowed": [], "client_ip_proxy_header": "", "client_ip_header_depth": 0,