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

New module utils openssh.certificate #246

Merged

Conversation

Ajpantuso
Copy link
Collaborator

SUMMARY

Adding utilities and classes for directly parsing OpenSSH certificates.

ISSUE TYPE
  • Feature Pull Request
COMPONENT NAME

plugins/module_utils/openssh/certificate.py

ADDITIONAL INFORMATION

Given that ssh-keygen does not automate well and the lack of support from common Python libraries implementing SSH I have implemented OpenSSH style certificates in python directly. The original idea was to utilize either cryptography or paramiko, but even the backend utilities of these libraries do not fully implement SSH certificates. Starting from where these libraries left off I introduced a "poor man's" reader/parser and writer for SSH data formats as utilities. The primary interface is exposed in certificate.py as OpensshCertificate which can load relevant certificate information for each supported key type as well as the signing key and signature.

What was left out:

  • Certificate generation (This requires nonce generation which can be done via secrets, but also key signing which would make cryptography a dependency)
  • Validations for pre-defined critical_options or extensions (Modules should handle this directly)
  • Custom exception classes (Would consider this for the future, but would refactor all exceptions into a single file shared under module_utils/openssh

@Ajpantuso Ajpantuso changed the title [WIP] New module utils openssh.certificate New module utils openssh.certificate Jun 10, 2021
@Ajpantuso Ajpantuso marked this pull request as ready for review June 10, 2021 00:47
@felixfontein felixfontein mentioned this pull request Jun 11, 2021
1 task
Copy link
Contributor

@felixfontein felixfontein left a comment

Choose a reason for hiding this comment

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

Thanks for your contribution!

I'm not really familiar with the OpenSSH file formats, so I mainly looked at the code itself and not whether it's OpenSSH file handling is correct.

I'm wondering a bit whether it's a good idea to do that much parsing here. It might be better to get this (resp. something like this) placed in cryptography, because a) it gets much more thoroughly checked there, and b) by many more eyes than here.

plugins/module_utils/openssh/certificate.py Outdated Show resolved Hide resolved
plugins/module_utils/openssh/utils.py Outdated Show resolved Hide resolved
plugins/module_utils/openssh/utils.py Show resolved Hide resolved
@Ajpantuso
Copy link
Collaborator Author

Thanks for your contribution!

I'm not really familiar with the OpenSSH file formats, so I mainly looked at the code itself and not whether it's OpenSSH file handling is correct.

I'm wondering a bit whether it's a good idea to do that much parsing here. It might be better to get this (resp. something like this) placed in cryptography, because a) it gets much more thoroughly checked there, and b) by many more eyes than here.

I agree cryptography is the better place for this functionality and I can work on implementing it there, but my guess is that would be a much larger discussion and effort.

So my justifications for doing it here first are:

  • This does not include the actual cryptographic elements such as generation, signing, and verifying
  • This only supplements ssh-keygen to facilitate idempotency checks which the output of ssh-keygen makes very difficult
  • It would avoid a dependency on cryptography which should only be invoked when an interface to OpenSSL is required IMO

@felixfontein
Copy link
Contributor

* This does not include the actual cryptographic elements such as generation, signing, and verifying
* This only supplements `ssh-keygen` to facilitate idempotency checks which the output of `ssh-keygen` makes very difficult

That sounds good.

In that case, I would remove the writer part of the PR, though, to make sure nobody starts abusing it to create / modify objects in the future :)

* It would avoid a dependency on cryptography which should only be invoked when an interface to `OpenSSL` is required IMO

In the beginning the module must still work without a dependency on some library that's not widely available on common distros anyway. (It takes years until that's the case.) I think it's still a good long-term goal, and once a cryptography version widely available supports this, I don't see why we can't rely on it. (Hopefully such a version will also support more operations so we can eventually get rid of ssh-keygen :) )

@Ajpantuso
Copy link
Collaborator Author

* This does not include the actual cryptographic elements such as generation, signing, and verifying
* This only supplements `ssh-keygen` to facilitate idempotency checks which the output of `ssh-keygen` makes very difficult

That sounds good.

In that case, I would remove the writer part of the PR, though, to make sure nobody starts abusing it to create / modify objects in the future :)

* It would avoid a dependency on cryptography which should only be invoked when an interface to `OpenSSL` is required IMO

In the beginning the module must still work without a dependency on some library that's not widely available on common distros anyway. (It takes years until that's the case.) I think it's still a good long-term goal, and once a cryptography version widely available supports this, I don't see why we can't rely on it. (Hopefully such a version will also support more operations so we can eventually get rid of ssh-keygen :) )

I do need several of the writer's functions to determine public key fingerprints:

    def public_key_fingerprint(self):
        if any([self.e is None, self.n is None]):
            return b''

        writer = OpensshWriter()
        writer.string(_SSH_TYPE_STRINGS['rsa'])
        writer.mpint(self.e)
        writer.mpint(self.n)

        return fingerprint(writer.bytes())

I put a warning in the docstring if that's acceptable.
Otherwise I can move the required functions into certificate.py and name them to be unexported.

@felixfontein
Copy link
Contributor

I do need several of the writer's functions to determine public key fingerprints:

Ah, I somehow missed these when grepping.

I put a warning in the docstring if that's acceptable.
Otherwise I can move the required functions into certificate.py and name them to be unexported.

How about keeping them in that file, but making them internal by renaming OpensshWriter to _OpensshWriter and mentioning in the docstring that this is a private API that must not be used except by the openssh module_utils?

@Ajpantuso
Copy link
Collaborator Author

How about keeping them in that file, but making them internal by renaming OpensshWriter to _OpensshWriter and mentioning in the docstring that this is a private API that must not be used except by the openssh module_utils?

Done, thanks.

@felixfontein felixfontein merged commit 5d153e0 into ansible-collections:main Jun 22, 2021
@felixfontein
Copy link
Contributor

@Ajpantuso thanks a lot for this contribution!

@Ajpantuso Ajpantuso deleted the certificate_module_utils branch June 22, 2021 12:46
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.

2 participants