go: x/crypto/ssh: publicKeyCallback cannot handshake using ssh-rsa keys signed using the ssh-rsa-sha2-256 algorithm
This relates to:
x/crypto/ssh: cannot sign certificate with different algorithm #36261 x/crypto/ssh: support RSA SHA-2 host key signatures #37278
What version of Go are you using (go version)?
1.14.2
Does this issue reproduce with the latest release?
yes
What operating system and processor architecture are you using (go env)?
go env Output
$ go env GO111MODULE="" GOARCH="amd64" GOBIN="" GOCACHE="/Users/mmarsh/Library/Caches/go-build" GOENV="/Users/mmarsh/Library/Application Support/go/env" GOEXE="" GOFLAGS="" GOHOSTARCH="amd64" GOHOSTOS="darwin" GOINSECURE="" GONOPROXY="" GONOSUMDB="" GOOS="darwin" GOPATH="/Users/mmarsh/go" GOPRIVATE="" GOPROXY="https://proxy.golang.org,direct" GOROOT="/usr/local/go" GOSUMDB="sum.golang.org" GOTMPDIR="" GOTOOLDIR="/usr/local/go/pkg/tool/darwin_amd64" GCCGO="gccgo" AR="ar" CC="clang" CXX="clang++" CGO_ENABLED="1" GOMOD="/Users/mmarsh/go/src/golang.org/x/crypto/go.mod" CGO_CFLAGS="-g -O2" CGO_CPPFLAGS="" CGO_CXXFLAGS="-g -O2" CGO_FFLAGS="-g -O2" CGO_LDFLAGS="-g -O2" PKG_CONFIG="pkg-config" GOGCCFLAGS="-fPIC -m64 -pthread -fno-caret-diagnostics -Qunused-arguments -fmessage-length=0 -fdebug-prefix-map=/var/folders/8t/0yb5q0_x6mb2jldqq_vjn3lr0000gn/T/go-build245084723=/tmp/go-build -gno-record-gcc-switches -fno-common"
What did you do?
Here’s a gist containing code to reproduce this issue, provided you have an instance to connect to that’s similarly set up to mine. I’ve got full steps to create such an instance in the gist’s README.md.
https://gist.github.com/SwampDragons/8b913208add452f1b50f6f47426ac329
What did you expect to see?
I expected to be able to connect to the instance using a custom AlgorithmSigner.
I am able to connect to an instance before the crypto policy on that instance is updated to deny keys signed using the “ssh-rsa” algorithm. Note that the policy being applied does accept the ssh-rsa keys if they are instead signed with the “rsa-sha2-256” algorithm. Theoretically, I should be able to create my own AlgorithmSigner to apply this algorithm to my key.
What did you see instead?
I get denied access to the instance with an authentication error.
I have traced this to the validateKey and publicKeyCallback methods in ssh/client_auth. These methods assume that the algorithm is always the same as the key type, which is not the case in my situation.
Possible Solutions
Create a new interface, AlgorithmSignerWithAlgo, which has the method Algorithm(). When called, Algorithm() will return the type of algorithm used, so that publicKeyCallback can set this field accurately. We will also need to update the validateKey method to not return an error if the algorithm used to sign the validation request doesn’t match the key type.
Here’s a diff of a lightweight solution that enables users to implement their own signers that the default publicKeyCallback can use to correctly handshake: https://github.com/SwampDragons/crypto/pull/1/files
About this issue
- Original URL
- State: closed
- Created 4 years ago
- Reactions: 36
- Comments: 24 (8 by maintainers)
Links to this issue
Commits related to this issue
- explicitly allow sshd to accept ssh-rsa Due to https://github.com/golang/go/issues/39885 if we only allow rsa-sha2-256,rsa-sha2-512 golang/x/crypto doesn't work as it presents the key as a regular ss... — committed to earthly/earthly by alexcb 2 years ago
- explicitly allow sshd to accept ssh-rsa (#1633) Due to https://github.com/golang/go/issues/39885 if we only allow rsa-sha2-256,rsa-sha2-512 golang/x/crypto doesn't work as it presents the key as a ... — committed to earthly/earthly by alexcb 2 years ago
- ssh: support rsa-sha2-256/512 for client authentication CL 220037 had implemented support for host authentication using rsa-sha2-256/512, but not client public key authentication. OpenSSH disabled th... — committed to CircleCI-Public/golang-crypto by FiloSottile 2 years ago
- ssh: support rsa-sha2-256/512 for client authentication CL 220037 had implemented support for host authentication using rsa-sha2-256/512, but not client public key authentication. OpenSSH disabled th... — committed to CircleCI-Public/golang-crypto by FiloSottile 2 years ago
- ssh: support rsa-sha2-256/512 for client certificates The server-sig-algs logic was not working for certificate algorithms. Follow-up on CL 392394. Tested with OpenSSH 8.8 configured with Pubke... — committed to golang/crypto by FiloSottile 2 years ago
- ssh: support rsa-sha2-256/512 for client certificates The server-sig-algs logic was not working for certificate algorithms. Follow-up on CL 392394. Tested with OpenSSH 8.8 configured with Pubke... — committed to jorgemarey/crypto by FiloSottile 2 years ago
- fix(e2e): upgrade golang.org/x/crypto to support rsa-sha2-256/512 OpenSSH disabled the SHA-1 based ssh-rsa by default in version 8.8. See more: golang/go#39885 Signed-off-by: zwtop <wang.zhan@smart... — committed to zwtop/everoute by zwtop 2 years ago
- fix(e2e): upgrade golang.org/x/crypto to support rsa-sha2-256/512 OpenSSH disabled the SHA-1 based ssh-rsa by default in version 8.8. See more: golang/go#39885 Signed-off-by: zwtop <wang.zhan@smart... — committed to zwtop/everoute by zwtop 2 years ago
- fix(e2e): upgrade golang.org/x/crypto to support rsa-sha2-256/512 OpenSSH disabled the SHA-1 based ssh-rsa by default in version 8.8. See more: golang/go#39885 Signed-off-by: zwtop <wang.zhan@smart... — committed to zwtop/everoute by zwtop 2 years ago
- fix(e2e): upgrade golang.org/x/crypto to support rsa-sha2-256/512 OpenSSH disabled the SHA-1 based ssh-rsa by default in version 8.8. See more: golang/go#39885 Signed-off-by: zwtop <wang.zhan@smart... — committed to everoute/everoute by zwtop 2 years ago
- fix(e2e): upgrade golang.org/x/crypto to support rsa-sha2-256/512 OpenSSH disabled the SHA-1 based ssh-rsa by default in version 8.8. See more: golang/go#39885 Signed-off-by: zwtop <wang.zhan@smart... — committed to everoute/everoute by zwtop 2 years ago
- ssh: support rsa-sha2-256/512 for client authentication CL 220037 had implemented support for host authentication using rsa-sha2-256/512, but not client public key authentication. OpenSSH disabled th... — committed to LewiGoddard/crypto by FiloSottile 2 years ago
- ssh: support rsa-sha2-256/512 for client certificates The server-sig-algs logic was not working for certificate algorithms. Follow-up on CL 392394. Tested with OpenSSH 8.8 configured with Pubke... — committed to LewiGoddard/crypto by FiloSottile 2 years ago
- ssh: support rsa-sha2-256/512 for client authentication CL 220037 had implemented support for host authentication using rsa-sha2-256/512, but not client public key authentication. OpenSSH disabled th... — committed to BiiChris/crypto by FiloSottile 2 years ago
Hi, I know it’s annoying having someone nagging at an issue, but it’s been a while and having some kind of solution to this is a blocker for some people using Packer. Is there any way @hanwen or @FiloSottile could take a look at the patch I’ve submitted?
Would love to see this reviewed, it is a blocker for me with packer. Thanks!
Fundamentally this is an issue with the
ssh.Signertype and/or its use inhandshake.go.The issue is that the type
ssh.Signer:Doesn’t provide a mechanism to say which type of algorithm is supported by this Signer - and its assumed incorrectly that that is the PublicKey type and although the
AlgorithmSignerdoes provide a different signing method it’s still not saying which type of Signer it is:And so it is not wired into handshake.go.
The Signer interface would actually need to be something like:
This would then allow backwards compatibility with the current system and allow handshake.go to be changed to:
A couple of utility types and associated functions would need to be provided - the most simple being one that takes an
AlgorithmSignerand creates aTypeSignerfor a given algorithm.A
MultipleAlgorithmSignercould be created as below but it would be more complex to wire in:My suspicion is that this would be better used as a way of an
AlgorithmSigneradvertising whichTypeSignerthey could become rather than being used directly inhandshake.go. The idea being that it would be good if there was some programmatic way of listing all of the things could be supported. This could at first be used with some utility function to provide Signers for everything but later cut down to only support specific Signers.At present there is no programmatic way of determining which algorithms are supported.
Edit: This is only part of the problem – we also need RFC8308 supported. (see #49269)
The linked PR only appears to implement handshake for the client.
Is it intended to do this for the server part?
Yeah I think that’s a better approach too. This patchset was a very timid “I don’t want to muck around too much inside anything or change any current behaviors without explicit interaction” approach, which I’d hoped would help get it adopted faster since it seemed pretty low risk to current users.
That said, I bet we could simply have the validateKey check determine whether the PublicKey.Type() is SigAlgoRSA (“ssh-rsa”), and if the validation fails try again with SigAlgoRSASHA2256 (“rsa-sha2-256”) instead. It would be a lot less code and prevent users from having to implement this.
Thanks! The test code in https://github.com/golang/go/issues/39885#issuecomment-835233610 now works after upgrading to:
golang.org/x/crypto v0.0.0-20220314234724-5d542ad81a58.This still affects golang 1.17 and we’ve recently noticed this on Gitea https://github.com/go-gitea/gitea/issues/17175
The underlying problem is that:
https://github.com/golang/crypto/blob/089bfa5675191fd96a44247682f76ebca03d7916/ssh/handshake.go#L459-L462
Makes the assumption that a public key type is the same as the key algorithm that that key can produce. There’s not really any way of defining what the algorithms are separate from these keys - and in fact once we start needing to completely deprecate the
ssh-rsamethod there doesn’t appear to be a way of defining which Algorithms we support separate from the keys.In Gitea the workaround I’ve come up with is to simply wrap around the
Signerand itsPublicKeyto shadow theType()and switch the algorithm to the alternative as per theType().See https://github.com/go-gitea/gitea/pull/17281
OpenSSH 8.8 was released on 2021-09-26. RSA + SHA-1 has now been disabled by default (release notes):
SHA-256 and SHA-512 have been supported by SSH servers for many years now. The Go SSH package should default to using SHA-256 to avoid ongoing connectivity failures with well maintained servers.
Getting back up to speed, as RFC8332 and RFC8308 are much newer than my previous ssh work. It looks like the correct negotiation here is to implement
server-sig-algsto get the supported algorithms before polling for an accepted key algorithm, to avoid auth penalties with certain server configurations. Agent usage often encounters these limits already (which is a primary use for theIdentityFileopenssh option with an agent), and tripling the requests is likely to exacerbate the problem significantly.The key types should however still be decoupled from the algorithm name, as the public key format and algorithm were only the same by coincidence and
AlgorithmSigneralone can’t break that association.If I understand correctly, I think in this case the ssh client should be responsible for the signing algorithm negotiation. Because the user may not know if the server configuration will accept the legacy format or not, I don’t think think we can place the burden on the user to determine the signing algorithm, plus it’s kind of cumbersome since changing the algorithm for most keys isn’t valid anyway. The
AlgorithmSignerWithAlgoalso has the drawback of not working with agent signers, which have been the most common in my experience.It looks to me like we should be able to easily negotiate the signing algorithm while verifying the allowed public keys with the server, and pass the discovered algorithm along. We probably need to add
SignWithAlgorithmto the agent signer, and have some special handling of Certificates to deal with the nist names they have wrapped around the public key types, but I think that might be a better approach to solve the issue transparently for users.