Browse Source

webdav: allow to disable the WWW-Authenticate header

Signed-off-by: Nicola Murino <[email protected]>
Nicola Murino 3 years ago
parent
commit
6bfdf941bc

+ 1 - 0
docs/full-configuration.md

@@ -182,6 +182,7 @@ The configuration file contains the following sections:
     - `proxy_allowed`, list of IP addresses and IP ranges allowed to set client IP proxy header such as `X-Forwarded-For`. Any client IP proxy headers, if set on requests from a connection address not in this list, will be silently ignored. Default: empty.
     - `client_ip_proxy_header`, string. Defines the allowed client IP proxy header such as `X-Forwarded-For`, `X-Real-IP` etc. Default: empty
     - `client_ip_header_depth`, integer. Some client IP headers such as `X-Forwarded-For` can contain multiple IP address, this setting define the position to trust starting from the right. For example if we have: `10.0.0.1,11.0.0.1,12.0.0.1,13.0.0.1` and the depth is `0`, SFTPGo will use `13.0.0.1` as client IP, if depth is `1`, `12.0.0.1` will be used and so on. Default: `0`.
+    - `disable_www_auth_header`, boolean. Set to `true` to not add the WWW-Authenticate header after an authentication failure, only the `401` status code will be sent. Default: `false`.
   - `certificate_file`, string. Certificate for WebDAV over HTTPS. This can be an absolute path or a path relative to the config dir.
   - `certificate_key_file`, string. Private key matching the above certificate. This can be an absolute path or a path relative to the config dir. A certificate and a private key are required to enable HTTPS connections. Certificate and key files can be reloaded on demand sending a `SIGHUP` signal on Unix based systems and a `paramchange` request to the running service on Windows.
   - `ca_certificates`, list of strings. Set of root certificate authorities to be used to verify client certificates.

+ 27 - 20
internal/config/config.go

