[ca] Add utility to create cross-signed roots to the RootCA.#2001
Conversation
Codecov Report
@@ Coverage Diff @@
## master #2001 +/- ##
==========================================
+ Coverage 53.74% 53.79% +0.05%
==========================================
Files 109 109
Lines 19003 19051 +48
==========================================
+ Hits 10213 10249 +36
Misses 7559 7559
- Partials 1231 1243 +12Continue to review full report at Codecov.
|
|
|
||
| newCert, err := helpers.ParseCertificatePEM(otherRootCert) | ||
| if err != nil { | ||
| return nil, err |
There was a problem hiding this comment.
Let's wrap this error with a mention that it's a parse error of the supplied certificate.
| // create a new cert with exactly the same parameters, including the public key and exact NotBefore and NotAfter | ||
| rootCert, err := helpers.ParseCertificatePEM(rca.Cert) | ||
| if err != nil { | ||
| return nil, err |
There was a problem hiding this comment.
Let's wrap this error with a mention that it's a parse error of current root certificate.
| } | ||
| rootSigner, err := helpers.ParsePrivateKeyPEM(rca.Key) | ||
| if err != nil { | ||
| return nil, err |
There was a problem hiding this comment.
Let's wrap this error with a mention that it's a parse error of the current root key.
d7012e5 to
e894eef
Compare
|
I think this is finally ready for review. |
| } | ||
|
|
||
| // generateSignRequest generates a signer.SignRequest for a cross-signed certificate | ||
| func (rca *RootCA) generateSignRequest() (*cfsigner.SignRequest, error) { |
There was a problem hiding this comment.
Why is this a method on RootCA? Implies that we're going to be doing this operation to the currently loaded root, when in fact we only do it to a RootCA that is passed as an argument.
There was a problem hiding this comment.
No particular reason - happy to move it.
| if ext.Id.Equal(BasicConstraintsOID) { | ||
| req.Extensions = append(req.Extensions, cfsigner.Extension{ | ||
| ID: config.OID(ext.Id), | ||
| Critical: ext.Critical, |
There was a problem hiding this comment.
Technically we the "critical" flag is for extensions which are not standard right? I think this is fine though, since it follows the semantics of "field which is critical for safe operation".
There was a problem hiding this comment.
I'm... not sure. :) I was just translating the struct from a pkix.Extension to a signer.Extension object. Should I just leave this out?
There was a problem hiding this comment.
I think this is fine like this. Just wanted to see if you had some rationale around it. Worth adding a comment?
| } | ||
| // cfssl actually ignores non subject alt name extensions in the CSR, so we have to add the CA extension in the signing | ||
| // request as well | ||
| for _, ext := range rootCert.Extensions { |
There was a problem hiding this comment.
Wouldn't it be easier to simply add all of the extensions instead of filtering for Basic Constraints?
There was a problem hiding this comment.
The other constraints would also have to be whitelisted by the signing policy - it seemed easier to restrict the types of extension added, as opposed to adding every possible extension to the whitelist.
There was a problem hiding this comment.
Wouldn't we want a certificate with anything else to fail instead of being silently signed w/ a portion of the original extensions?
There was a problem hiding this comment.
I thought all CA certs generally have at least the 3 X509v3 extensions: key usage, basic constraints, and the subject key identifier.
We probably don't need to pass the other 2, because the CA signing policy should generate the correct values for them.
But this cert comes from a RootCA object - if we want to reject CA certs with extra extensions, the validation should probably go into constructor NewRootCA instead. Do we want to do more extensive cert validation?
There was a problem hiding this comment.
No, I guess this is fine.
There was a problem hiding this comment.
Let me double check to see if this is actually needed - the CFSSL comment suggested that it was, but looking at the comment vs the implementation of <local signer>.Sign that may not be accurate.
There was a problem hiding this comment.
Ok, so you DO need to pass the extensions in the sign request if the signer's policy has a CSR whitelist. If there is no CSR whitelist, and the signer just signs whatever the CSR says, then we can extensions exactly as they were in the original root CA and they will be respected by the signer.
So we can use CFSSL's initca.CAPolicy() on the external signer, and just pass the extensions straight through if we like. But if the signer is at all more strict (whitelisting any CSR fields) then that will fail.
…ertificate, and a utility to generate an intermediate by submitting a CSR to an external CA (which must support CA signing). Signed-off-by: cyli <ying.li@docker.com>
e894eef to
220f504
Compare
|
Ping @aaronlehmann to take another look. Let's get this merged. |
|
The mechanics of creating the CSR are a bit over my head, but the concept makes sense, there are unit tests, and I guess we'll find out soon enough if this works in practice. LGTM |
Mine too :P Let's hope I didn't mess that up :) |
This PR provides 2 ways of creating cross-signed roots, to be used as intermediates during root rotation:
Take a certificate, and cross-sign the certificate. This can be used when rotating from
internal->internal, andinternal->external.Generate a CA CSR from the root certificate, and submit it to an external CA to be signed as an intermediate. This can be used when rotating from external->internal.
external->externalcannot be supported without adding an extra API to the external signing server (either taking query parameters, or adding a new URL) in order for the external CA to also just sign an intermediate using just a certificate (because we need the private key material in order to generate an actual CSR).After further discussion with @diogomonica and @jlhawn , we can probably shelve this use case for now, because a user can rotate from
external->internal, and theninternal->externalagain, although it will be slower.If it does need to be supported, we can add the extra API requirement to the external server.
This PR is part of addressing #1990.