cosign icon indicating copy to clipboard operation
cosign copied to clipboard

Generalized Cosign External Signer/HSM Interface

Open dbilling opened this issue 4 years ago • 20 comments

As cosign gains momentum, invariably there will be more and more requests from the community for ways to integrate into more complex key management services and solutions. Enterprises are likely to have security requirements, access control/audit restrictions, physical conditions (e.g. datacenter/cloud VMs), and existing HSM/keystore systems in place such none of the current cosign key management solutions are tenable. In addition, many enterprise level HSM systems operate under a "private key never leaves the HSM" principle, i.e. material is submitted for signing, and the HSM returns the signature to the signing requestor. cosign, as the signing requestor, would never have access to the private key at all.

Further complicating the situation is there is not just one way to communicate with an HSM/secure keystore/signing service. There are many different HSM protocols: PKCS#11, KMIP, REST, and vendor specific SDKs come to mind.

The main idea for this feature/enhancement is instead of cosign individually supporting all of these HSM interoperability cases natively inside cosign, it would be much better if cosign could develop a external signer plugin/exported function system (maybe using https://golang.org/pkg/plugin/ ?) to accommodate these situations. Then the logic within cosign could be as simple defining a set of parameters to use when calling a third party plugin with a request to sign, and wait for the signature to come back from the plugin.

Thoughts?

dbilling avatar Jun 29 '21 19:06 dbilling

Agreed on the idea, but I'm not sure using plugins is the right approach. I think cosign should be updated to work with any signing backend that provides compatibility with the crypto.Signer interface. Doing so would make it trivial to integrate PKCS#11 and others in the future. Which integrations are brought into the core cosign project would be left up to the maintainers.

jalseth avatar Jun 30 '21 16:06 jalseth

Go plugins are slightly problematic for this use-case. I don't know enough about PCKS11, but is that a generic enough API to work with most remote systems?

dlorenc avatar Jun 30 '21 17:06 dlorenc

PKCS#11 is a standard API for communicating with hardware crypto modules. HSM vendors provide a PKCS#11 module, which is a shared library that exports the expected functions, and then handles any device-specific logic for communicating with the vendor's device. With this, any software that supports PKCS#11 can now work with a large variety of hardware crypto modules from various vendors.

jalseth avatar Jun 30 '21 17:06 jalseth

As the author of the issue, I can state definitively that I do not want or need PKCS#11 for my use case (although others might), rather I need to have cosign interoperate with a signer that works over a custom REST API. I think @jalseth nailed it when he said "cosign should be updated to work with any signing backend that provides compatibility with the crypto.Signer interface". In my particular case I would likely need write a custom shared library that would interoperate with our REST-based HSM setup. The only reason I mentioned plugins was to allow this customization capability -- if go interfaces can do the same thing via shared libraries, and folks can write their own customizations I'm all for it. Even better if that same infra can be used to build-out whatever other external signer protocols might be needed, PKCS#11 or whatever.

dbilling avatar Jun 30 '21 18:06 dbilling

I would use PKCS#11, so I'd like to see support for it added to cosign, but like I said as long as cosign supports crypto.Signer that's what really matters as I can write the glue to PKCS#11 myself if I need to. Without that though, I'd have to maintain a fork which can turn into a headache pretty quick.

jalseth avatar Jun 30 '21 22:06 jalseth

I think I understand now, thanks everyone for the discussion! This is a case of the standard in tree vs out of tree go extensibility problem :)

crypto.Signer is a good place to start as a rule for how we'll add other methods. As for when things go in tree, I think we'll have to establish some criteria. I don't want to be overly cautious here, but at the same time we can't just add direct support for anything in the world.

I would really like to avoid any external plugin system if possible, and instead try to lean on standard unix composability for the cases where we just can't get something in-tree. Cosign has commands already to generate unsigned payloads to disk or stdout, which can be signed by anything. They can then be uploaded to a registry with other existing commands. We have the same "extensibility escape hatches" for verification as well.

For in tree signers, here are some top level principles:

  • Demand! Code that no one is going to use is a drain. Think "I would use this" vs. "I could imagine someone using this"
  • Maintainability. I don't think this is a huge concern, but the cosign maintainers have to be comfortable understanding and maintaining the code. We can always add more maintainers, too :)
  • Security. This one should be obvious. Let's not merge dangerous or insecure things if we can avoid it.
  • Open-ness. I don't mind merging code to work with proprietary systems (see the cloud kms stuff), but ideally these would work over standard APIs and be reasonably interoperable. If pkcs11 doesn't work for everything, we can always make up our own interfaces :)

