mirror of
https://github.com/drakkan/sftpgo.git
synced 2024-11-21 15:10:23 +00:00
ssh: allow to configure public key auth algorithms
Signed-off-by: Nicola Murino <nicola.murino@gmail.com>
This commit is contained in:
parent
f83600225b
commit
c5c5860012
15 changed files with 160 additions and 46 deletions
|
@ -147,6 +147,7 @@ The configuration file contains the following sections:
|
|||
- `kex_algorithms`, list of strings. Available KEX (Key Exchange) algorithms in preference order. Leave empty to use default values. The supported values are: `curve25519-sha256`, `curve25519-sha256@libssh.org`, `ecdh-sha2-nistp256`, `ecdh-sha2-nistp384`, `ecdh-sha2-nistp521`, `diffie-hellman-group14-sha256`, `diffie-hellman-group16-sha512`, `diffie-hellman-group14-sha1`, `diffie-hellman-group1-sha1`. Default values: `curve25519-sha256`, `curve25519-sha256@libssh.org`, `ecdh-sha2-nistp256`, `ecdh-sha2-nistp384`, `ecdh-sha2-nistp521`, `diffie-hellman-group14-sha256`. SHA512 based KEXs are disabled by default because they are slow. If you set one or more moduli files, `diffie-hellman-group-exchange-sha256` and `diffie-hellman-group-exchange-sha1` will be available.
|
||||
- `ciphers`, list of strings. Allowed ciphers in preference order. Leave empty to use default values. The supported values are: `aes128-gcm@openssh.com`, `aes256-gcm@openssh.com`, `chacha20-poly1305@openssh.com`, `aes128-ctr`, `aes192-ctr`, `aes256-ctr`, `aes128-cbc`, `aes192-cbc`, `aes256-cbc`, `3des-cbc`, `arcfour256`, `arcfour128`, `arcfour`. Default values: `aes128-gcm@openssh.com`, `aes256-gcm@openssh.com`, `chacha20-poly1305@openssh.com`, `aes128-ctr`, `aes192-ctr`, `aes256-ctr`. Please note that the ciphers disabled by default are insecure, you should expect that an active attacker can recover plaintext if you enable them.
|
||||
- `macs`, list of strings. Available MAC (message authentication code) algorithms in preference order. Leave empty to use default values. The supported values are: `hmac-sha2-256-etm@openssh.com`, `hmac-sha2-256`, `hmac-sha2-512-etm@openssh.com`, `hmac-sha2-512`, `hmac-sha1`, `hmac-sha1-96`. Default values: `hmac-sha2-256-etm@openssh.com`, `hmac-sha2-256`.
|
||||
- `public_key_algorithms`, list of strings. Public key algorithms that the server will accept for client authentication. The supported values are: `ecdsa-sha2-nistp256`, `ecdsa-sha2-nistp384`, `ecdsa-sha2-nistp521`, `rsa-sha2-512`, `rsa-sha2-256`, `ssh-rsa`, `ssh-dss`, `ssh-ed25519`, `sk-ssh-ed25519@openssh.com`, `sk-ecdsa-sha2-nistp256@openssh.com`. Default values: `ecdsa-sha2-nistp256`, `ecdsa-sha2-nistp384`, `ecdsa-sha2-nistp521`, `rsa-sha2-512`, `rsa-sha2-256`, `ssh-ed25519`, `sk-ssh-ed25519@openssh.com`, `sk-ecdsa-sha2-nistp256@openssh.com`.
|
||||
- `trusted_user_ca_keys`, list of public keys paths of certificate authorities that are trusted to sign user certificates for authentication. The paths can be absolute or relative to the configuration directory.
|
||||
- `revoked_user_certs_file`, path to a file containing the revoked user certificates. The path can be absolute or relative to the configuration directory. It must contain a JSON list with the public key fingerprints of the revoked certificates. Example content: `["SHA256:bsBRHC/xgiqBJdSuvSTNpJNLTISP/G356jNMCRYC5Es","SHA256:119+8cL/HH+NLMawRsJx6CzPF1I3xC+jpM60bQHXGE8"]`. The revocation list can be reloaded on demand sending a `SIGHUP` signal on Unix based systems and a `paramchange` request to the running service on Windows. Default: "".
|
||||
- `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 disable login banner.
|
||||
|
|
2
go.mod
2
go.mod
|
@ -177,5 +177,5 @@ replace (
|
|||
github.com/fclairamb/ftpserverlib => github.com/drakkan/ftpserverlib v0.0.0-20230820193955-e7243edeb89b
|
||||
github.com/jlaffaye/ftp => github.com/drakkan/ftp v0.0.0-20201114075148-9b9adce499a9
|
||||
github.com/robfig/cron/v3 => github.com/drakkan/cron/v3 v3.0.0-20230222140221-217a1e4d96c0
|
||||
golang.org/x/crypto => github.com/drakkan/crypto v0.0.0-20231109082937-60ac5813bca0
|
||||
golang.org/x/crypto => github.com/drakkan/crypto v0.0.0-20231109180513-aa0daef37eeb
|
||||
)
|
||||
|
|
4
go.sum
4
go.sum
|
@ -153,8 +153,8 @@ github.com/dnaeon/go-vcr v1.2.0 h1:zHCHvJYTMh1N7xnV7zf1m1GPBF9Ad0Jk/whtQ1663qI=
|
|||
github.com/dnaeon/go-vcr v1.2.0/go.mod h1:R4UdLID7HZT3taECzJs4YgbbH6PIGXB6W/sc5OLb6RQ=
|
||||
github.com/drakkan/cron/v3 v3.0.0-20230222140221-217a1e4d96c0 h1:EW9gIJRmt9lzk66Fhh4S8VEtURA6QHZqGeSRE9Nb2/U=
|
||||
github.com/drakkan/cron/v3 v3.0.0-20230222140221-217a1e4d96c0/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro=
|
||||
github.com/drakkan/crypto v0.0.0-20231109082937-60ac5813bca0 h1:5UwG68raSmbWbZdCDOxxaQpzfRS/2/4XLjP+o5lOvt0=
|
||||
github.com/drakkan/crypto v0.0.0-20231109082937-60ac5813bca0/go.mod h1:4ChreQoLWfG3xLDer1WdlH5NdlQ3+mwnQq1YTKY+72g=
|
||||
github.com/drakkan/crypto v0.0.0-20231109180513-aa0daef37eeb h1:2CkHnBtgdS29SoGR4SI9wkE711HRkC9983PNYi+vtKQ=
|
||||
github.com/drakkan/crypto v0.0.0-20231109180513-aa0daef37eeb/go.mod h1:4ChreQoLWfG3xLDer1WdlH5NdlQ3+mwnQq1YTKY+72g=
|
||||
github.com/drakkan/ftp v0.0.0-20201114075148-9b9adce499a9 h1:LPH1dEblAOO/LoG7yHPMtBLXhQmjaga91/DDjWk9jWA=
|
||||
github.com/drakkan/ftp v0.0.0-20201114075148-9b9adce499a9/go.mod h1:2lmrmq866uF2tnje75wQHzmPXhmSWUt7Gyx2vgK1RCU=
|
||||
github.com/drakkan/ftpserverlib v0.0.0-20230820193955-e7243edeb89b h1:sCtiYerLxfOQrSludkwGwwXLlSVHxpvfmyOxjCOf0ec=
|
||||
|
|
|
@ -263,6 +263,7 @@ func Init() {
|
|||
KexAlgorithms: []string{},
|
||||
Ciphers: []string{},
|
||||
MACs: []string{},
|
||||
PublicKeyAlgorithms: []string{},
|
||||
TrustedUserCAKeys: []string{},
|
||||
RevokedUserCertsFile: "",
|
||||
LoginBannerFile: "",
|
||||
|
@ -2020,6 +2021,7 @@ func setViperDefaults() {
|
|||
viper.SetDefault("sftpd.kex_algorithms", globalConf.SFTPD.KexAlgorithms)
|
||||
viper.SetDefault("sftpd.ciphers", globalConf.SFTPD.Ciphers)
|
||||
viper.SetDefault("sftpd.macs", globalConf.SFTPD.MACs)
|
||||
viper.SetDefault("sftpd.public_key_algorithms", globalConf.SFTPD.PublicKeyAlgorithms)
|
||||
viper.SetDefault("sftpd.trusted_user_ca_keys", globalConf.SFTPD.TrustedUserCAKeys)
|
||||
viper.SetDefault("sftpd.revoked_user_certs_file", globalConf.SFTPD.RevokedUserCertsFile)
|
||||
viper.SetDefault("sftpd.login_banner_file", globalConf.SFTPD.LoginBannerFile)
|
||||
|
|
|
@ -28,8 +28,9 @@ import (
|
|||
|
||||
// Supported values for host keys, KEXs, ciphers, MACs
|
||||
var (
|
||||
supportedHostKeyAlgos = []string{ssh.KeyAlgoRSA}
|
||||
supportedKexAlgos = []string{
|
||||
supportedHostKeyAlgos = []string{ssh.KeyAlgoRSA}
|
||||
supportedPublicKeyAlgos = []string{ssh.KeyAlgoRSA, ssh.KeyAlgoDSA}
|
||||
supportedKexAlgos = []string{
|
||||
"diffie-hellman-group16-sha512", "diffie-hellman-group14-sha1", "diffie-hellman-group1-sha1",
|
||||
"diffie-hellman-group-exchange-sha256", "diffie-hellman-group-exchange-sha1",
|
||||
}
|
||||
|
@ -45,17 +46,21 @@ var (
|
|||
|
||||
// SFTPDConfigs defines configurations for SFTPD
|
||||
type SFTPDConfigs struct {
|
||||
HostKeyAlgos []string `json:"host_key_algos,omitempty"`
|
||||
Moduli []string `json:"moduli,omitempty"`
|
||||
KexAlgorithms []string `json:"kex_algorithms,omitempty"`
|
||||
Ciphers []string `json:"ciphers,omitempty"`
|
||||
MACs []string `json:"macs,omitempty"`
|
||||
HostKeyAlgos []string `json:"host_key_algos,omitempty"`
|
||||
PublicKeyAlgos []string `json:"public_key_algos,omitempty"`
|
||||
Moduli []string `json:"moduli,omitempty"`
|
||||
KexAlgorithms []string `json:"kex_algorithms,omitempty"`
|
||||
Ciphers []string `json:"ciphers,omitempty"`
|
||||
MACs []string `json:"macs,omitempty"`
|
||||
}
|
||||
|
||||
func (c *SFTPDConfigs) isEmpty() bool {
|
||||
if len(c.HostKeyAlgos) > 0 {
|
||||
return false
|
||||
}
|
||||
if len(c.PublicKeyAlgos) > 0 {
|
||||
return false
|
||||
}
|
||||
if len(c.Moduli) > 0 {
|
||||
return false
|
||||
}
|
||||
|
@ -76,6 +81,11 @@ func (*SFTPDConfigs) GetSupportedHostKeyAlgos() []string {
|
|||
return supportedHostKeyAlgos
|
||||
}
|
||||
|
||||
// GetSupportedPublicKeyAlgos returns the supported legacy public key algos
|
||||
func (*SFTPDConfigs) GetSupportedPublicKeyAlgos() []string {
|
||||
return supportedPublicKeyAlgos
|
||||
}
|
||||
|
||||
// GetSupportedKEXAlgos returns the supported KEX algos
|
||||
func (*SFTPDConfigs) GetSupportedKEXAlgos() []string {
|
||||
return supportedKexAlgos
|
||||
|
@ -129,12 +139,19 @@ func (c *SFTPDConfigs) validate() error {
|
|||
return util.NewValidationError(fmt.Sprintf("unsupported MAC algorithm %q", mac))
|
||||
}
|
||||
}
|
||||
for _, algo := range c.PublicKeyAlgos {
|
||||
if !util.Contains(supportedPublicKeyAlgos, algo) {
|
||||
return util.NewValidationError(fmt.Sprintf("unsupported public key algorithm %q", algo))
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *SFTPDConfigs) getACopy() *SFTPDConfigs {
|
||||
hostKeys := make([]string, len(c.HostKeyAlgos))
|
||||
copy(hostKeys, c.HostKeyAlgos)
|
||||
publicKeys := make([]string, len(c.PublicKeyAlgos))
|
||||
copy(publicKeys, c.PublicKeyAlgos)
|
||||
moduli := make([]string, len(c.Moduli))
|
||||
copy(moduli, c.Moduli)
|
||||
kexs := make([]string, len(c.KexAlgorithms))
|
||||
|
@ -145,11 +162,12 @@ func (c *SFTPDConfigs) getACopy() *SFTPDConfigs {
|
|||
copy(macs, c.MACs)
|
||||
|
||||
return &SFTPDConfigs{
|
||||
HostKeyAlgos: hostKeys,
|
||||
Moduli: moduli,
|
||||
KexAlgorithms: kexs,
|
||||
Ciphers: ciphers,
|
||||
MACs: macs,
|
||||
HostKeyAlgos: hostKeys,
|
||||
PublicKeyAlgos: publicKeys,
|
||||
Moduli: moduli,
|
||||
KexAlgorithms: kexs,
|
||||
Ciphers: ciphers,
|
||||
MACs: macs,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -7823,7 +7823,8 @@ func TestLoaddata(t *testing.T) {
|
|||
}
|
||||
configs := dataprovider.Configs{
|
||||
SFTPD: &dataprovider.SFTPDConfigs{
|
||||
HostKeyAlgos: []string{ssh.KeyAlgoRSA, ssh.CertAlgoRSAv01},
|
||||
HostKeyAlgos: []string{ssh.KeyAlgoRSA, ssh.CertAlgoRSAv01},
|
||||
PublicKeyAlgos: []string{ssh.KeyAlgoDSA},
|
||||
},
|
||||
SMTP: &dataprovider.SMTPConfigs{
|
||||
Host: "mail.example.com",
|
||||
|
@ -7890,6 +7891,7 @@ func TestLoaddata(t *testing.T) {
|
|||
assert.NoError(t, err)
|
||||
assert.Equal(t, configs.SMTP, configsGet.SMTP)
|
||||
assert.Equal(t, []string{ssh.KeyAlgoRSA}, configsGet.SFTPD.HostKeyAlgos)
|
||||
assert.Equal(t, []string{ssh.KeyAlgoDSA}, configsGet.SFTPD.PublicKeyAlgos)
|
||||
assert.Len(t, configsGet.SFTPD.Moduli, 0)
|
||||
assert.Len(t, configsGet.SFTPD.KexAlgorithms, 0)
|
||||
assert.Len(t, configsGet.SFTPD.Ciphers, 0)
|
||||
|
@ -12722,6 +12724,7 @@ func TestWebConfigsMock(t *testing.T) {
|
|||
// save SFTP configs
|
||||
form.Set("sftp_host_key_algos", ssh.KeyAlgoRSA)
|
||||
form.Add("sftp_host_key_algos", ssh.CertAlgoDSAv01)
|
||||
form.Set("sftp_pub_key_algos", ssh.KeyAlgoDSA)
|
||||
form.Set("sftp_moduli", "path 1 , path 2")
|
||||
form.Set("form_action", "sftp_submit")
|
||||
req, err = http.NewRequest(http.MethodPost, webConfigsPath, bytes.NewBuffer([]byte(form.Encode())))
|
||||
|
@ -12733,6 +12736,7 @@ func TestWebConfigsMock(t *testing.T) {
|
|||
assert.Contains(t, rr.Body.String(), ssh.CertAlgoDSAv01) // invalid algo
|
||||
form.Set("sftp_host_key_algos", ssh.KeyAlgoRSA)
|
||||
form.Add("sftp_host_key_algos", ssh.CertAlgoRSAv01)
|
||||
form.Set("sftp_pub_key_algos", ssh.KeyAlgoDSA)
|
||||
form.Set("sftp_kex_algos", "diffie-hellman-group18-sha512")
|
||||
form.Add("sftp_kex_algos", "diffie-hellman-group16-sha512")
|
||||
req, err = http.NewRequest(http.MethodPost, webConfigsPath, bytes.NewBuffer([]byte(form.Encode())))
|
||||
|
@ -12747,6 +12751,8 @@ func TestWebConfigsMock(t *testing.T) {
|
|||
assert.NoError(t, err)
|
||||
assert.Len(t, configs.SFTPD.HostKeyAlgos, 1)
|
||||
assert.Contains(t, configs.SFTPD.HostKeyAlgos, ssh.KeyAlgoRSA)
|
||||
assert.Len(t, configs.SFTPD.PublicKeyAlgos, 1)
|
||||
assert.Contains(t, configs.SFTPD.PublicKeyAlgos, ssh.KeyAlgoDSA)
|
||||
assert.Len(t, configs.SFTPD.Moduli, 2)
|
||||
assert.Contains(t, configs.SFTPD.Moduli, "path 1")
|
||||
assert.Contains(t, configs.SFTPD.Moduli, "path 2")
|
||||
|
@ -12795,6 +12801,8 @@ func TestWebConfigsMock(t *testing.T) {
|
|||
assert.NoError(t, err)
|
||||
assert.Len(t, configs.SFTPD.HostKeyAlgos, 1)
|
||||
assert.Contains(t, configs.SFTPD.HostKeyAlgos, ssh.KeyAlgoRSA)
|
||||
assert.Len(t, configs.SFTPD.PublicKeyAlgos, 1)
|
||||
assert.Contains(t, configs.SFTPD.PublicKeyAlgos, ssh.KeyAlgoDSA)
|
||||
assert.Len(t, configs.SFTPD.Moduli, 2)
|
||||
assert.Equal(t, "mail.example.net", configs.SMTP.Host)
|
||||
assert.Equal(t, 587, configs.SMTP.Port)
|
||||
|
@ -12865,6 +12873,8 @@ func TestWebConfigsMock(t *testing.T) {
|
|||
assert.NoError(t, err)
|
||||
assert.Len(t, configs.SFTPD.HostKeyAlgos, 1)
|
||||
assert.Contains(t, configs.SFTPD.HostKeyAlgos, ssh.KeyAlgoRSA)
|
||||
assert.Len(t, configs.SFTPD.PublicKeyAlgos, 1)
|
||||
assert.Contains(t, configs.SFTPD.PublicKeyAlgos, ssh.KeyAlgoDSA)
|
||||
assert.Len(t, configs.SFTPD.Moduli, 2)
|
||||
assert.Equal(t, 80, configs.ACME.HTTP01Challenge.Port)
|
||||
assert.Equal(t, 7, configs.ACME.Protocols)
|
||||
|
@ -12896,6 +12906,7 @@ func TestWebConfigsMock(t *testing.T) {
|
|||
configs, err = dataprovider.GetConfigs()
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, configs.SFTPD.HostKeyAlgos, 1)
|
||||
assert.Len(t, configs.SFTPD.PublicKeyAlgos, 1)
|
||||
assert.Equal(t, 402, configs.ACME.HTTP01Challenge.Port)
|
||||
assert.Equal(t, 1, configs.ACME.Protocols)
|
||||
assert.Equal(t, domain, configs.ACME.Domain)
|
||||
|
|
|
@ -2553,11 +2553,12 @@ func getIPListEntryFromPostFields(r *http.Request, listType dataprovider.IPListT
|
|||
|
||||
func getSFTPConfigsFromPostFields(r *http.Request) *dataprovider.SFTPDConfigs {
|
||||
return &dataprovider.SFTPDConfigs{
|
||||
HostKeyAlgos: r.Form["sftp_host_key_algos"],
|
||||
Moduli: getSliceFromDelimitedValues(r.Form.Get("sftp_moduli"), ","),
|
||||
KexAlgorithms: r.Form["sftp_kex_algos"],
|
||||
Ciphers: r.Form["sftp_ciphers"],
|
||||
MACs: r.Form["sftp_macs"],
|
||||
HostKeyAlgos: r.Form["sftp_host_key_algos"],
|
||||
PublicKeyAlgos: r.Form["sftp_pub_key_algos"],
|
||||
Moduli: getSliceFromDelimitedValues(r.Form.Get("sftp_moduli"), ","),
|
||||
KexAlgorithms: r.Form["sftp_kex_algos"],
|
||||
Ciphers: r.Form["sftp_ciphers"],
|
||||
MACs: r.Form["sftp_macs"],
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1879,13 +1879,15 @@ func TestConfigsFromProvider(t *testing.T) {
|
|||
assert.Len(t, c.KexAlgorithms, 0)
|
||||
assert.Len(t, c.Ciphers, 0)
|
||||
assert.Len(t, c.MACs, 0)
|
||||
assert.Len(t, c.PublicKeyAlgorithms, 0)
|
||||
configs := dataprovider.Configs{
|
||||
SFTPD: &dataprovider.SFTPDConfigs{
|
||||
HostKeyAlgos: []string{ssh.KeyAlgoRSA},
|
||||
Moduli: []string{"/etc/ssh/moduli"},
|
||||
KexAlgorithms: []string{kexDHGroupExchangeSHA256},
|
||||
Ciphers: []string{"aes128-cbc", "aes192-cbc", "aes256-cbc"},
|
||||
MACs: []string{"hmac-sha2-512-etm@openssh.com"},
|
||||
HostKeyAlgos: []string{ssh.KeyAlgoRSA},
|
||||
Moduli: []string{"/etc/ssh/moduli"},
|
||||
KexAlgorithms: []string{kexDHGroupExchangeSHA256},
|
||||
Ciphers: []string{"aes128-cbc", "aes192-cbc", "aes256-cbc"},
|
||||
MACs: []string{"hmac-sha2-512-etm@openssh.com"},
|
||||
PublicKeyAlgos: []string{ssh.KeyAlgoDSA},
|
||||
},
|
||||
}
|
||||
err = dataprovider.UpdateConfigs(&configs, "", "", "")
|
||||
|
@ -1896,11 +1898,13 @@ func TestConfigsFromProvider(t *testing.T) {
|
|||
expectedKEXs := append(preferredKexAlgos, configs.SFTPD.KexAlgorithms...)
|
||||
expectedCiphers := append(preferredCiphers, configs.SFTPD.Ciphers...)
|
||||
expectedMACs := append(preferredMACs, configs.SFTPD.MACs...)
|
||||
expectedPublicKeyAlgos := append(preferredPublicKeyAlgos, configs.SFTPD.PublicKeyAlgos...)
|
||||
assert.Equal(t, expectedHostKeyAlgos, c.HostKeyAlgorithms)
|
||||
assert.Equal(t, expectedKEXs, c.KexAlgorithms)
|
||||
assert.Equal(t, expectedCiphers, c.Ciphers)
|
||||
assert.Equal(t, expectedMACs, c.MACs)
|
||||
assert.Equal(t, configs.SFTPD.Moduli, c.Moduli)
|
||||
assert.Equal(t, expectedPublicKeyAlgos, c.PublicKeyAlgorithms)
|
||||
|
||||
err = dataprovider.UpdateConfigs(nil, "", "", "")
|
||||
assert.NoError(t, err)
|
||||
|
|
|
@ -69,6 +69,19 @@ var (
|
|||
ssh.KeyAlgoECDSA256, ssh.KeyAlgoECDSA384, ssh.KeyAlgoECDSA521,
|
||||
ssh.KeyAlgoED25519,
|
||||
}
|
||||
supportedPublicKeyAlgos = []string{
|
||||
ssh.KeyAlgoED25519,
|
||||
ssh.KeyAlgoSKED25519, ssh.KeyAlgoSKECDSA256,
|
||||
ssh.KeyAlgoECDSA256, ssh.KeyAlgoECDSA384, ssh.KeyAlgoECDSA521,
|
||||
ssh.KeyAlgoRSASHA256, ssh.KeyAlgoRSASHA512, ssh.KeyAlgoRSA,
|
||||
ssh.KeyAlgoDSA,
|
||||
}
|
||||
preferredPublicKeyAlgos = []string{
|
||||
ssh.KeyAlgoED25519,
|
||||
ssh.KeyAlgoSKED25519, ssh.KeyAlgoSKECDSA256,
|
||||
ssh.KeyAlgoECDSA256, ssh.KeyAlgoECDSA384, ssh.KeyAlgoECDSA521,
|
||||
ssh.KeyAlgoRSASHA256, ssh.KeyAlgoRSASHA512,
|
||||
}
|
||||
supportedKexAlgos = []string{
|
||||
"curve25519-sha256", "curve25519-sha256@libssh.org",
|
||||
"ecdh-sha2-nistp256", "ecdh-sha2-nistp384", "ecdh-sha2-nistp521",
|
||||
|
@ -171,6 +184,8 @@ type Configuration struct {
|
|||
// MACs Specifies the available MAC (message authentication code) algorithms
|
||||
// in preference order
|
||||
MACs []string `json:"macs" mapstructure:"macs"`
|
||||
// PublicKeyAlgorithms lists the supported public key algorithms for client authentication.
|
||||
PublicKeyAlgorithms []string `json:"public_key_algorithms" mapstructure:"public_key_algorithms"`
|
||||
// TrustedUserCAKeys specifies a list of public keys paths of certificate authorities
|
||||
// that are trusted to sign user certificates for authentication.
|
||||
// The paths can be absolute or relative to the configuration directory
|
||||
|
@ -318,6 +333,12 @@ func (c *Configuration) loadFromProvider() error {
|
|||
}
|
||||
c.HostKeyAlgorithms = append(c.HostKeyAlgorithms, configs.SFTPD.HostKeyAlgos...)
|
||||
}
|
||||
if len(configs.SFTPD.PublicKeyAlgos) > 0 {
|
||||
if len(c.PublicKeyAlgorithms) == 0 {
|
||||
c.PublicKeyAlgorithms = preferredPublicKeyAlgos
|
||||
}
|
||||
c.PublicKeyAlgorithms = append(c.PublicKeyAlgorithms, configs.SFTPD.PublicKeyAlgos...)
|
||||
}
|
||||
c.Moduli = append(c.Moduli, configs.SFTPD.Moduli...)
|
||||
if len(configs.SFTPD.KexAlgorithms) > 0 {
|
||||
if len(c.KexAlgorithms) == 0 {
|
||||
|
@ -441,7 +462,7 @@ func (c *Configuration) serve(listener net.Listener, serverConfig *ssh.ServerCon
|
|||
}
|
||||
}
|
||||
|
||||
func (c *Configuration) configureSecurityOptions(serverConfig *ssh.ServerConfig) error {
|
||||
func (c *Configuration) configureKeyAlgos(serverConfig *ssh.ServerConfig) error {
|
||||
if len(c.HostKeyAlgorithms) == 0 {
|
||||
c.HostKeyAlgorithms = preferredHostKeyAlgos
|
||||
} else {
|
||||
|
@ -453,6 +474,27 @@ func (c *Configuration) configureSecurityOptions(serverConfig *ssh.ServerConfig)
|
|||
}
|
||||
}
|
||||
|
||||
if len(c.PublicKeyAlgorithms) > 0 {
|
||||
c.PublicKeyAlgorithms = util.RemoveDuplicates(c.PublicKeyAlgorithms, true)
|
||||
for _, algo := range c.PublicKeyAlgorithms {
|
||||
if !util.Contains(supportedPublicKeyAlgos, algo) {
|
||||
return fmt.Errorf("unsupported public key authentication algorithm %q", algo)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
c.PublicKeyAlgorithms = preferredPublicKeyAlgos
|
||||
}
|
||||
serverConfig.PublicKeyAuthAlgorithms = c.PublicKeyAlgorithms
|
||||
serviceStatus.PublicKeyAlgorithms = c.PublicKeyAlgorithms
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Configuration) configureSecurityOptions(serverConfig *ssh.ServerConfig) error {
|
||||
if err := c.configureKeyAlgos(serverConfig); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(c.KexAlgorithms) > 0 {
|
||||
hasDHGroupKEX := util.Contains(supportedKexAlgos, kexDHGroupExchangeSHA256)
|
||||
if !hasDHGroupKEX {
|
||||
|
@ -468,11 +510,12 @@ func (c *Configuration) configureSecurityOptions(serverConfig *ssh.ServerConfig)
|
|||
return fmt.Errorf("unsupported key-exchange algorithm %q", kex)
|
||||
}
|
||||
}
|
||||
serverConfig.KeyExchanges = c.KexAlgorithms
|
||||
serviceStatus.KexAlgorithms = c.KexAlgorithms
|
||||
} else {
|
||||
serviceStatus.KexAlgorithms = preferredKexAlgos
|
||||
c.KexAlgorithms = preferredKexAlgos
|
||||
}
|
||||
serverConfig.KeyExchanges = c.KexAlgorithms
|
||||
serviceStatus.KexAlgorithms = c.KexAlgorithms
|
||||
|
||||
if len(c.Ciphers) > 0 {
|
||||
c.Ciphers = util.RemoveDuplicates(c.Ciphers, true)
|
||||
for _, cipher := range c.Ciphers {
|
||||
|
@ -480,11 +523,12 @@ func (c *Configuration) configureSecurityOptions(serverConfig *ssh.ServerConfig)
|
|||
return fmt.Errorf("unsupported cipher %q", cipher)
|
||||
}
|
||||
}
|
||||
serverConfig.Ciphers = c.Ciphers
|
||||
serviceStatus.Ciphers = c.Ciphers
|
||||
} else {
|
||||
serviceStatus.Ciphers = preferredCiphers
|
||||
c.Ciphers = preferredCiphers
|
||||
}
|
||||
serverConfig.Ciphers = c.Ciphers
|
||||
serviceStatus.Ciphers = c.Ciphers
|
||||
|
||||
if len(c.MACs) > 0 {
|
||||
c.MACs = util.RemoveDuplicates(c.MACs, true)
|
||||
for _, mac := range c.MACs {
|
||||
|
@ -492,11 +536,12 @@ func (c *Configuration) configureSecurityOptions(serverConfig *ssh.ServerConfig)
|
|||
return fmt.Errorf("unsupported MAC algorithm %q", mac)
|
||||
}
|
||||
}
|
||||
serverConfig.MACs = c.MACs
|
||||
serviceStatus.MACs = c.MACs
|
||||
} else {
|
||||
serviceStatus.MACs = preferredMACs
|
||||
c.MACs = preferredMACs
|
||||
}
|
||||
serverConfig.MACs = c.MACs
|
||||
serviceStatus.MACs = c.MACs
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
|
@ -77,14 +77,15 @@ func (h *HostKey) GetAlgosAsString() string {
|
|||
|
||||
// ServiceStatus defines the service status
|
||||
type ServiceStatus struct {
|
||||
IsActive bool `json:"is_active"`
|
||||
Bindings []Binding `json:"bindings"`
|
||||
SSHCommands []string `json:"ssh_commands"`
|
||||
HostKeys []HostKey `json:"host_keys"`
|
||||
Authentications []string `json:"authentications"`
|
||||
MACs []string `json:"macs"`
|
||||
KexAlgorithms []string `json:"kex_algorithms"`
|
||||
Ciphers []string `json:"ciphers"`
|
||||
IsActive bool `json:"is_active"`
|
||||
Bindings []Binding `json:"bindings"`
|
||||
SSHCommands []string `json:"ssh_commands"`
|
||||
HostKeys []HostKey `json:"host_keys"`
|
||||
Authentications []string `json:"authentications"`
|
||||
MACs []string `json:"macs"`
|
||||
KexAlgorithms []string `json:"kex_algorithms"`
|
||||
Ciphers []string `json:"ciphers"`
|
||||
PublicKeyAlgorithms []string `json:"public_key_algorithms"`
|
||||
}
|
||||
|
||||
// GetSSHCommandsAsString returns enabled SSH commands as comma separated string
|
||||
|
@ -112,6 +113,12 @@ func (s *ServiceStatus) GetCiphersAsString() string {
|
|||
return strings.Join(s.Ciphers, ", ")
|
||||
}
|
||||
|
||||
// GetPublicKeysAlgosAsString returns enabled public key authentication
|
||||
// algorithms as comma separated string
|
||||
func (s *ServiceStatus) GetPublicKeysAlgosAsString() string {
|
||||
return strings.Join(s.PublicKeyAlgorithms, ", ")
|
||||
}
|
||||
|
||||
// GetStatus returns the server status
|
||||
func GetStatus() ServiceStatus {
|
||||
return serviceStatus
|
||||
|
|
|
@ -438,6 +438,12 @@ func TestInitialization(t *testing.T) {
|
|||
assert.Contains(t, err.Error(), "unsupported key-exchange algorithm")
|
||||
}
|
||||
sftpdConf.KexAlgorithms = nil
|
||||
sftpdConf.PublicKeyAlgorithms = []string{"not a pub key algo"}
|
||||
err = sftpdConf.Initialize(configDir)
|
||||
if assert.Error(t, err) {
|
||||
assert.Contains(t, err.Error(), "unsupported public key authentication algorithm")
|
||||
}
|
||||
sftpdConf.PublicKeyAlgorithms = nil
|
||||
sftpdConf.HostKeyAlgorithms = []string{"not a host key algo"}
|
||||
err = sftpdConf.Initialize(configDir)
|
||||
if assert.Error(t, err) {
|
||||
|
@ -581,6 +587,7 @@ func TestBasicSFTPHandling(t *testing.T) {
|
|||
assert.NotEmpty(t, status.GetMACsAsString())
|
||||
assert.NotEmpty(t, status.GetKEXsAsString())
|
||||
assert.NotEmpty(t, status.GetCiphersAsString())
|
||||
assert.NotEmpty(t, status.GetPublicKeysAlgosAsString())
|
||||
}
|
||||
|
||||
func TestBasicSFTPFsHandling(t *testing.T) {
|
||||
|
|
|
@ -6505,6 +6505,10 @@ components:
|
|||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/SSHAuthentications'
|
||||
public_key_algorithms:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
macs:
|
||||
type: array
|
||||
items:
|
||||
|
|
|
@ -91,6 +91,7 @@
|
|||
"kex_algorithms": [],
|
||||
"ciphers": [],
|
||||
"macs": [],
|
||||
"public_key_algorithms": [],
|
||||
"trusted_user_ca_keys": [],
|
||||
"revoked_user_certs_file": "",
|
||||
"login_banner_file": "",
|
||||
|
|
|
@ -65,6 +65,17 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group row">
|
||||
<label for="idPubKeyAlgos" class="col-sm-2 col-form-label">Public Key Algos</label>
|
||||
<div class="col-sm-10">
|
||||
<select class="form-control selectpicker" id="idPubKeyAlgos" name="sftp_pub_key_algos" multiple>
|
||||
{{range $val := .Configs.SFTPD.GetSupportedPublicKeyAlgos}}
|
||||
<option value="{{$val}}" {{range $algo :=$.Configs.SFTPD.PublicKeyAlgos }}{{if eq $algo $val}}selected{{end}}{{end}}>{{$val}}</option>
|
||||
{{end}}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group row">
|
||||
<label for="idModuli" class="col-sm-2 col-form-label">Moduli</label>
|
||||
<div class="col-sm-10">
|
||||
|
|
|
@ -50,9 +50,11 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|||
<br>
|
||||
{{end}}
|
||||
<br>
|
||||
MAC algorithms: "{{.Status.SSH.GetMACsAsString}}"
|
||||
Public key authentication algorithms: "{{.Status.SSH.GetPublicKeysAlgosAsString}}"
|
||||
<br><br>
|
||||
KEX algorithms: "{{.Status.SSH.GetKEXsAsString}}"
|
||||
Message authentication algorithms: "{{.Status.SSH.GetMACsAsString}}"
|
||||
<br><br>
|
||||
Key exchange algorithms: "{{.Status.SSH.GetKEXsAsString}}"
|
||||
<br><br>
|
||||
Ciphers: "{{.Status.SSH.GetCiphersAsString}}"
|
||||
<br>
|
||||
|
|
Loading…
Reference in a new issue