diff --git a/config/config.go b/config/config.go index 0ef15238..4d594e0f 100644 --- a/config/config.go +++ b/config/config.go @@ -53,6 +53,7 @@ var ( ApplyProxyConfig: true, TLSMode: 0, ForcePassiveIP: "", + PassiveIPOverrides: nil, ClientAuthType: 0, TLSCipherSuites: nil, PassiveConnectionsSecurity: 0, @@ -852,6 +853,31 @@ func getSFTPDBindindFromEnv(idx int) { } } +func getFTPDPassiveIPOverridesFromEnv(idx int) []ftpd.PassiveIPOverride { + var overrides []ftpd.PassiveIPOverride + + for subIdx := 0; subIdx < 10; subIdx++ { + var override ftpd.PassiveIPOverride + + ip, ok := os.LookupEnv(fmt.Sprintf("SFTPGO_FTPD__BINDINGS__%v__PASSIVE_IP_OVERRIDES__%v__IP", idx, subIdx)) + if ok { + override.IP = ip + } + + networks, ok := lookupStringListFromEnv(fmt.Sprintf("SFTPGO_FTPD__BINDINGS__%v__PASSIVE_IP_OVERRIDES__%v__NETWORKS", + idx, subIdx)) + if ok { + override.Networks = networks + } + + if len(override.Networks) > 0 { + overrides = append(overrides, override) + } + } + + return overrides +} + func getFTPDBindingFromEnv(idx int) { binding := ftpd.Binding{ ApplyProxyConfig: true, @@ -892,6 +918,12 @@ func getFTPDBindingFromEnv(idx int) { isSet = true } + passiveIPOverrides := getFTPDPassiveIPOverridesFromEnv(idx) + if len(passiveIPOverrides) > 0 { + binding.PassiveIPOverrides = passiveIPOverrides + isSet = true + } + clientAuthType, ok := lookupIntFromEnv(fmt.Sprintf("SFTPGO_FTPD__BINDINGS__%v__CLIENT_AUTH_TYPE", idx)) if ok { binding.ClientAuthType = int(clientAuthType) diff --git a/config/config_test.go b/config/config_test.go index da2aa2cc..95f9799b 100644 --- a/config/config_test.go +++ b/config/config_test.go @@ -628,12 +628,15 @@ func TestFTPDBindingsFromEnv(t *testing.T) { os.Setenv("SFTPGO_FTPD__BINDINGS__0__APPLY_PROXY_CONFIG", "f") os.Setenv("SFTPGO_FTPD__BINDINGS__0__TLS_MODE", "2") os.Setenv("SFTPGO_FTPD__BINDINGS__0__FORCE_PASSIVE_IP", "127.0.1.2") + os.Setenv("SFTPGO_FTPD__BINDINGS__0__PASSIVE_IP_OVERRIDES__0__IP", "172.16.1.1") os.Setenv("SFTPGO_FTPD__BINDINGS__0__TLS_CIPHER_SUITES", "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256,TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256") os.Setenv("SFTPGO_FTPD__BINDINGS__0__PASSIVE_CONNECTIONS_SECURITY", "1") os.Setenv("SFTPGO_FTPD__BINDINGS__9__ADDRESS", "127.0.1.1") os.Setenv("SFTPGO_FTPD__BINDINGS__9__PORT", "2203") os.Setenv("SFTPGO_FTPD__BINDINGS__9__TLS_MODE", "1") os.Setenv("SFTPGO_FTPD__BINDINGS__9__FORCE_PASSIVE_IP", "127.0.1.1") + os.Setenv("SFTPGO_FTPD__BINDINGS__9__PASSIVE_IP_OVERRIDES__3__IP", "192.168.1.1") + os.Setenv("SFTPGO_FTPD__BINDINGS__9__PASSIVE_IP_OVERRIDES__3__NETWORKS", "192.168.1.0/24, 192.168.3.0/25") os.Setenv("SFTPGO_FTPD__BINDINGS__9__CLIENT_AUTH_TYPE", "2") os.Setenv("SFTPGO_FTPD__BINDINGS__9__DEBUG", "1") os.Setenv("SFTPGO_FTPD__BINDINGS__9__ACTIVE_CONNECTIONS_SECURITY", "1") @@ -644,12 +647,15 @@ func TestFTPDBindingsFromEnv(t *testing.T) { os.Unsetenv("SFTPGO_FTPD__BINDINGS__0__APPLY_PROXY_CONFIG") os.Unsetenv("SFTPGO_FTPD__BINDINGS__0__TLS_MODE") os.Unsetenv("SFTPGO_FTPD__BINDINGS__0__FORCE_PASSIVE_IP") + os.Unsetenv("SFTPGO_FTPD__BINDINGS__0__PASSIVE_IP_OVERRIDES__0__IP") os.Unsetenv("SFTPGO_FTPD__BINDINGS__0__TLS_CIPHER_SUITES") os.Unsetenv("SFTPGO_FTPD__BINDINGS__0__ACTIVE_CONNECTIONS_SECURITY") os.Unsetenv("SFTPGO_FTPD__BINDINGS__9__ADDRESS") os.Unsetenv("SFTPGO_FTPD__BINDINGS__9__PORT") os.Unsetenv("SFTPGO_FTPD__BINDINGS__9__TLS_MODE") os.Unsetenv("SFTPGO_FTPD__BINDINGS__9__FORCE_PASSIVE_IP") + os.Unsetenv("SFTPGO_FTPD__BINDINGS__9__PASSIVE_IP_OVERRIDES__3__IP") + os.Unsetenv("SFTPGO_FTPD__BINDINGS__9__PASSIVE_IP_OVERRIDES__3__NETWORKS") os.Unsetenv("SFTPGO_FTPD__BINDINGS__9__CLIENT_AUTH_TYPE") os.Unsetenv("SFTPGO_FTPD__BINDINGS__9__DEBUG") os.Unsetenv("SFTPGO_FTPD__BINDINGS__9__ACTIVE_CONNECTIONS_SECURITY") @@ -665,6 +671,7 @@ func TestFTPDBindingsFromEnv(t *testing.T) { require.False(t, bindings[0].ApplyProxyConfig) require.Equal(t, 2, bindings[0].TLSMode) require.Equal(t, "127.0.1.2", bindings[0].ForcePassiveIP) + require.Len(t, bindings[0].PassiveIPOverrides, 0) require.Equal(t, 0, bindings[0].ClientAuthType) require.Len(t, bindings[0].TLSCipherSuites, 2) require.Equal(t, "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256", bindings[0].TLSCipherSuites[0]) @@ -677,6 +684,11 @@ func TestFTPDBindingsFromEnv(t *testing.T) { require.True(t, bindings[1].ApplyProxyConfig) // default value require.Equal(t, 1, bindings[1].TLSMode) require.Equal(t, "127.0.1.1", bindings[1].ForcePassiveIP) + require.Len(t, bindings[1].PassiveIPOverrides, 1) + require.Equal(t, "192.168.1.1", bindings[1].PassiveIPOverrides[0].IP) + require.Len(t, bindings[1].PassiveIPOverrides[0].Networks, 2) + require.Equal(t, "192.168.1.0/24", bindings[1].PassiveIPOverrides[0].Networks[0]) + require.Equal(t, "192.168.3.0/25", bindings[1].PassiveIPOverrides[0].Networks[1]) require.Equal(t, 2, bindings[1].ClientAuthType) require.Nil(t, bindings[1].TLSCipherSuites) require.Equal(t, 0, bindings[1].PassiveConnectionsSecurity) diff --git a/docs/full-configuration.md b/docs/full-configuration.md index 95aefa8e..86da1ff6 100644 --- a/docs/full-configuration.md +++ b/docs/full-configuration.md @@ -121,6 +121,9 @@ The configuration file contains the following sections: - `apply_proxy_config`, boolean. If enabled the common proxy configuration, if any, will be applied. Please note that we expect the proxy header on control and data connections. Default `true`. - `tls_mode`, integer. 0 means accept both cleartext and encrypted sessions. 1 means TLS is required for both control and data connection. 2 means implicit TLS. Do not enable this blindly, please check that a proper TLS config is in place if you set `tls_mode` is different from 0. - `force_passive_ip`, ip address. External IP address to expose for passive connections. Leavy empty to autodetect. If not empty, it must be a valid IPv4 address. Defaut: "". + - `passive_ip_overrides`, list of struct that allows to return a different passive ip based on the client IP address. Each struct has the following fields: + - `networks`, list of strings. Each string must define a network in CIDR notation, for example 192.168.1.0/24. + - `ip`, string. Passive IP to return if the client IP address belongs to the defined networks. Empty means autodetect. - `client_auth_type`, integer. Set to `1` to require a client certificate and verify it. Set to `2` to request a client certificate during the TLS handshake and verify it if given, in this mode the client is allowed not to send a certificate. At least one certification authority must be defined in order to verify client certificates. If no certification authority is defined, this setting is ignored. Default: 0. - `tls_cipher_suites`, list of strings. List of supported cipher suites for TLS version 1.2. If empty, a default list of secure cipher suites is used, with a preference order based on hardware performance. Note that TLS 1.3 ciphersuites are not configurable. The supported ciphersuites names are defined [here](https://github.com/golang/go/blob/master/src/crypto/tls/cipher_suites.go#L52). Any invalid name will be silently ignored. The order matters, the ciphers listed first will be the preferred ones. Default: empty. - `passive_connections_security`, integer. Defines the security checks for passive data connections. Set to `0` to require matching peer IP addresses of control and data connection. Set to `1` to disable any checks. Please note that if you run the FTP service behind a proxy you must enable the proxy protocol for control and data connections. Default: `0`. diff --git a/ftpd/ftpd.go b/ftpd/ftpd.go index 07493d57..7639afcd 100644 --- a/ftpd/ftpd.go +++ b/ftpd/ftpd.go @@ -2,9 +2,11 @@ package ftpd import ( + "errors" "fmt" "net" "path/filepath" + "strings" ftpserver "github.com/fclairamb/ftpserverlib" @@ -22,6 +24,14 @@ var ( serviceStatus ServiceStatus ) +// PassiveIPOverride defines an exception for the configured passive IP +type PassiveIPOverride struct { + Networks []string `json:"networks" mapstructure:"networks"` + // if empty the local address will be returned + IP string `json:"ip" mapstructure:"ip"` + parsedNetworks []func(net.IP) bool +} + // Binding defines the configuration for a network listener type Binding struct { // The address to listen on. A blank value means listen on all available network interfaces. @@ -35,6 +45,9 @@ type Binding struct { TLSMode int `json:"tls_mode" mapstructure:"tls_mode"` // External IP address to expose for passive connections. ForcePassiveIP string `json:"force_passive_ip" mapstructure:"force_passive_ip"` + // PassiveIPOverrides allows to define different IP addresses to expose for passive connections + // based on the client IP address + PassiveIPOverrides []PassiveIPOverride `json:"passive_ip_overrides" mapstructure:"passive_ip_overrides"` // Set to 1 to require client certificate authentication. // Set to 2 to require a client certificate and verfify it if given. In this mode // the client is allowed not to send a certificate. @@ -99,19 +112,61 @@ func (b *Binding) checkSecuritySettings() error { func (b *Binding) checkPassiveIP() error { if b.ForcePassiveIP != "" { - ip := net.ParseIP(b.ForcePassiveIP) - if ip == nil { - return fmt.Errorf("the provided passive IP %#v is not valid", b.ForcePassiveIP) + ip, err := parsePassiveIP(b.ForcePassiveIP) + if err != nil { + return err } - ip = ip.To4() - if ip == nil { - return fmt.Errorf("the provided passive IP %#v is not a valid IPv4 address", b.ForcePassiveIP) + b.ForcePassiveIP = ip + } + for idx, passiveOverride := range b.PassiveIPOverrides { + var ip string + + if passiveOverride.IP != "" { + var err error + ip, err = parsePassiveIP(passiveOverride.IP) + if err != nil { + return err + } } - b.ForcePassiveIP = ip.String() + if len(passiveOverride.Networks) == 0 { + return errors.New("passive IP networks override cannot be empty") + } + checkFuncs, err := util.ParseAllowedIPAndRanges(passiveOverride.Networks) + if err != nil { + return fmt.Errorf("invalid passive IP networks override %+v: %w", passiveOverride.Networks, err) + } + b.PassiveIPOverrides[idx].IP = ip + b.PassiveIPOverrides[idx].parsedNetworks = checkFuncs } return nil } +func (b *Binding) getPassiveIP(cc ftpserver.ClientContext) string { + if b.ForcePassiveIP != "" { + return b.ForcePassiveIP + } + return strings.Split(cc.LocalAddr().String(), ":")[0] +} + +func (b *Binding) passiveIPResolver(cc ftpserver.ClientContext) (string, error) { + if len(b.PassiveIPOverrides) > 0 { + clientIP := net.ParseIP(util.GetIPFromRemoteAddress(cc.RemoteAddr().String())) + if clientIP != nil { + for _, override := range b.PassiveIPOverrides { + for _, fn := range override.parsedNetworks { + if fn(clientIP) { + if override.IP == "" { + return strings.Split(cc.LocalAddr().String(), ":")[0], nil + } + return override.IP, nil + } + } + } + } + } + return b.getPassiveIP(cc), nil +} + // HasProxy returns true if the proxy protocol is active for this binding func (b *Binding) HasProxy() bool { return b.ApplyProxyConfig && common.Config.ProxyProtocol > 0 @@ -268,6 +323,18 @@ func GetStatus() ServiceStatus { return serviceStatus } +func parsePassiveIP(passiveIP string) (string, error) { + ip := net.ParseIP(passiveIP) + if ip == nil { + return "", fmt.Errorf("the provided passive IP %#v is not valid", passiveIP) + } + ip = ip.To4() + if ip == nil { + return "", fmt.Errorf("the provided passive IP %#v is not a valid IPv4 address", passiveIP) + } + return ip.String(), nil +} + func getConfigPath(name, configDir string) string { if !util.IsFileInputValid(name) { return "" diff --git a/ftpd/internal_test.go b/ftpd/internal_test.go index 014c6afe..5f2ca9b2 100644 --- a/ftpd/internal_test.go +++ b/ftpd/internal_test.go @@ -254,6 +254,8 @@ xr5cb9VBRBtB9aOKVfuRhpatAfS2Pzm2Htae9lFn7slGPUmu2hkjDw== type mockFTPClientContext struct { lastDataChannel ftpserver.DataChannel + remoteIP string + localIP string } func (cc mockFTPClientContext) Path() string { @@ -271,11 +273,19 @@ func (cc mockFTPClientContext) ID() uint32 { } func (cc mockFTPClientContext) RemoteAddr() net.Addr { - return &net.IPAddr{IP: []byte("127.0.0.1")} + ip := "127.0.0.1" + if cc.remoteIP != "" { + ip = cc.remoteIP + } + return &net.IPAddr{IP: net.ParseIP(ip)} } func (cc mockFTPClientContext) LocalAddr() net.Addr { - return &net.IPAddr{IP: []byte("127.0.0.1")} + ip := "127.0.0.1" + if cc.localIP != "" { + ip = cc.localIP + } + return &net.IPAddr{IP: net.ParseIP(ip)} } func (cc mockFTPClientContext) GetClientVersion() string { @@ -924,3 +934,69 @@ func TestCiphers(t *testing.T) { require.Len(t, b.ciphers, 2) require.Equal(t, []uint16{tls.TLS_AES_128_GCM_SHA256, tls.TLS_AES_256_GCM_SHA384}, b.ciphers) } + +func TestPassiveIPResolver(t *testing.T) { + b := Binding{ + PassiveIPOverrides: []PassiveIPOverride{ + {}, + }, + } + err := b.checkPassiveIP() + assert.Error(t, err) + assert.Contains(t, err.Error(), "passive IP networks override cannot be empty") + b = Binding{ + PassiveIPOverrides: []PassiveIPOverride{ + { + IP: "invalid ip", + }, + }, + } + err = b.checkPassiveIP() + assert.Error(t, err) + assert.Contains(t, err.Error(), "is not valid") + + b = Binding{ + PassiveIPOverrides: []PassiveIPOverride{ + { + IP: "192.168.1.1", + Networks: []string{"192.168.1.0/24", "invalid cidr"}, + }, + }, + } + err = b.checkPassiveIP() + assert.Error(t, err) + assert.Contains(t, err.Error(), "invalid passive IP networks override") + b = Binding{ + ForcePassiveIP: "192.168.2.1", + PassiveIPOverrides: []PassiveIPOverride{ + { + IP: "::ffff:192.168.1.1", + Networks: []string{"192.168.1.0/24"}, + }, + }, + } + err = b.checkPassiveIP() + assert.NoError(t, err) + assert.Equal(t, "192.168.1.1", b.PassiveIPOverrides[0].IP) + require.Len(t, b.PassiveIPOverrides[0].parsedNetworks, 1) + ip := net.ParseIP("192.168.1.2") + assert.True(t, b.PassiveIPOverrides[0].parsedNetworks[0](ip)) + ip = net.ParseIP("192.168.0.2") + assert.False(t, b.PassiveIPOverrides[0].parsedNetworks[0](ip)) + + mockCC := mockFTPClientContext{ + remoteIP: "192.168.1.10", + localIP: "192.168.1.3", + } + passiveIP, err := b.passiveIPResolver(mockCC) + assert.NoError(t, err) + assert.Equal(t, "192.168.1.1", passiveIP) + b.PassiveIPOverrides[0].IP = "" + passiveIP, err = b.passiveIPResolver(mockCC) + assert.NoError(t, err) + assert.Equal(t, "192.168.1.3", passiveIP) + mockCC.remoteIP = "172.16.2.3" + passiveIP, err = b.passiveIPResolver(mockCC) + assert.NoError(t, err) + assert.Equal(t, b.ForcePassiveIP, passiveIP) +} diff --git a/ftpd/server.go b/ftpd/server.go index 69ab6fa0..4941512b 100644 --- a/ftpd/server.go +++ b/ftpd/server.go @@ -124,7 +124,7 @@ func (s *Server) GetSettings() (*ftpserver.Settings, error) { return &ftpserver.Settings{ Listener: ftpListener, ListenAddr: s.binding.GetAddress(), - PublicHost: s.binding.ForcePassiveIP, + PublicIPResolver: s.binding.passiveIPResolver, PassiveTransferPortRange: portRange, ActiveTransferPortNon20: s.config.ActiveTransfersPortNon20, IdleTimeout: -1, diff --git a/go.mod b/go.mod index 60b87fce..bbb61732 100644 --- a/go.mod +++ b/go.mod @@ -7,7 +7,7 @@ require ( github.com/Azure/azure-storage-blob-go v0.14.0 github.com/GehirnInc/crypt v0.0.0-20200316065508-bb7000b8a962 github.com/alexedwards/argon2id v0.0.0-20210511081203-7d35d68092b8 - github.com/aws/aws-sdk-go v1.42.9 + github.com/aws/aws-sdk-go v1.42.12 github.com/cockroachdb/cockroach-go/v2 v2.2.4 github.com/eikenb/pipeat v0.0.0-20210603033007-44fc3ffce52b github.com/fclairamb/ftpserverlib v0.16.0 @@ -52,8 +52,8 @@ require ( go.uber.org/automaxprocs v1.4.0 gocloud.dev v0.24.0 golang.org/x/crypto v0.0.0-20211117183948-ae814b36b871 - golang.org/x/net v0.0.0-20211118161319-6a13c67c3ce4 - golang.org/x/sys v0.0.0-20211117180635-dee7805ff2e1 + golang.org/x/net v0.0.0-20211123203042-d83791d6bcd9 + golang.org/x/sys v0.0.0-20211124211545-fe61309f8881 golang.org/x/time v0.0.0-20211116232009-f0f3c7e86c11 google.golang.org/api v0.60.0 google.golang.org/grpc v1.42.0 @@ -101,7 +101,7 @@ require ( github.com/lestrrat-go/option v1.0.0 // indirect github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect github.com/magiconair/properties v1.8.5 // indirect - github.com/mattn/go-colorable v0.1.11 // indirect + github.com/mattn/go-colorable v0.1.12 // indirect github.com/mattn/go-ieproxy v0.0.1 // indirect github.com/mattn/go-isatty v0.0.14 // indirect github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect @@ -139,5 +139,5 @@ replace ( github.com/fclairamb/ftpserverlib => github.com/drakkan/ftpserverlib v0.0.0-20211107071448-34ff70e85dfb github.com/jlaffaye/ftp => github.com/drakkan/ftp v0.0.0-20201114075148-9b9adce499a9 golang.org/x/crypto => github.com/drakkan/crypto v0.0.0-20211120085116-d3e2208cd0bd - golang.org/x/net => github.com/drakkan/net v0.0.0-20211120084140-32033f6a21da + golang.org/x/net => github.com/drakkan/net v0.0.0-20211125114103-f7adc41924ea ) diff --git a/go.sum b/go.sum index 39766acf..3a64bc53 100644 --- a/go.sum +++ b/go.sum @@ -137,8 +137,8 @@ github.com/aws/aws-sdk-go v1.15.27/go.mod h1:mFuSZ37Z9YOHbQEwBWztmVzqXrEkub65tZo github.com/aws/aws-sdk-go v1.37.0/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2zKMmprdro= github.com/aws/aws-sdk-go v1.38.68/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2zKMmprdro= github.com/aws/aws-sdk-go v1.40.34/go.mod h1:585smgzpB/KqRA+K3y/NL/oYRqQvpNJYvLm+LY1U59Q= -github.com/aws/aws-sdk-go v1.42.9 h1:8ptAGgA+uC2TUbdvUeOVSfBocIZvGE2NKiLxkAcn1GA= -github.com/aws/aws-sdk-go v1.42.9/go.mod h1:585smgzpB/KqRA+K3y/NL/oYRqQvpNJYvLm+LY1U59Q= +github.com/aws/aws-sdk-go v1.42.12 h1:zVrAgi3/HuMPygZknc+f2KAHcn+Zuq767857hnHBMPA= +github.com/aws/aws-sdk-go v1.42.12/go.mod h1:585smgzpB/KqRA+K3y/NL/oYRqQvpNJYvLm+LY1U59Q= github.com/aws/aws-sdk-go-v2 v1.7.0/go.mod h1:tb9wi5s61kTDA5qCkcDbt3KRVV74GGslQkl/DRdX/P4= github.com/aws/aws-sdk-go-v2 v1.9.0/go.mod h1:cK/D0BBs0b/oWPIcX/Z/obahJK1TT7IPVjy53i/mX/4= github.com/aws/aws-sdk-go-v2/config v1.7.0/go.mod h1:w9+nMZ7soXCe5nT46Ri354SNhXDQ6v+V5wqDjnZE+GY= @@ -224,8 +224,8 @@ github.com/drakkan/ftp v0.0.0-20201114075148-9b9adce499a9 h1:LPH1dEblAOO/LoG7yHP github.com/drakkan/ftp v0.0.0-20201114075148-9b9adce499a9/go.mod h1:2lmrmq866uF2tnje75wQHzmPXhmSWUt7Gyx2vgK1RCU= github.com/drakkan/ftpserverlib v0.0.0-20211107071448-34ff70e85dfb h1:cT/w4XStm7m022JgVqmrXZLcZ4UjoUER1VW5/5gd6ec= github.com/drakkan/ftpserverlib v0.0.0-20211107071448-34ff70e85dfb/go.mod h1:fBiQ19WDhtvKArMu0Pifg71k+0xqRYn+F0d9AsjkZw8= -github.com/drakkan/net v0.0.0-20211120084140-32033f6a21da h1:RAs8vjTnp+stqm/Ieq0n5akxtVzyAlq4aVoGCIM2JtQ= -github.com/drakkan/net v0.0.0-20211120084140-32033f6a21da/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +github.com/drakkan/net v0.0.0-20211125114103-f7adc41924ea h1:M5ZyIIYPyx1dhIQm1QbF4ofccWkjWFdSbJJ5qYziWek= +github.com/drakkan/net v0.0.0-20211125114103-f7adc41924ea/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= github.com/drakkan/pipeat v0.0.0-20210805162858-70e57fa8a639 h1:8tfGdb4kg/YCvAbIrsMazgoNtnqdOqQVDKW12uUCuuU= github.com/drakkan/pipeat v0.0.0-20210805162858-70e57fa8a639/go.mod h1:kltMsfRMTHSFdMbK66XdS8mfMW77+FZA1fGY1xYMF84= github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= @@ -591,8 +591,8 @@ github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVc github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= -github.com/mattn/go-colorable v0.1.11 h1:nQ+aFkoE2TMGc0b68U2OKSexC+eq46+XwZzWXHRmPYs= -github.com/mattn/go-colorable v0.1.11/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= +github.com/mattn/go-colorable v0.1.12 h1:jF+Du6AlPIjs2BiUiQlKOX0rt3SujHxPnksPKZbaA40= +github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= github.com/mattn/go-ieproxy v0.0.1 h1:qiyop7gCflfhwCzGyeT0gro3sF9AIg9HU98JORTkqfI= github.com/mattn/go-ieproxy v0.0.1/go.mod h1:pYabZ6IHcRpFh7vIaLfK7rdcWgFEb3SFJ6/gNWuh88E= github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= @@ -985,8 +985,8 @@ golang.org/x/sys v0.0.0-20210917161153-d61c044b1678/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211013075003-97ac67df715c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211117180635-dee7805ff2e1 h1:kwrAHlwJ0DUBZwQ238v+Uod/3eZ8B2K5rYsUHBQvzmI= -golang.org/x/sys v0.0.0-20211117180635-dee7805ff2e1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211124211545-fe61309f8881 h1:TyHqChC80pFkXWraUUf6RuB5IqFdQieMLwwCJokV2pc= +golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1 h1:v+OssWQX+hTHEmOBgwxdZxK4zHq3yOs8F9J7mk0PY8E= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= diff --git a/sftpgo.json b/sftpgo.json index fa98e4d5..b50c6236 100644 --- a/sftpgo.json +++ b/sftpgo.json @@ -86,6 +86,7 @@ "apply_proxy_config": true, "tls_mode": 0, "force_passive_ip": "", + "passive_ip_overrides": [], "client_auth_type": 0, "tls_cipher_suites": [], "passive_connections_security": 0,