Repository: minio/minio (language: go)

Issue:
SHA-1 hash algorithm enabled on SFTP service
## NOTE
I found that MinIO has added support for [SFTP](https://min.io/docs/minio/linux/developers/file-transfer-protocol.html) however with quick testing with [ssh-audit](https://github.com/jtesta/ssh-audit) told me that SHA-1 hash algorithm looks be enabled by default and I didn't found way to disable it.

## Expected Behavior
SHA-1 is considered as weak algorithm so it would be better to default to have disabled with possibility to enable it by those who really need it.

## Current Behavior
ssh-audit command output:
```
(kex) diffie-hellman-group14-sha1    -- [fail] using broken SHA-1 hash algorithm
```



Discussion / hints:
@aead I don't expect you to disagree. But just checking.
I'll add this to my list, got a couple of things to take care of first though

Fix patch:
diff --git a/cmd/sftp-server.go b/cmd/sftp-server.go
index 04fa336a86175..e68aeadb5bc7d 100644
--- a/cmd/sftp-server.go
+++ b/cmd/sftp-server.go
@@ -40,6 +40,85 @@ func (s *sftpLogger) Info(tag xsftp.LogType, msg string) {
 	logger.Info(msg)
 }
 
+const (
+	kexAlgoDH1SHA1                = "diffie-hellman-group1-sha1"
+	kexAlgoDH14SHA1               = "diffie-hellman-group14-sha1"
+	kexAlgoDH14SHA256             = "diffie-hellman-group14-sha256"
+	kexAlgoDH16SHA512             = "diffie-hellman-group16-sha512"
+	kexAlgoECDH256                = "ecdh-sha2-nistp256"
+	kexAlgoECDH384                = "ecdh-sha2-nistp384"
+	kexAlgoECDH521                = "ecdh-sha2-nistp521"
+	kexAlgoCurve25519SHA256LibSSH = "curve25519-sha256@libssh.org"
+	kexAlgoCurve25519SHA256       = "curve25519-sha256"
+
+	chacha20Poly1305ID = "chacha20-poly1305@openssh.com"
+	gcm256CipherID     = "aes256-gcm@openssh.com"
+	aes128cbcID        = "aes128-cbc"
+	tripledescbcID     = "3des-cbc"
+)
+
+// https://cs.opensource.google/go/x/crypto/+/refs/tags/v0.22.0:ssh/common.go;l=46
+// preferredKexAlgos specifies the default preference for key-exchange
+// algorithms in preference order. The diffie-hellman-group16-sha512 algorithm
+// is disabled by default because it is a bit slower than the others.
+var preferredKexAlgos = []string{
+	kexAlgoCurve25519SHA256, kexAlgoCurve25519SHA256LibSSH,
+	kexAlgoECDH256, kexAlgoECDH384, kexAlgoECDH521,
+	kexAlgoDH14SHA256, kexAlgoDH14SHA1,
+}
+
+// supportedKexAlgos specifies the supported key-exchange algorithms in
+// preference order.
+// https://cs.opensource.google/go/x/crypto/+/refs/tags/v0.22.0:ssh/common.go;l=44
+var supportedKexAlgos = []string{
+	kexAlgoCurve25519SHA256, kexAlgoCurve25519SHA256LibSSH,
+	// P384 and P521 are not constant-time yet, but since we don't
+	// reuse ephemeral keys, using them for ECDH should be OK.
+	kexAlgoECDH256, kexAlgoECDH384, kexAlgoECDH521,
+	kexAlgoDH14SHA256, kexAlgoDH16SHA512, kexAlgoDH14SHA1,
+	kexAlgoDH1SHA1,
+}
+
+// supportedPubKeyAuthAlgos specifies the supported client public key
+// authentication algorithms. Note that this doesn't include certificate types
+// since those use the underlying algorithm. This list is sent to the client if
+// it supports the server-sig-algs extension. Order is irrelevant.
+// https://cs.opensource.google/go/x/crypto/+/refs/tags/v0.22.0:ssh/common.go;l=142
+var supportedPubKeyAuthAlgos = []string{
+	ssh.KeyAlgoED25519,
+	ssh.KeyAlgoSKED25519, ssh.KeyAlgoSKECDSA256,
+	ssh.KeyAlgoECDSA256, ssh.KeyAlgoECDSA384, ssh.KeyAlgoECDSA521,
+	ssh.KeyAlgoRSASHA256, ssh.KeyAlgoRSASHA512, ssh.KeyAlgoRSA,
+	ssh.KeyAlgoDSA,
+}
+
+// supportedCiphers lists ciphers we support but might not recommend.
+// https://cs.opensource.google/go/x/crypto/+/refs/tags/v0.22.0:ssh/common.go;l=28
+var supportedCiphers = []string{
+	"aes128-ctr", "aes192-ctr", "aes256-ctr",
+	"aes128-gcm@openssh.com", gcm256CipherID,
+	chacha20Poly1305ID,
+	"arcfour256", "arcfour128", "arcfour",
+	aes128cbcID,
+	tripledescbcID,
+}
+
+// preferredCiphers specifies the default preference for ciphers.
+// https://cs.opensource.google/go/x/crypto/+/refs/tags/v0.22.0:ssh/common.go;l=37
+var preferredCiphers = []string{
+	"aes128-gcm@openssh.com", gcm256CipherID,
+	chacha20Poly1305ID,
+	"aes128-ctr", "aes192-ctr", "aes256-ctr",
+}
+
+// supportedMACs specifies a default set of MAC algorithms in preference order.
+// This is based on RFC 4253, section 6.4, but with hmac-md5 variants removed
+// because they have reached the end of their useful life.
+// https://cs.opensource.google/go/x/crypto/+/refs/tags/v0.22.0:ssh/common.go;l=85
+var supportedMACs = []string{
+	"hmac-sha2-256-etm@openssh.com", "hmac-sha2-512-etm@openssh.com", "hmac-sha2-256", "hmac-sha2-512", "hmac-sha1", "hmac-sha1-96",
+}
+
 func (s *sftpLogger) Error(tag xsftp.LogType, err error) {
 	switch tag {
 	case xsftp.AcceptNetworkError:
@@ -53,13 +132,41 @@ func (s *sftpLogger) Error(tag xsftp.LogType, err error) {
 	}
 }
 
+func filterAlgos(arg string, want []string, allowed []string) []string {
+	var filteredAlgos []string
+	found := false
+	for _, algo := range want {
+		if len(algo) == 0 {
+			continue
+		}
+		for _, allowedAlgo := range allowed {
+			algo := strings.ToLower(strings.TrimSpace(algo))
+			if algo == allowedAlgo {
+				filteredAlgos = append(filteredAlgos, algo)
+				found = true
+				break
+			}
+		}
+		if !found {
+			logger.Fatal(fmt.Errorf("unknown algorithm %q passed to --sftp=%s\nValid algorithms: %v", algo, arg, strings.Join(allowed, ", ")), "unable to start SFTP server")
+		}
+	}
+	if len(filteredAlgos) == 0 {
+		logger.Fatal(fmt.Errorf("no valid algorithms passed to --sftp=%s\nValid algorithms: %v", arg, strings.Join(allowed, ", ")), "unable to start SFTP server")
+	}
+	return filteredAlgos
+}
+
 func startSFTPServer(args []string) {
 	var (
 		port          int
 		publicIP      string
 		sshPrivateKey string
 	)
-
+	allowPubKeys := supportedPubKeyAuthAlgos
+	allowKexAlgos := preferredKexAlgos
+	allowCiphers := preferredCiphers
+	allowMACs := supportedMACs
 	var err error
 	for _, arg := range args {
 		tokens := strings.SplitN(arg, "=", 2)
@@ -82,6 +189,14 @@ func startSFTPServer(args []string) {
 			publicIP = host
 		case "ssh-private-key":
 			sshPrivateKey = tokens[1]
+		case "pub-key-algos":
+			allowPubKeys = filterAlgos(arg, strings.Split(tokens[1], ","), supportedPubKeyAuthAlgos)
+		case "kex-algos":
+			allowKexAlgos = filterAlgos(arg, strings.Split(tokens[1], ","), supportedKexAlgos)
+		case "cipher-algos":
+			allowCiphers = filterAlgos(arg, strings.Split(tokens[1], ","), supportedCiphers)
+		case "mac-algos":
+			allowCiphers = filterAlgos(arg, strings.Split(tokens[1], ","), supportedMACs)
 		}
 	}
 
@@ -106,6 +221,12 @@ func startSFTPServer(args []string) {
 	// An SSH server is represented by a ServerConfig, which holds
 	// certificate details and handles authentication of ServerConns.
 	sshConfig := &ssh.ServerConfig{
+		Config: ssh.Config{
+			KeyExchanges: allowKexAlgos,
+			Ciphers:      allowCiphers,
+			MACs:         allowMACs,
+		},
+		PublicKeyAuthAlgorithms: allowPubKeys,
 		PasswordCallback: func(c ssh.ConnMetadata, pass []byte) (*ssh.Permissions, error) {
 			if globalIAMSys.LDAPConfig.Enabled() {
 				sa, _, err := globalIAMSys.getServiceAccount(context.Background(), c.User())
diff --git a/docs/ftp/README.md b/docs/ftp/README.md
index de7b07eec16ee..2c6f4afd38f06 100644
--- a/docs/ftp/README.md
+++ b/docs/ftp/README.md
@@ -167,3 +167,78 @@ Unlike SFTP server, FTP server is insecure by default. To operate under TLS mode
 > certs from the server certificate chain, this is mainly to add simplicity of setup. However if you wish to terminate
 > TLS certificates via a different domain for your FTP servers you may choose the above command line options.
 
+
+### Custom Algorithms (SFTP)
+
+Custom algorithms can be specified via command line parameters.
+Algorithms are comma separated. 
+Note that valid values does not in all cases represent default values.
+
+`--sftp=pub-key-algos=...` specifies the supported client public key
+authentication algorithms. Note that this doesn't include certificate types
+since those use the underlying algorithm. This list is sent to the client if
+it supports the server-sig-algs extension. Order is irrelevant.
+
+Valid values
+```
+ssh-ed25519
+sk-ssh-ed25519@openssh.com
+sk-ecdsa-sha2-nistp256@openssh.com
+ecdsa-sha2-nistp256
+ecdsa-sha2-nistp384
+ecdsa-sha2-nistp521
+rsa-sha2-256
+rsa-sha2-512
+ssh-rsa
+ssh-dss
+```
+
+`--sftp=kex-algos=...` specifies the supported key-exchange algorithms in preference order.
+
+Valid values: 
+
+```
+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
+```
+
+`--sftp=cipher-algos=...` specifies the allowed cipher algorithms. 
+If unspecified then a sensible default is used.
+
+Valid values: 
+```
+aes128-ctr
+aes192-ctr
+aes256-ctr
+aes128-gcm@openssh.com
+aes256-gcm@openssh.com
+chacha20-poly1305@openssh.com
+arcfour256
+arcfour128
+arcfour
+aes128-cbc
+3des-cbc
+```
+
+`--sftp=mac-algos=...` specifies a default set of MAC algorithms in preference order.
+This is based on RFC 4253, section 6.4, but with hmac-md5 variants removed because they have 
+reached the end of their useful life.
+
+Valid values: 
+
+```
+hmac-sha2-256-etm@openssh.com
+hmac-sha2-512-etm@openssh.com
+hmac-sha2-256
+hmac-sha2-512
+hmac-sha1
+hmac-sha1-96
+```
+


Test patch: