Skip to content

Keys generated by ssh-rsa algorithm are not supported by gitlab-sshd

Overview

Public key authentication works on version 8.6 of OpenSSH, but with 8.8 it raises debug1: send_pubkey_test: no mutual signature algorithm. It happens because OpenSSH deprecates ssh-rsa for SHA1, but even ssh-rsa is using SHA256 or SHA512, it's still rejected.

➜  ssh git@staging.gitlab.com
git@staging.gitlab.com: Permission denied (publickey).

➜  ssh -o PubkeyAcceptedAlgorithms=ssh-rsa git@staging.gitlab.com
PTY allocation request failed on channel 0
Welcome to GitLab, @igor-drozdov!
Connection to staging.gitlab.com closed.

It works correctly with the current implementation of Gitlab-Shell because it uses server-side OpenSSH that provides backward-compatible mechanism: https://stackoverflow.com/questions/69656858/git-bash-ssh-connection-issue/69657512#69657512. If a client sends a key with ssh-rsa, it may also mean that sha256/sha512 is used:

 * The RFC8332 RSA SHA-2 signature algorithms rsa-sha2-256/512. These
   algorithms have the advantage of using the same key type as
   "ssh-rsa" but use the safe SHA-2 hash algorithms. These have been
   supported since OpenSSH 7.2 and are already used by default if the
   client and server support them.

Problem to solve

gitlab-sshd should accept all the public key algorithms that are accepted by Gitlab Shell at the moment to avoid disruption when gitlab-sshd is deployed on production.

Possible solution

The server should send SSH2_MSG_EXT_INFO message that contains a list of algorithms that the server agrees to accept:

debug1: SSH2_MSG_EXT_INFO received
debug1: kex_input_ext_info: server-sig-algs=<ssh-ed25519,ssh-rsa,rsa-sha2-256,rsa-sha2-512,ssh-dss,ecdsa-sha2-nistp256,ecdsa-sha2-nistp384,ecdsa-sha2-nistp521>

Currently, there is no way to do it via github.com/golang/crypto (the Go library used by gitlab-sshd to handle SSH connections) and ssh-rsa correctly is not processed correctly even if it's for sha256/sha512. There's an MR that fixes this problem: https://github.com/CircleCI-Public/golang-crypto/commit/36879566a0a1dc474d238f74fe3e3f86b8d8f2a8, but it doesn't seem that it's going to be merged soon.

The solution works locally, so as an option we can create our own fork with the fix.

Minimal solution

In order to resolve the issue, we only need the piece of code that sends the supported algorithms as an extension message on handshake:

extSupported := false
for _, kexAlgo := range clientInit.KexAlgos {
	if kexAlgo == extInfoClient {
		extSupported = true
	}
}
if extSupported && !t.extInfoSent {
	extInfo := &extInfoMsg{NumExts: 1, Ext: extServerSigAlgs, Algs: serverSigAlgs}
	if err := t.conn.writePacket(Marshal(extInfo)); err != nil {
		return err
	}
	t.extInfoSent = true
}

Full diff of the minimal solution that we need in the fork in order to resolve the issue: min-solution.diff

Edited by Igor Drozdov