FTPD: allow to set different passive IPs based on the client's IP address

This commit is contained in:
Nicola Murino 2021-11-25 12:45:09 +01:00
parent 531cb5b5a1
commit 4652f9ede8
No known key found for this signature in database
GPG key ID: 2F1FB59433D5A8CB
9 changed files with 214 additions and 23 deletions

View file

@ -53,6 +53,7 @@ var (
ApplyProxyConfig: true, ApplyProxyConfig: true,
TLSMode: 0, TLSMode: 0,
ForcePassiveIP: "", ForcePassiveIP: "",
PassiveIPOverrides: nil,
ClientAuthType: 0, ClientAuthType: 0,
TLSCipherSuites: nil, TLSCipherSuites: nil,
PassiveConnectionsSecurity: 0, 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) { func getFTPDBindingFromEnv(idx int) {
binding := ftpd.Binding{ binding := ftpd.Binding{
ApplyProxyConfig: true, ApplyProxyConfig: true,
@ -892,6 +918,12 @@ func getFTPDBindingFromEnv(idx int) {
isSet = true 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)) clientAuthType, ok := lookupIntFromEnv(fmt.Sprintf("SFTPGO_FTPD__BINDINGS__%v__CLIENT_AUTH_TYPE", idx))
if ok { if ok {
binding.ClientAuthType = int(clientAuthType) binding.ClientAuthType = int(clientAuthType)

View file

@ -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__APPLY_PROXY_CONFIG", "f")
os.Setenv("SFTPGO_FTPD__BINDINGS__0__TLS_MODE", "2") 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__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__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__0__PASSIVE_CONNECTIONS_SECURITY", "1")
os.Setenv("SFTPGO_FTPD__BINDINGS__9__ADDRESS", "127.0.1.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__PORT", "2203")
os.Setenv("SFTPGO_FTPD__BINDINGS__9__TLS_MODE", "1") 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__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__CLIENT_AUTH_TYPE", "2")
os.Setenv("SFTPGO_FTPD__BINDINGS__9__DEBUG", "1") os.Setenv("SFTPGO_FTPD__BINDINGS__9__DEBUG", "1")
os.Setenv("SFTPGO_FTPD__BINDINGS__9__ACTIVE_CONNECTIONS_SECURITY", "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__APPLY_PROXY_CONFIG")
os.Unsetenv("SFTPGO_FTPD__BINDINGS__0__TLS_MODE") os.Unsetenv("SFTPGO_FTPD__BINDINGS__0__TLS_MODE")
os.Unsetenv("SFTPGO_FTPD__BINDINGS__0__FORCE_PASSIVE_IP") 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__TLS_CIPHER_SUITES")
os.Unsetenv("SFTPGO_FTPD__BINDINGS__0__ACTIVE_CONNECTIONS_SECURITY") os.Unsetenv("SFTPGO_FTPD__BINDINGS__0__ACTIVE_CONNECTIONS_SECURITY")
os.Unsetenv("SFTPGO_FTPD__BINDINGS__9__ADDRESS") os.Unsetenv("SFTPGO_FTPD__BINDINGS__9__ADDRESS")
os.Unsetenv("SFTPGO_FTPD__BINDINGS__9__PORT") os.Unsetenv("SFTPGO_FTPD__BINDINGS__9__PORT")
os.Unsetenv("SFTPGO_FTPD__BINDINGS__9__TLS_MODE") os.Unsetenv("SFTPGO_FTPD__BINDINGS__9__TLS_MODE")
os.Unsetenv("SFTPGO_FTPD__BINDINGS__9__FORCE_PASSIVE_IP") 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__CLIENT_AUTH_TYPE")
os.Unsetenv("SFTPGO_FTPD__BINDINGS__9__DEBUG") os.Unsetenv("SFTPGO_FTPD__BINDINGS__9__DEBUG")
os.Unsetenv("SFTPGO_FTPD__BINDINGS__9__ACTIVE_CONNECTIONS_SECURITY") 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.False(t, bindings[0].ApplyProxyConfig)
require.Equal(t, 2, bindings[0].TLSMode) require.Equal(t, 2, bindings[0].TLSMode)
require.Equal(t, "127.0.1.2", bindings[0].ForcePassiveIP) 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.Equal(t, 0, bindings[0].ClientAuthType)
require.Len(t, bindings[0].TLSCipherSuites, 2) require.Len(t, bindings[0].TLSCipherSuites, 2)
require.Equal(t, "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256", bindings[0].TLSCipherSuites[0]) 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.True(t, bindings[1].ApplyProxyConfig) // default value
require.Equal(t, 1, bindings[1].TLSMode) require.Equal(t, 1, bindings[1].TLSMode)
require.Equal(t, "127.0.1.1", bindings[1].ForcePassiveIP) 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.Equal(t, 2, bindings[1].ClientAuthType)
require.Nil(t, bindings[1].TLSCipherSuites) require.Nil(t, bindings[1].TLSCipherSuites)
require.Equal(t, 0, bindings[1].PassiveConnectionsSecurity) require.Equal(t, 0, bindings[1].PassiveConnectionsSecurity)

View file

@ -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`. - `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. - `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: "". - `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. - `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. - `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`. - `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`.

View file

@ -2,9 +2,11 @@
package ftpd package ftpd
import ( import (
"errors"
"fmt" "fmt"
"net" "net"
"path/filepath" "path/filepath"
"strings"
ftpserver "github.com/fclairamb/ftpserverlib" ftpserver "github.com/fclairamb/ftpserverlib"
@ -22,6 +24,14 @@ var (
serviceStatus ServiceStatus 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 // Binding defines the configuration for a network listener
type Binding struct { type Binding struct {
// The address to listen on. A blank value means listen on all available network interfaces. // 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"` TLSMode int `json:"tls_mode" mapstructure:"tls_mode"`
// External IP address to expose for passive connections. // External IP address to expose for passive connections.
ForcePassiveIP string `json:"force_passive_ip" mapstructure:"force_passive_ip"` 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 1 to require client certificate authentication.
// Set to 2 to require a client certificate and verfify it if given. In this mode // 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. // the client is allowed not to send a certificate.
@ -99,19 +112,61 @@ func (b *Binding) checkSecuritySettings() error {
func (b *Binding) checkPassiveIP() error { func (b *Binding) checkPassiveIP() error {
if b.ForcePassiveIP != "" { if b.ForcePassiveIP != "" {
ip := net.ParseIP(b.ForcePassiveIP) ip, err := parsePassiveIP(b.ForcePassiveIP)
if ip == nil { if err != nil {
return fmt.Errorf("the provided passive IP %#v is not valid", b.ForcePassiveIP) return err
} }
ip = ip.To4() b.ForcePassiveIP = ip
if ip == nil {
return fmt.Errorf("the provided passive IP %#v is not a valid IPv4 address", b.ForcePassiveIP)
} }
b.ForcePassiveIP = ip.String() 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
}
}
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 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 // HasProxy returns true if the proxy protocol is active for this binding
func (b *Binding) HasProxy() bool { func (b *Binding) HasProxy() bool {
return b.ApplyProxyConfig && common.Config.ProxyProtocol > 0 return b.ApplyProxyConfig && common.Config.ProxyProtocol > 0
@ -268,6 +323,18 @@ func GetStatus() ServiceStatus {
return 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 { func getConfigPath(name, configDir string) string {
if !util.IsFileInputValid(name) { if !util.IsFileInputValid(name) {
return "" return ""

View file

@ -254,6 +254,8 @@ xr5cb9VBRBtB9aOKVfuRhpatAfS2Pzm2Htae9lFn7slGPUmu2hkjDw==
type mockFTPClientContext struct { type mockFTPClientContext struct {
lastDataChannel ftpserver.DataChannel lastDataChannel ftpserver.DataChannel
remoteIP string
localIP string
} }
func (cc mockFTPClientContext) Path() string { func (cc mockFTPClientContext) Path() string {
@ -271,11 +273,19 @@ func (cc mockFTPClientContext) ID() uint32 {
} }
func (cc mockFTPClientContext) RemoteAddr() net.Addr { 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 { 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 { func (cc mockFTPClientContext) GetClientVersion() string {
@ -924,3 +934,69 @@ func TestCiphers(t *testing.T) {
require.Len(t, b.ciphers, 2) require.Len(t, b.ciphers, 2)
require.Equal(t, []uint16{tls.TLS_AES_128_GCM_SHA256, tls.TLS_AES_256_GCM_SHA384}, b.ciphers) 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)
}

View file

@ -124,7 +124,7 @@ func (s *Server) GetSettings() (*ftpserver.Settings, error) {
return &ftpserver.Settings{ return &ftpserver.Settings{
Listener: ftpListener, Listener: ftpListener,
ListenAddr: s.binding.GetAddress(), ListenAddr: s.binding.GetAddress(),
PublicHost: s.binding.ForcePassiveIP, PublicIPResolver: s.binding.passiveIPResolver,
PassiveTransferPortRange: portRange, PassiveTransferPortRange: portRange,
ActiveTransferPortNon20: s.config.ActiveTransfersPortNon20, ActiveTransferPortNon20: s.config.ActiveTransfersPortNon20,
IdleTimeout: -1, IdleTimeout: -1,

10
go.mod
View file

@ -7,7 +7,7 @@ require (
github.com/Azure/azure-storage-blob-go v0.14.0 github.com/Azure/azure-storage-blob-go v0.14.0
github.com/GehirnInc/crypt v0.0.0-20200316065508-bb7000b8a962 github.com/GehirnInc/crypt v0.0.0-20200316065508-bb7000b8a962
github.com/alexedwards/argon2id v0.0.0-20210511081203-7d35d68092b8 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/cockroachdb/cockroach-go/v2 v2.2.4
github.com/eikenb/pipeat v0.0.0-20210603033007-44fc3ffce52b github.com/eikenb/pipeat v0.0.0-20210603033007-44fc3ffce52b
github.com/fclairamb/ftpserverlib v0.16.0 github.com/fclairamb/ftpserverlib v0.16.0
@ -52,8 +52,8 @@ require (
go.uber.org/automaxprocs v1.4.0 go.uber.org/automaxprocs v1.4.0
gocloud.dev v0.24.0 gocloud.dev v0.24.0
golang.org/x/crypto v0.0.0-20211117183948-ae814b36b871 golang.org/x/crypto v0.0.0-20211117183948-ae814b36b871
golang.org/x/net v0.0.0-20211118161319-6a13c67c3ce4 golang.org/x/net v0.0.0-20211123203042-d83791d6bcd9
golang.org/x/sys v0.0.0-20211117180635-dee7805ff2e1 golang.org/x/sys v0.0.0-20211124211545-fe61309f8881
golang.org/x/time v0.0.0-20211116232009-f0f3c7e86c11 golang.org/x/time v0.0.0-20211116232009-f0f3c7e86c11
google.golang.org/api v0.60.0 google.golang.org/api v0.60.0
google.golang.org/grpc v1.42.0 google.golang.org/grpc v1.42.0
@ -101,7 +101,7 @@ require (
github.com/lestrrat-go/option v1.0.0 // indirect github.com/lestrrat-go/option v1.0.0 // indirect
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect
github.com/magiconair/properties v1.8.5 // 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-ieproxy v0.0.1 // indirect
github.com/mattn/go-isatty v0.0.14 // indirect github.com/mattn/go-isatty v0.0.14 // indirect
github.com/matttproud/golang_protobuf_extensions v1.0.1 // 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/fclairamb/ftpserverlib => github.com/drakkan/ftpserverlib v0.0.0-20211107071448-34ff70e85dfb
github.com/jlaffaye/ftp => github.com/drakkan/ftp v0.0.0-20201114075148-9b9adce499a9 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/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
) )

16
go.sum
View file

@ -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.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.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.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.12 h1:zVrAgi3/HuMPygZknc+f2KAHcn+Zuq767857hnHBMPA=
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/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.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 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= 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/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 h1:cT/w4XStm7m022JgVqmrXZLcZ4UjoUER1VW5/5gd6ec=
github.com/drakkan/ftpserverlib v0.0.0-20211107071448-34ff70e85dfb/go.mod h1:fBiQ19WDhtvKArMu0Pifg71k+0xqRYn+F0d9AsjkZw8= 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-20211125114103-f7adc41924ea h1:M5ZyIIYPyx1dhIQm1QbF4ofccWkjWFdSbJJ5qYziWek=
github.com/drakkan/net v0.0.0-20211120084140-32033f6a21da/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= 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 h1:8tfGdb4kg/YCvAbIrsMazgoNtnqdOqQVDKW12uUCuuU=
github.com/drakkan/pipeat v0.0.0-20210805162858-70e57fa8a639/go.mod h1:kltMsfRMTHSFdMbK66XdS8mfMW77+FZA1fGY1xYMF84= 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= 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.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.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.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.12 h1:jF+Du6AlPIjs2BiUiQlKOX0rt3SujHxPnksPKZbaA40=
github.com/mattn/go-colorable v0.1.11/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= 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 h1:qiyop7gCflfhwCzGyeT0gro3sF9AIg9HU98JORTkqfI=
github.com/mattn/go-ieproxy v0.0.1/go.mod h1:pYabZ6IHcRpFh7vIaLfK7rdcWgFEb3SFJ6/gNWuh88E= 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= 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-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-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-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-20211124211545-fe61309f8881 h1:TyHqChC80pFkXWraUUf6RuB5IqFdQieMLwwCJokV2pc=
golang.org/x/sys v0.0.0-20211117180635-dee7805ff2e1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 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 h1:v+OssWQX+hTHEmOBgwxdZxK4zHq3yOs8F9J7mk0PY8E=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= 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= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=

View file

@ -86,6 +86,7 @@
"apply_proxy_config": true, "apply_proxy_config": true,
"tls_mode": 0, "tls_mode": 0,
"force_passive_ip": "", "force_passive_ip": "",
"passive_ip_overrides": [],
"client_auth_type": 0, "client_auth_type": 0,
"tls_cipher_suites": [], "tls_cipher_suites": [],
"passive_connections_security": 0, "passive_connections_security": 0,