mirror of
https://github.com/drakkan/sftpgo.git
synced 2024-11-21 23:20:24 +00:00
sftpd: add configuration options for allowed ciphers, MACs and KEX algorithms
add support for login banner too Fixes #32
This commit is contained in:
parent
dc5eeb54fd
commit
49a40f7a0b
5 changed files with 73 additions and 3 deletions
|
@ -132,6 +132,10 @@ The `sftpgo` configuration file contains the following sections:
|
|||
- `keys`, struct array. It contains the daemon's private keys. If empty or missing the daemon will search or try to generate `id_rsa` in the configuration directory.
|
||||
- `private_key`, path to the private key file. It can be a path relative to the config dir or an absolute one.
|
||||
- `enable_scp`, boolean. Default disabled. Set to `true` to enable SCP support. SCP is an experimental feature, we have our own SCP implementation since we can't rely on `scp` system command to proper handle permissions, quota and user's home dir restrictions. The SCP protocol is quite simple but there is no official docs about it, so we need more testing and feedbacks before enabling it by default. We may not handle some borderline cases or have sneaky bugs. Please do accurate tests yourself before enabling SCP and let us known if something does not work as expected for your use cases. SCP between two remote hosts is supported using the `-3` scp option.
|
||||
- `kex_algorithms`, list of strings. Available KEX (Key Exchange) algorithms in preference order. Leave empty to use default values. The supported values can be found here: [`crypto/ssh`](https://github.com/golang/crypto/blob/master/ssh/common.go#L46 "Supported kex algos")
|
||||
- `ciphers`, list of strings. Allowed ciphers. Leave empty to use default values. The supported values can be found here: [`crypto/ssh`](https://github.com/golang/crypto/blob/master/ssh/common.go#L28 "Supported ciphers")
|
||||
- `macs`, list of strings. available MAC (message authentication code) algorithms in preference order. Leave empty to use default values. The supported values can be found here: [`crypto/ssh`](https://github.com/golang/crypto/blob/master/ssh/common.go#L76 "Supported MACs")
|
||||
- `login_banner_file`, path to the login banner file. The contents of the specified file, if any, are sent to the remote user before authentication is allowed. It can be a path relative to the config dir or an absolute one. Leave empty to send no login banner
|
||||
- **"data_provider"**, the configuration for the data provider
|
||||
- `driver`, string. Supported drivers are `sqlite`, `mysql`, `postgresql`, `bolt`
|
||||
- `name`, string. Database name. For driver `sqlite` this can be the database name relative to the config dir or the absolute path to the SQLite database.
|
||||
|
|
|
@ -53,8 +53,12 @@ func init() {
|
|||
Command: "",
|
||||
HTTPNotificationURL: "",
|
||||
},
|
||||
Keys: []sftpd.Key{},
|
||||
IsSCPEnabled: false,
|
||||
Keys: []sftpd.Key{},
|
||||
IsSCPEnabled: false,
|
||||
KexAlgorithms: []string{},
|
||||
Ciphers: []string{},
|
||||
MACs: []string{},
|
||||
LoginBannerFile: "",
|
||||
},
|
||||
ProviderConf: dataprovider.Config{
|
||||
Driver: "sqlite",
|
||||
|
|
|
@ -62,6 +62,17 @@ type Configuration struct {
|
|||
// Please do accurate tests yourself before enabling SCP and let us known
|
||||
// if something does not work as expected for your use cases
|
||||
IsSCPEnabled bool `json:"enable_scp" mapstructure:"enable_scp"`
|
||||
// KexAlgorithms specifies the available KEX (Key Exchange) algorithms in
|
||||
// preference order.
|
||||
KexAlgorithms []string `json:"kex_algorithms" mapstructure:"kex_algorithms"`
|
||||
// Ciphers specifies the ciphers allowed
|
||||
Ciphers []string `json:"ciphers" mapstructure:"ciphers"`
|
||||
// MACs Specifies the available MAC (message authentication code) algorithms
|
||||
// in preference order
|
||||
MACs []string `json:"macs" mapstructure:"macs"`
|
||||
// LoginBannerFile the contents of the specified file, if any, are sent to
|
||||
// the remote user before authentication is allowed.
|
||||
LoginBannerFile string `json:"login_banner_file" mapstructure:"login_banner_file"`
|
||||
}
|
||||
|
||||
// Key contains information about host keys
|
||||
|
@ -127,6 +138,9 @@ func (c Configuration) Initialize(configDir string) error {
|
|||
serverConfig.AddHostKey(private)
|
||||
}
|
||||
|
||||
c.configureSecurityOptions(serverConfig)
|
||||
c.configureLoginBanner(serverConfig, configDir)
|
||||
|
||||
listener, err := net.Listen("tcp", fmt.Sprintf("%s:%d", c.BindAddress, c.BindPort))
|
||||
if err != nil {
|
||||
logger.Warn(logSender, "error starting listener on address %s:%d: %v", c.BindAddress, c.BindPort, err)
|
||||
|
@ -148,6 +162,39 @@ func (c Configuration) Initialize(configDir string) error {
|
|||
}
|
||||
}
|
||||
|
||||
func (c Configuration) configureSecurityOptions(serverConfig *ssh.ServerConfig) {
|
||||
if len(c.KexAlgorithms) > 0 {
|
||||
serverConfig.KeyExchanges = c.KexAlgorithms
|
||||
}
|
||||
if len(c.Ciphers) > 0 {
|
||||
serverConfig.Ciphers = c.Ciphers
|
||||
}
|
||||
if len(c.MACs) > 0 {
|
||||
serverConfig.MACs = c.MACs
|
||||
}
|
||||
}
|
||||
|
||||
func (c Configuration) configureLoginBanner(serverConfig *ssh.ServerConfig, configDir string) error {
|
||||
var err error
|
||||
if len(c.LoginBannerFile) > 0 {
|
||||
bannerFilePath := c.LoginBannerFile
|
||||
if !filepath.IsAbs(bannerFilePath) {
|
||||
bannerFilePath = filepath.Join(configDir, bannerFilePath)
|
||||
}
|
||||
var banner []byte
|
||||
banner, err = ioutil.ReadFile(bannerFilePath)
|
||||
if err == nil {
|
||||
serverConfig.BannerCallback = func(conn ssh.ConnMetadata) string {
|
||||
return string(banner)
|
||||
}
|
||||
} else {
|
||||
logger.WarnToConsole("unable to read login banner file: %v", err)
|
||||
logger.Warn(logSender, "unable to read login banner file: %v", err)
|
||||
}
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// AcceptInboundConnection handles an inbound connection to the server instance and determines if the request should be served or not.
|
||||
func (c Configuration) AcceptInboundConnection(conn net.Conn, config *ssh.ServerConfig) {
|
||||
defer conn.Close()
|
||||
|
|
|
@ -88,6 +88,9 @@ var (
|
|||
|
||||
func TestMain(m *testing.M) {
|
||||
logfilePath := filepath.Join(configDir, "sftpgo_sftpd_test.log")
|
||||
loginBannerFileName := "login_banner"
|
||||
loginBannerFile := filepath.Join(configDir, loginBannerFileName)
|
||||
ioutil.WriteFile(loginBannerFile, []byte("simple login banner\n"), 0777)
|
||||
logger.InitLogger(logfilePath, 5, 1, 28, false, zerolog.DebugLevel)
|
||||
config.LoadConfig(configDir, "")
|
||||
providerConf := config.GetProviderConf()
|
||||
|
@ -102,6 +105,12 @@ func TestMain(m *testing.M) {
|
|||
httpdConf := config.GetHTTPDConfig()
|
||||
router := api.GetHTTPRouter()
|
||||
sftpdConf.BindPort = 2022
|
||||
sftpdConf.KexAlgorithms = []string{"curve25519-sha256@libssh.org", "ecdh-sha2-nistp256",
|
||||
"ecdh-sha2-nistp384"}
|
||||
sftpdConf.Ciphers = []string{"chacha20-poly1305@openssh.com", "aes128-gcm@openssh.com",
|
||||
"aes256-ctr"}
|
||||
sftpdConf.MACs = []string{"hmac-sha2-256-etm@openssh.com", "hmac-sha2-256"}
|
||||
sftpdConf.LoginBannerFile = loginBannerFileName
|
||||
// we need to test SCP support
|
||||
sftpdConf.IsSCPEnabled = true
|
||||
// we run the test cases with UploadMode atomic. The non atomic code path
|
||||
|
@ -163,6 +172,7 @@ func TestMain(m *testing.M) {
|
|||
|
||||
exitCode := m.Run()
|
||||
os.Remove(logfilePath)
|
||||
os.Remove(loginBannerFile)
|
||||
os.Exit(exitCode)
|
||||
}
|
||||
|
||||
|
@ -171,6 +181,7 @@ func TestInitialization(t *testing.T) {
|
|||
sftpdConf := config.GetSFTPDConfig()
|
||||
sftpdConf.Umask = "invalid umask"
|
||||
sftpdConf.BindPort = 2022
|
||||
sftpdConf.LoginBannerFile = "invalid_file"
|
||||
err := sftpdConf.Initialize(configDir)
|
||||
if err == nil {
|
||||
t.Errorf("Inizialize must fail, a SFTP server should be already running")
|
||||
|
|
|
@ -13,7 +13,11 @@
|
|||
"http_notification_url": ""
|
||||
},
|
||||
"keys": [],
|
||||
"enable_scp": false
|
||||
"enable_scp": false,
|
||||
"kex_algorithms": [],
|
||||
"ciphers": [],
|
||||
"macs": [],
|
||||
"login_banner_file": ""
|
||||
},
|
||||
"data_provider": {
|
||||
"driver": "sqlite",
|
||||
|
|
Loading…
Reference in a new issue