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