-
Notifications
You must be signed in to change notification settings - Fork 4.2k
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
Allow Multiple Issuers in PKI Secret Engine Mounts - PKI Pod #15277
Merged
Conversation
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
* Simple starting PKI storage api for CA rotation * Add key and issuer storage apis * Add listKeys and listIssuers storage implementations * Add simple keys and issuers configuration storage api methods
The API context will usually have a user-specified reference to the key. This is either the literal string "default" to select the default key, an identifier of the key, or a slug name for the key. Here, we wish to resolve this reference to an actual identifier that can be understood by storage. Also adds the missing Name field to keys. Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
This adds a method to construct a certutil.CertBundle from the specified issuer identifier, optionally loading its corresponding key for signing. Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
This refactors the parsing of PrivateKeys from PEM blobs into shared methods (ParsePEMKey, ParseDERKey) that can be reused by the existing Bundle parsing logic (ParsePEMBundle) or independently in the new issuers/key-based PKI storage code. Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
importKey is generally preferable to the low-level writeKey for adding new entries. This takes only the contents of the private key (as a string -- so a PEM bundle or a managed key handle) and checks if it already exists in the storage. If it does, it returns the existing key instance. Otherwise, we create a new one. In the process, we detect any issuers using this key and link them back to the new key entry. The same holds for importCert over importKey, with the note that keys are not modified when importing certificates. Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
This adds tests for importing keys and issuers into the new storage layout, ensuring that identifiers are correctly inferred and linked. Note that directly writing entries to storage (writeKey/writeissuer) will take KeyID links from the parent entry and should not be used for import; only existing entries should be updated with this info. Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
- Hook into the backend::initialize function, calling the migration on a primary only. - Migrate an existing certificate bundle to the new issuers and key layout
This allows fetchCAInfo to fetch a specified issuer, via a reference parameter provided by the user. We pass that into the storage layer and have it return a cert bundle for us. Finally, we need to validate that it truly has the key desired. Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
This implements the fetch operations around issuers in the PKI Secrets Engine. We implement the following operations: - LIST /issuers - returns a list of known issuers' IDs and names. - GET /issuer/:ref - returns a JSON blob with information about this issuer. - POST /issuer/:ref - allows configuring information about issuers, presently just its name. - DELETE /issuer/:ref - allows deleting the specified issuer. - GET /issuer/:ref/{der,pem} - returns a raw API response with just the DER (or PEM) of the issuer's certificate. Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
This adds the two core import code paths to the API: /issuers/import/cert and /issuers/import/bundle. The former differs from the latter in that the latter allows the import of keys. This allows operators to restrict importing of keys to privileged roles, while allowing more operators permission to import additional certificates (not used for signing, but instead for path/chain building). Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
This endpoint allows existing issuers to be used to sign intermediate CA certificates. In the process, we've updated the existing /root/sign-intermediate endpoint to be equivalent to a call to /issuer/default/sign-intermediate. Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
This endpoint allows existing issuers to be used to sign self-signed certificates. In the process, we've updated the existing /root/sign-self-issued endpoint to be equivalent to a call to /issuer/default/sign-self-issued. Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
This endpoint allows existing issuers to be used to directly sign CSRs. In the process, we've updated the existing /sign-verbatim endpoint to be equivalent to a call to /issuer/:ref/sign-verbatim. Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
Using the new updateDefaultIssuerId(...) from the storage migration PR allows for easy implementation of configuring the default issuer. We restrict callers from setting blank defaults and setting default to default. Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
After setting a default issuer, one should be able to use the old /ca, /ca_chain, and /cert/{ca,ca_chain} endpoints to fetch the default issuer (and its chain). Update the fetchCertBySerial helper to no longer support fetching the ca and prefer fetchCAInfo for that instead (as we've already updated that to support fetching the new issuer location). Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
This updates the /sign and /issue endpoints, allowing them to take the default issuer (if none is provided by a role) and adding issuer-specific versions of them. Note that at this point in time, the behavior isn't yet ideal (as /sign/:role allows adding the ref=... parameter to override the default issuer); a later change adding role-based issuer specification will fix this incorrect behavior. Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
- Update all new API endpoints to use the new agreed upon argument names. - issuer_ref & key_ref to refer to existing - issuer_name & key_name for new definitions - Update returned values to always user issuer_id and key_id
- Add utility methods to fetch the issuer_name, issuer_ref, key_name and key_ref arguments from data fields. - Centralize the logic to clean up these inputs and apply various validations to all of them.
- Use the buildPath convention for the function name instead of common...
…suer} methods - PR feedback, move setting up the default configuration references within the import methods instead of within the writeCaBundle method. This should now cover all use cases of us setting up the defaults properly.
- Addresses some test failures due to an incorrect refactoring of a legacy api path /sign-verbatim within PKI
The existing bundle import code will satisfy the intermediate import; use it instead of the old ca_bundle import logic. Additionally, update /config/ca to use the new import code as well. While testing, a panic was discovered: > reflect.Value.SetMapIndex: value of type string is not assignable to type pki.keyId This was caused by returning a map with type issuerId->keyId; instead switch to returning string->string maps so the audit log can properly HMAC them. Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
When the default issuer and key are missing (and haven't yet been specified), we should clarify that error message. Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
This makes two minor changes to the existing test suite: 1. Importing partial bundles should now succeed, where they'd previously error. 2. fetchCertBySerial no longer handles CA certificates. Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
The old DELETE /root code must now delete all keys and issuers for backwards compatibility. We strongly suggest calling individual delete methods (DELETE /key/:key_ref or DELETE /issuer/:issuer_ref) instead, for finer control. In the process, we detect whether the deleted key/issuers was set as the default. This will allow us to warn (from the single key/deletion issuer code) whether or not the default was deleted (while allowing the operation to succeed). Signed-off-by: Alexander Scheel <alex.scheel@hashicorp.com>
- Replace hardcoded "default" references with a constant to easily identify various usages. - Use the addIssuerRefField function instead of redefining the field in various locations.
- Validate that generate/root calls are no longer idempotent, but the bundle importing does not generate new keys/issuers - As before make sure that the delete root api resets everything - Address a bug within the storage that we bombed when we had multiple different key types within storage.
This was referenced Jun 20, 2022
Merged
3 tasks
This was referenced Nov 17, 2022
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Overview
This pull request adds support for multiple issuers (mixed roots and intermediates) under the same PKI secrets engine mount point. We expose two new major API paths,
/issuer{s}
and/key{s}
, for managing issuing certificates and their associated keys respectively.The intent of this design was to enable a forward-rotation strategy for root CAs, with additional goals of aiding the live rotation of intermediates as well. Previously, to rotate issuing certificates in Vault 1.10.x and earlier, either the mount would have to be completely removed (and replaced with a new one -- with all audit and role information recreated) or the root/intermediate would have to have been deleted.
Existing APIs are preserved and will continue to function. We introduce the notion of a "default" issuer (
/config/issuers
) to handle underlying selection of issuer when existing APIs elide that information. Role now allow selection of an explicit issuer, allowing operators to choose issuers for users without them necessarily noticing the impact.Unless otherwise unauthenticated, many of these new APIs should be considered privileged and adequately ACL'd.
Additionally, as a design choice, all certificate authorities (whether the mount's issuing certificate with backing key or a parent root certificate without key) in the mount have an issuer entry. This allows a mount's functional issuers to change over time (including the introduction of a soft delete, wherein both keys and certificates for an issuer are perserved, but the ability to revoke its certificates remains). There's now explicit chain building support as well; previously on many intermediate mounts, the parent root certificate(s) were not imported -- they can now be imported and exposed to callers.
However, as a result of this design, we open the possibility for many new, streamlined usages of the PKI secrets engine. To give just one example, a twin-intermediate approach is now viable, wherein high-impact certs could be signed by an HSM-backed intermediate and low-impact certs could be signed by a Vault-backed intermediate, all chosen by the operator based on user role selection. Users could be transparently switched between intermediates, without needing to update their client configuration. This is enabled by allowing multiple active issuers in the same mount.
Review Strategy
This PR is segmented into a number of incremental commits; see incremental commit messages and changes for details.
At a high level, we'd love to see the following questions answered:
Is forwarding of requests handled correctly? (Active node of the primary cluster needs to handle much of issuer&key responses along with role and configuration information).Discussed in call Initial Website Import #1.Do we need a read lock on the active primary/secondary &c for issuer consistency? These should be relatively low-traffic APIs, but there's a risk. How would such a distributed lock be created and held? Notably, issuers are only modified on the active node of the primary cluster, but these get replicated on storage to other nodes in secondary clusters, and thus they may have a temporarily inconsistent view of storage that may allow requests to suceed/fail until they've sufficiently caught up.Discussed in call Demo updates #3.There's many placesI don't believe so right now as we haven't previously had support for this.RawSubject
andRawIssuer
are used directly for comparisons, but I believe this is incorrect; in particular, RDNs areSEQUENCE
s ofSET
s, which means the internal comparison should be an unordered comparison, meaning we may incorrectly deny some valid links due to differing order of RDN pieces. Is this critical?As part of this work, CRLs will now be rebuilt on secondary clusters when necessary; in particular, this means that when adding a new secondary cluster, it will no longer serve aThis shouldn't be a problem.nil
CRL until a certificate is revoked (or the CRL is manually rotated).Are there any new security concerns or are the privileged APIs largely understood?I think we largely understand the security concerns and access toissuers/
andissuer/
authed endpoints could/should be restricted by policy, not bysudo
permission.Outstanding Work
There's a bit of work left to enable managed keys in Enterprise. @stevendpclark has most of this work already in branches and it will be opened in parallel. When this merges, we'll do an OSS->ENT merge and then merge that PR.
We aim to store the issuer ID used to issue requests (when
no_store=false
); this work isn't done but doesn't hold up this merge.There's another piece of correctness around issuer import (ensuring correct comparisons even when PEM contents differ); this also shouldn't hold up the merge.
There's additional work to allow revoking an issuer if another issuer in the same mount signed it; this can happen after the
main
merge.Additional, more comprehensive integration tests (for this feature set in particular) are still WIP and will be merged later.
The many UI changes to incoprorate this feature set will come at a separate point in time.
Extensive documentation for this change set is still outstanding; API docs are started in a separate PR. Other docs, learn guides, and tutorials are still outstanding.
Miscellanea
Incremental PRs
In working towards this feature, many incremental PRs were made towards this feature. We've used the
rotation
label for this; please feel free to walk through earlier discussions on earlier PRs for some discussion on these topics.Hashicorp Internal Context
See internal RFC VLT-191 for extensive information about this change set; feel free to ask questions on
#team-vault-crypto
.Meeting invites will be scheduled to give crypto team and core team a place to discuss this PR and start reviewing it. In discussion with @sgmiller, we felt it was best to open a single large PR, rather than attempting to recreate the incremental PRs above. This PR is essentially a large refactoring of the PKI code base, breaking several
Thanks
Many thanks to @kitography and @stevendpclark who helped with implementation of this feature!
Additionally, many thanks to everyone else who contributed at earlier stages, whether with great UX designs (@ivana-mcconnell :D), BK, Jeff Mitchell and Scott, the entire Vault Crypto team, or anyone else who reviewed drafts of this RFC, plus anyone else I forgot to thank.
If possible, I'd like to see this rebased (rather than squash merged), to give adequate credit to everyone who's contributed along the way. Many internal PRs were squashed merged and I believe the commit series makes most sense separately rather than as a large single change.