@@ -81,18 +81,19 @@ var (
 		Debug:                      false,
 	}
 	defaultWebDAVDBinding = webdavd.Binding{
-		Address:             "",
-		Port:                0,
-		EnableHTTPS:         false,
-		CertificateFile:     "",
-		CertificateKeyFile:  "",
-		MinTLSVersion:       12,
-		ClientAuthType:      0,
-		TLSCipherSuites:     nil,
-		Prefix:              "",
-		ProxyAllowed:        nil,
-		ClientIPProxyHeader: "",
-		ClientIPHeaderDepth: 0,
+		Address:              "",
+		Port:                 0,
+		EnableHTTPS:          false,
+		CertificateFile:      "",
+		CertificateKeyFile:   "",
+		MinTLSVersion:        12,
+		ClientAuthType:       0,
+		TLSCipherSuites:      nil,
+		Prefix:               "",
+		ProxyAllowed:         nil,
+		ClientIPProxyHeader:  "",
+		ClientIPHeaderDepth:  0,
+		DisableWWWAuthHeader: false,
 	}
 	defaultHTTPDBinding = httpd.Binding{
 		Address:               "",
@@ -1193,21 +1194,21 @@ func getWebDAVDBindingFromEnv(idx int) {
 		isSet = true
 	}
 
-	certificateFile, ok := os.LookupEnv(fmt.Sprintf("SFTPGO_WEBDAVD__BINDINGS__%v__CERTIFICATE_FILE", idx))
+	enableHTTPS, ok := lookupBoolFromEnv(fmt.Sprintf("SFTPGO_WEBDAVD__BINDINGS__%v__ENABLE_HTTPS", idx))
 	if ok {
-		binding.CertificateFile = certificateFile
+		binding.EnableHTTPS = enableHTTPS
 		isSet = true
 	}
 
-	certificateKeyFile, ok := os.LookupEnv(fmt.Sprintf("SFTPGO_WEBDAVD__BINDINGS__%v__CERTIFICATE_KEY_FILE", idx))
+	certificateFile, ok := os.LookupEnv(fmt.Sprintf("SFTPGO_WEBDAVD__BINDINGS__%v__CERTIFICATE_FILE", idx))
 	if ok {
-		binding.CertificateKeyFile = certificateKeyFile
+		binding.CertificateFile = certificateFile
 		isSet = true
 	}
 
-	enableHTTPS, ok := lookupBoolFromEnv(fmt.Sprintf("SFTPGO_WEBDAVD__BINDINGS__%v__ENABLE_HTTPS", idx))
+	certificateKeyFile, ok := os.LookupEnv(fmt.Sprintf("SFTPGO_WEBDAVD__BINDINGS__%v__CERTIFICATE_KEY_FILE", idx))
 	if ok {
-		binding.EnableHTTPS = enableHTTPS
+		binding.CertificateKeyFile = certificateKeyFile
 		isSet = true
 	}
 
@@ -1229,13 +1230,19 @@ func getWebDAVDBindingFromEnv(idx int) {
 		isSet = true
 	}
 
+	prefix, ok := os.LookupEnv(fmt.Sprintf("SFTPGO_WEBDAVD__BINDINGS__%v__PREFIX", idx))
+	if ok {
+		binding.Prefix = prefix
+		isSet = true
+	}
+
 	if getWebDAVDBindingProxyConfigsFromEnv(idx, &binding) {
 		isSet = true
 	}
 
-	prefix, ok := os.LookupEnv(fmt.Sprintf("SFTPGO_WEBDAVD__BINDINGS__%v__PREFIX", idx))
+	disableWWWAuth, ok := lookupBoolFromEnv(fmt.Sprintf("SFTPGO_WEBDAVD__BINDINGS__%v__DISABLE_WWW_AUTH_HEADER", idx))
 	if ok {
-		binding.Prefix = prefix
+		binding.DisableWWWAuthHeader = disableWWWAuth
 		isSet = true
 	}
 

+ 5 - 0
internal/config/config_test.go

@@ -943,6 +943,7 @@ func TestWebDAVBindingsFromEnv(t *testing.T) {
 	os.Setenv("SFTPGO_WEBDAVD__BINDINGS__2__PREFIX", "/dav2")
 	os.Setenv("SFTPGO_WEBDAVD__BINDINGS__2__CERTIFICATE_FILE", "webdav.crt")
 	os.Setenv("SFTPGO_WEBDAVD__BINDINGS__2__CERTIFICATE_KEY_FILE", "webdav.key")
+	os.Setenv("SFTPGO_WEBDAVD__BINDINGS__2__DISABLE_WWW_AUTH_HEADER", "1")
 
 	t.Cleanup(func() {
 		os.Unsetenv("SFTPGO_WEBDAVD__BINDINGS__1__ADDRESS")
@@ -960,6 +961,7 @@ func TestWebDAVBindingsFromEnv(t *testing.T) {
 		os.Unsetenv("SFTPGO_WEBDAVD__BINDINGS__2__PREFIX")
 		os.Unsetenv("SFTPGO_WEBDAVD__BINDINGS__2__CERTIFICATE_FILE")
 		os.Unsetenv("SFTPGO_WEBDAVD__BINDINGS__2__CERTIFICATE_KEY_FILE")
+		os.Unsetenv("SFTPGO_WEBDAVD__BINDINGS__2__DISABLE_WWW_AUTH_HEADER")
 	})
 
 	err := config.LoadConfig(configDir, "")
@@ -973,6 +975,7 @@ func TestWebDAVBindingsFromEnv(t *testing.T) {
 	require.Len(t, bindings[0].TLSCipherSuites, 0)
 	require.Empty(t, bindings[0].Prefix)
 	require.Equal(t, 0, bindings[0].ClientIPHeaderDepth)
+	require.False(t, bindings[0].DisableWWWAuthHeader)
 	require.Equal(t, 8000, bindings[1].Port)
 	require.Equal(t, "127.0.0.1", bindings[1].Address)
 	require.False(t, bindings[1].EnableHTTPS)
@@ -984,6 +987,7 @@ func TestWebDAVBindingsFromEnv(t *testing.T) {
 	require.Equal(t, "X-Forwarded-For", bindings[1].ClientIPProxyHeader)
 	require.Equal(t, 2, bindings[1].ClientIPHeaderDepth)
 	require.Empty(t, bindings[1].Prefix)
+	require.False(t, bindings[1].DisableWWWAuthHeader)
 	require.Equal(t, 9000, bindings[2].Port)
 	require.Equal(t, "127.0.1.1", bindings[2].Address)
 	require.True(t, bindings[2].EnableHTTPS)
@@ -994,6 +998,7 @@ func TestWebDAVBindingsFromEnv(t *testing.T) {
 	require.Equal(t, "webdav.crt", bindings[2].CertificateFile)
 	require.Equal(t, "webdav.key", bindings[2].CertificateKeyFile)
 	require.Equal(t, 0, bindings[2].ClientIPHeaderDepth)
+	require.True(t, bindings[2].DisableWWWAuthHeader)
 }
 
 func TestHTTPDBindingsFromEnv(t *testing.T) {

+ 3 - 1
internal/webdavd/server.go

@@ -189,7 +189,9 @@ func (s *webDavServer) ServeHTTP(w http.ResponseWriter, r *http.Request) {
 	user, isCached, lockSystem, loginMethod, err := s.authenticate(r, ipAddr)
 	if err != nil {
 		updateLoginMetrics(&user, ipAddr, loginMethod, err)
-		w.Header().Set("WWW-Authenticate", "Basic realm=\"SFTPGo WebDAV\"")
+		if !s.binding.DisableWWWAuthHeader {
+			w.Header().Set("WWW-Authenticate", "Basic realm=\"SFTPGo WebDAV\"")
+		}
 		http.Error(w, fmt.Sprintf("Authentication error: %v", err), http.StatusUnauthorized)
 		return
 	}

+ 4 - 1
internal/webdavd/webdavd.go

@@ -122,7 +122,10 @@ type Binding struct {
 	// "10.0.0.1,11.0.0.1,12.0.0.1,13.0.0.1" and the depth is 0, SFTPGo will use "13.0.0.1"
 	// as client IP, if depth is 1, "12.0.0.1" will be used and so on
 	ClientIPHeaderDepth int `json:"client_ip_header_depth" mapstructure:"client_ip_header_depth"`
-	allowHeadersFrom    []func(net.IP) bool
+	// Do not add the WWW-Authenticate header after an authentication error,
+	// only the 401 status code will be sent
+	DisableWWWAuthHeader bool `json:"disable_www_auth_header" mapstructure:"disable_www_auth_header"`
+	allowHeadersFrom     []func(net.IP) bool
 }
 
 func (b *Binding) parseAllowedProxy() error {

+ 2 - 1
sftpgo.json

@@ -151,7 +151,8 @@
         "prefix": "",
         "proxy_allowed": [],
         "client_ip_proxy_header": "",
-        "client_ip_header_depth": 0
+        "client_ip_header_depth": 0,
+        "disable_www_auth_header": false
       }
     ],
     "certificate_file": "",