Am I missing anything else?

dlorenc avatar Jul 01 '21 10:07 dlorenc

Hi!

Somewhat tangentially related to the original question - you could access hardware backends using Parsec. We're building this CNCF project with the exact goal of allowing applications abstracted access to crypto backends using high-level libraries written in whatever programming language is needed. Container/workload signing was actually one of the initial goals of the project, so our implementation so far is mainly aimed at that, i.e. focusing on signing and verification.

Currently we have backend support for PKCS11 v2.40, TPM 2.0, ATECCx08 devices, Trusted Services. You can see a summary of what operations/algorithms we support for each backend here.

Access to the Parsec service is through IPC (currently only Unix domain sockets) and we have a WIP Go client. The API is based on the PSA Crypto API. Essentially, you'd be able to access all of those backends using the same interface. You can find more info about the service here. You can also find some links to a few recorded presentations about the project here - tends to be easier than just reading lots of docs.

If this sounds of interest to you or if you'd like to get in touch for any questions, I'm happy to answer here or on our public Slack channel (#parsec in the CNCF workspace, more details here)!

ionut-arm avatar Aug 09 '21 22:08 ionut-arm

Supporting PKCS11 is all that is really needed here. For HSMs (or other cryptographic tokens/services) that provide a PKCS11 library, users will be good to go out-of-the-box without the need to custom code anything (assuming the PKCS11 library implements everything it needs to). For users that need to do something more custom (e.g., REST, gRPC, etc.), they can write their own PKCS11 library that does what they need and then integrate that way. The end result is that all use cases are covered with a single interface and, for many use cases, users won't have to write any custom code at all.

Alternatively, as mentioned above, you could create Signer and Keystore interfaces and then provide a default PKCS11 implementation of them. This would be very similar to how Java provides the SunPKCS11 JCE provider. This would satisfy the requirement but it seems like more work for mostly the same result. The only benefit would be that users could provide their custom implementations without having to create a PKCS11 library (although they would still have to write their own code).

garantir-km avatar Aug 15 '21 19:08 garantir-km

Can we bump this up in priority? We have lots of big companies that want to use cosign but the lack of ability to integrate with their existing signing infrastructure is holding them back, and everyone would prefer to not have to fork their own version of cosign. I still believe that the simplest solution that satisfies everyone's needs and reduces the amount of custom coding required is by providing a PKCS11 integration. However, I would settle for a generic interface that we could write our own integrations to.

garantir-km avatar Sep 26 '21 22:09 garantir-km

PKCS11 should work. I'm not sure when we'll get to it, but happy to help if you're interested in giving it a try!

dlorenc avatar Sep 26 '21 22:09 dlorenc

I think I can give it a shot (or assign it to one of our engineers) at the beginning of October, once this quarter is over.

garantir-km avatar Sep 27 '21 02:09 garantir-km

As the original author of this issue, I am super excited to see evidence others like me looking for a way to get from cosign to HSM systems for remote signature operations. The desire to see this happening sooner rather than later is also super encouraging. However, I'm not really on board with the assessment that seems to have formed that anyone that needs can get to an HSM should get there through PKCS#11. We can tunnel TCP/IP through Appletalk, but that doesn't mean it's a good idea. ;-) The whole idea of the initial phrasing of the issue was to really inspire some thought into designing flexibility into the HSM approach so we can satisfy more than one camp here... I'm not an expert on go and certainly I must have used the wrong word "plugin" that freaked folks out, but all I really meant was to design with flexibility in mind and make it easy for folks to integrate what they need. jalseth's post made tons of sense to me, as did all the posts up to the one that said "PKCS#11 is all that's needed here" I could have written the original issue "REST is all that's needed here" ;-) But I was trying to be more forward-thinking than that...

dbilling avatar Sep 27 '21 17:09 dbilling

First off, my apologies to @dbilling - I realize my post came across somewhat arrogant and that wasn't my intention. Also, for what it is worth, my company wants to integrate via a REST API as well.

I had a few of goals in mind when I wrote my comment about PKCS11:

  • Minimize the amount of custom coding required for the majority of people
  • Avoid creating a cosign-specific interface for cryptographic processing
  • Keep it simple and generic

Given that almost all HSMs and key managers come with a PKCS11 library, it seems to make sense to leverage the work done by the HSM providers. Even though most of the cloud KMS offerings don't offer PKCS11 libraries, cosign already supports them so we don't need to worry about them.

Assuming that we want to provide the ability for end users to leverage their existing PKCS11 libraries, I see two reasonable approaches:

  1. Integrate cosign directly via PKCS11
  2. Use an intermediary interface (e.g., interfaces defined in Go's crypto package) and then provide a default implementation of that interface that leverages PKCS11 (e.g., crypto11, pkcs11key, etc.). Note: I believe this is what @jalseth originally suggested.

If we went with option 1, end users that wanted to integrate via REST (or gRPC, or something else), would have to write a PKCS11 library that implements the functionality they want. If we went with option 2, those same users would have the choice to write their functionality in Go or via a PKCS11 library.

Honestly, I am just happy to see the capability come to cosign and can go either way on this. If it keeps everyone happy and leads to the changes being merged faster, I am happy to try my hand at option 2 above.

Thoughts?

garantir-km avatar Sep 27 '21 20:09 garantir-km

I would like to get started on this next week but obviously want to make sure that the work will get merged. I believe option 2 satisfies everyone's needs. If you object to option 2, could you please reply on here in the next few days? Thanks!

garantir-km avatar Oct 15 '21 21:10 garantir-km

It has been brought to my attention by one of our engineers that there is already the CertSignVerifier interface which implement's sigstore's SignerVerifier interface, and it is used by cosign. Since this is already a generic interface, is the work needed for this already done?

garantir-km avatar Oct 18 '21 19:10 garantir-km

It has been brought to my attention by one of our engineers that there is already the CertSignVerifier interface which implement's sigstore's SignerVerifier interface, and it is used by cosign. Since this is already a generic interface, is the work needed for this already done?

I think we're missing an "out of tree" extensibility model today. This interface would still require getting things upstreamed here into cosign. I'd like to be flexible in cosign and merge implementations that would see wide usage, but that won't be enough for every use case.

dlorenc avatar Oct 18 '21 19:10 dlorenc

We might be also interested on getting support for HSM.

hectorj2f avatar Oct 18 '21 20:10 hectorj2f

I think we're missing an "out of tree" extensibility model today. This interface would still require getting things upstreamed here into cosign. I'd like to be flexible in cosign and merge implementations that would see wide usage, but that won't be enough for every use case.

I don't want to sound like a broken record, but that's exactly what a PKCS#11 integration would provide. Cosign would be able to offload cryptographic processing to any PKCS#11 library and developers could implement their application-specific needs in their PKCS#11 libraries (and/or downstream cryptographic services). To do this, we would provide an implementation of the CertSignVerifier interface that is able to load and make use of PKCS#11 libraries.

The alternative is to create a provider architecture where each provider implements the CertSignVerifier interface. Users could specify which provider to use when they invoke cosign (likely via a new command line argument although we could also use an environment variable or a naming convention on the value specified for the --key argument). This would be similar to how Java has JCE/JCA providers and OpenSSL has providers (the successor to OpenSSL engines).

Both solutions provide the same result - developers are able to integrate cosign with any external HSM, cryptographic token, etc. that they want. However, the method of integration would differ (i.e., PKCS#11 library vs Go implementation of the CertSignVerifier interface). Of course, with some additional work you could mix the two solutions by providing a provider architecture along with a built-in provider that supports PKCS#11.

garantir-km avatar Oct 18 '21 21:10 garantir-km

Free time is getting harder to come by so we are going to go ahead and get started on this with a PKCS#11 integration before things get too busy. This integration will provide an "out of tree" extensibility model, but it will not satisfy the original author of this issue's stated desire to have a different interface than PKCS#11. Therefore, please don't close this issue once the PKCS#11 integration is complete.

garantir-km avatar Oct 22 '21 07:10 garantir-km

if it helps for any reference, her'es a side-show repo of using pkcs11 and an implementation of crypto.Signer for that (its nothing for prod use, just fun tinkering)

  • https://github.com/salrashid123/mtls_pkcs11
  • https://github.com/salrashid123/go_pkcs11#tpm

pkcs11 would provide the most flexiblity (you just need a std interface for any provider (whell, thats the idea)...like kms (gcp and aws has pkcs for that), yubikey, tpm, etc...You'll need to install the required .so files though first.

ther'es also backend-specific crypto.Signers around (eg, for go-tpm-tools )...butwith that you're ofcourse binding to specific thing and not abstracted like pkcs11. not recommended

salrashid123 avatar Jul 20 '22 19:07 salrashid123

This issue is stale because it has been open 60 days with no activity. Remove stale label or comment or this will be closed in 5 days.

github-actions[bot] avatar Sep 22 '22 02:09 github-actions[bot]

This issue was closed because it has been stalled for 5 days with no activity.

github-actions[bot] avatar Sep 27 '22 02:09 github-actions[bot]