Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[SSHD-1161] OpenSSH client certificate publickey authentication #194

Merged
merged 4 commits into from
May 17, 2021
Merged

[SSHD-1161] OpenSSH client certificate publickey authentication #194

merged 4 commits into from
May 17, 2021

Conversation

alex-sherwin
Copy link
Contributor

Fully implements https://issues.apache.org/jira/browse/SSHD-1161

This PR covers using OpenSSH client certificate publickey authentication from the MINA client code

It's unit tested against OpenSSH sshd using testcontainers

OpenSSH client certificate publickey authentication is an extension to https://datatracker.ietf.org/doc/html/rfc4252#section-7 as defined in https://cvsweb.openbsd.org/src/usr.bin/ssh/PROTOCOL.certkeys?annotate=HEAD

It slightly modifies the existing MINA certkeys code, which was originally added to only support host certificates. The most significant change to the existing code was renaming the OpenSshCertificate.getServerHostKey function to OpenSshCertificate.getCertPubKey, as getServerHostKey was a confusing name since a OpenSshCertificate instance can be either a server host cert or client cert.

The unit test covers the following client certificate types (which are all the types that current stable OpenSSH enables by default):

@alex-sherwin alex-sherwin changed the title Feature/sshd 1161 client certificates [SSHD-1161] OpenSSH client certificate publickey authentication May 11, 2021
Copy link
Member

@tomaswolf tomaswolf left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks very promising; unfortunately I can't get the test to run locally. Also, there's a number of minor fixable problems that fail the build.

@tomaswolf
Copy link
Member

Looks like the windows VMs used in the test builds don't have docker? We might have to make this test run conditionally on non-Windows only. Maybe with a profile that we'd enable only for the Linux builds? (If we used JUnit5, I think there'd be an @testcontainers(disabledWithoutDocker=true) annotation that could be used.)

@alex-sherwin
Copy link
Contributor Author

Hi @tomaswolf, is there a particular way you would prefer this to be done? I'm sure I can figure something out with maven profiles and the OS conditional checks, but didn't know if you had something specific pattern in mind.

Thanks,

@tomaswolf
Copy link
Member

No, not really. A profile was just the first thing that I thought of. An unsuspecting user who doesn't have docker on his machine should probably still be able to run a maven build locally and just have the tests that require testcontainers be skipped/not run. OTOH, we'd want to enable that profile in the build Github action for the Linux builds.

But maybe there are better ways to do this than via profiles. I also thought the Github Windows VMs did have docker:

Maybe it's a testcontainers problem.

@alex-sherwin
Copy link
Contributor Author

The Windows VM's do have docker, but it's windows-native docker (does anyone really use that?): actions/runner-images#1143 (comment)

They don't install any variant of Docker on Windows that wraps a Linux VM, unfortunately

tomaswolf and others added 2 commits May 14, 2021 22:43
Rename the method to getCertPubKey() in preparation of using the class
also for client certificate pubkey login.
@tomaswolf
Copy link
Member

This looks great. Thanks a lot! The PR now contains a lot of commits that leave intermediary stages in the repo and a lot of clean-up commits, and merging in master in the middle will make for a strange commit history when merged. I prefer to ultimately merge commits such that master builds at any stage, so I'd like to refactor this series of commits before merging:

  • Rebase it onto master, then:
  • One commit for the completely unrelated formatting clean-up in ReflectionUtils
  • One commit by Alec for the renaming of OpenSshCertificate.getServerHostKey()
  • One commit by you with Alec mentioned in the commit message for all the main code changes
  • One commit by you for the test and the testcontainers setup

@alex-sherwin , would it be OK with you if I did such a refactoring of the commits?

@alex-sherwin
Copy link
Contributor Author

@tomaswolf If possible could you make the commit w/ the main code changes by Alec and mention me instead?

He did stay up pretty late to sort out the final implementation :)

Other then that it sounds good to me

Drop the restriction to host keys only in OpenSshCertificate.

Add comparison of OpenSshCertificate taking into account the public
key of the certificates, their serial number, and their signatures.

Add a signature algorithm map to be able to choose the correct
signature algorithm name during pubkey authentication.

Also-by: Alex Sherwin <alex-sherwin@users.noreply.github.com>
@tomaswolf
Copy link
Member

If possible could you make the commit w/ the main code changes by Alec and mention me instead?

He did stay up pretty late to sort out the final implementation :)

Of course. Want me to force-push to your feature branch to update this PR so you can take a look before it goes in?

@alex-sherwin
Copy link
Contributor Author

That's good with me

@tomaswolf
Copy link
Member

Done. Take a look and if OK with you, I'd merge like that.

@FliegenKLATSCH
Copy link
Contributor

Nice work, two comments from my side:

  • If we have the SIGNATURE_ALGORITHM_MAP now, we should remove the mapping I introduced in the RSA classes, it's just duplicated..
  • We should still somewhere validate the type of the certificate such that a host certificate cannot be used for user authentication and vice versa.

@alex-sherwin
Copy link
Contributor Author

@tomaswolf The rebase looks good to me, I'm happy with the PR as-is

However I'm also happy to make the changes suggested by @FliegenKLATSCH, or, start a separate PR for that?

@tomaswolf
Copy link
Member

tomaswolf commented May 16, 2021

Good points. I suggest we do both in follow-up changes.

  • Validation of the certificate type would also have to happen in the server-side code (client certificate presented in pubkey auth, and don't consider and log host keys that are client certificates?) and in client-side code (host certificate in presented as host key, and in pubkey auth skip and log certificates that are not client certificates?), and need additional tests for these cases.
  • The clean-up for determining the signature algorithm name could be done separately anyway. Needs a little thought, too. I like FliegenKLATSCH's approach with the method on Signature, but unfortunately the logic in UserAuthPublicKey is a bit different and we don't have direct access to the Signatureobject there.

However, I'll make one more change in ClientOpenSSHCertificatesTest: derive it from BaseTestSupport and then use setupTestClient() instead of SshClient.setUpDefaultClient(). That way, the test will not read the real ~/.ssh/config.

Add a unit test exercising authentication with all supported *-cert
key types against a real OpenSSH server run in a docker container.

The OpenSSH server runs in an Alpine container and is primed with two
host keys, selectable via an environment variable, and two users. It
supports pubkey and password authentication.

The test case uses org.testcontainers to build a cached docker image
and to run and teardown the docker container instance. The test uses
the Apache MINA sshd client using client certificate keys to
authenticate at the OpenSSH server in the docker container.

The test is not run with the mina and netty back-ends; testcontainers
has problems resolving files when run from the re-usable test JAR. It
also isn't run on Windows; GitHub actions Windows Server VM's don't
have Docker (Linux) support.

Also note that the test makes sure that the real BouncyCastle security
provider is installed; testcontainers contains a shaded BC copy that
may fail at runtime due to being unsigned.
@tomaswolf
Copy link
Member

The build failures are unrelated. There are unstable tests. Going to merge this as is.

@tomaswolf tomaswolf merged commit baf2989 into apache:master May 17, 2021
@alex-sherwin alex-sherwin deleted the feature/SSHD-1161-client-certificates branch May 17, 2021 20:23
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants