Repository navigation
Conversation
This change updates the WFE GetOrder handler to include a "merkle-certificate" key with a mtc-retrieval URI value in orders returned to ACME clients when the wfe instance is configured with an MTCB service, and the order contains critical MTC fields. The change also adds a WFE MerkleCertificate handler for the mtc-retrieval URI, which asks the MTCB to return a Standalone certificate and hands it over to the client ( PEM-encoded ).
The order information returned through the mockRA should only include one or the other of a classic certificate serial number, or merkle tree certificate info, not both.
Claude was directed at failing integration test code. It traced the issue and added a helper function for WithSpanNameFormatter as the fix, I re-wrote its comment.
|
@ezekiel, this PR appears to contain configuration and/or SQL schema changes. Please ensure that a corresponding deployment ticket has been filed with the new values. |
1 similar comment
|
@ezekiel, this PR appears to contain configuration and/or SQL schema changes. Please ensure that a corresponding deployment ticket has been filed with the new values. |
Orders presented to clients should not have Certificate or MerkleCertificate fields, they just have Certificate, with an appropriate URL for the certificate type. This change also makes MTC / merkle tree certificate naming more consistent within the change, and does not abbreviate as "MerkleCert", etc.
|
With the merge of #9109 , this PR has an incoming merge of |
jsha
left a comment
There was a problem hiding this comment.
Thanks for working on this, excited to see it go out.
This PR has the WFE talking to the MTCB directly. I think that's okay to land as-is, but as a followup I think it would be nicer for the call to go WFE->RA->MTCB. That's an extra hop in the gRPC call graph but it is consistent with the choice we made for GetOrder, where the RA holds the knowledge about MTCB. As a practical matter, it means we only need to configure one service with access to MTCB, not two.
| }), | ||
| )) | ||
| ) | ||
| // Detect the lack of at least one PAIR of curly braces as a signal that |
There was a problem hiding this comment.
PAIR is emphasized here; why?
| } | ||
| } | ||
|
|
||
| // MerkleTreeCertificate is used by client to request a copy of their merkle tree certificate. |
There was a problem hiding this comment.
| // MerkleTreeCertificate is used by client to request a copy of their merkle tree certificate. | |
| // MerkleTreeCertificate is used by clients to request a copy of their merkle tree certificate. |
|
|
||
| // wfe instance not configured with an MTCB Client | ||
| if wfe.mtcb == nil { | ||
| wfe.sendError(response, logEvent, probs.Malformed("Not Supported"), nil) |
There was a problem hiding this comment.
IMO this is a little better as a 404 for now.
|
|
||
| reqAcctID, err := parsePositiveInt(request.PathValue("acctID")) | ||
| if err != nil { | ||
| wfe.sendError(response, logEvent, probs.Malformed("Request URL does not include a valid accountID"), nil) |
| // If the order is not yet valid, then we did not find what the requester | ||
| // wants. |
There was a problem hiding this comment.
IMO this and the below cases fall under CONTRIBUTING.md's "No need to gratuitously comment clear code." They're self-explanatory error cases, no comment needed.
| wfe.HandleFunc(m, renewalInfoPath, wfe.RenewalInfo, "GET", "POST") | ||
|
|
||
| // POST-as-GETable ACME endpoints | ||
| wfe.HandleFunc(m, mtcPath+"{acctID}/{orderID}/{logID}/{serial}", wfe.MerkleTreeCertificate, "POST") |
There was a problem hiding this comment.
| wfe.HandleFunc(m, mtcPath+"{acctID}/{orderID}/{logID}/{serial}", wfe.MerkleTreeCertificate, "POST") | |
| wfe.HandleFunc(m, mtcPath+"{acctID}/{orderID}/{mtcLogID}/{serial}", wfe.MerkleTreeCertificate, "POST") |
We use the fully-typed-out "mtcLogID" in other places in the codebase and I think it helps (a) to be consistent and (b) to be explicit, for instance this isn't the ID of some other type of log.
| return | ||
| } | ||
|
|
||
| if standalone.CertDER == nil { |
There was a problem hiding this comment.
core.IsAnyNilOrZero for consistency, even though there's only one parameter.
| // And PEM-encode the retrieved certificate for return to the client. | ||
| standalonePEM := pem.EncodeToMemory(&pem.Block{ |
There was a problem hiding this comment.
This comment is also unneeded: pem.EncodeToMemory tells us that this is PEM-encoding something.
| // certificate will likely exceed this threshold. Since we know the length | ||
| // we can reliably set it ourselves and not worry. | ||
| response.Header().Set("Content-Length", strconv.Itoa(len(standalonePEM))) | ||
| response.Header().Set("Content-Type", "application/pem-certificate-chain") |
There was a problem hiding this comment.
When downloading either certificate (Section 7.4.2 of [RFC8555]), ACME clients supporting Merkle Tree certificates SHOULD send "application/pem-certificate-chain-with-properties" in their Accept header (Section 12.5.1 of [RFC9110]). ACME servers issuing Merkle Tree certificates SHOULD then respond with that content type to include a CertificatePropertyList.
We don't have the code for CertificatePropertyList yet but might as well start using the Content-Type.
| // NOTE(@ezekiel): Similar to Certificate(), we must explicitly set the | ||
| // Content-Length header here. The Go HTTP library will only add this header | ||
| // if the body is below a certain size and a PEM encoded merkle tree | ||
| // certificate will likely exceed this threshold. Since we know the length | ||
| // we can reliably set it ourselves and not worry. |
There was a problem hiding this comment.
I see this comment is a copy of an existing comment with NOTE(@...). IMO, let's remove the handle (we don't generally need to record who wrote a comment), and let's also remove the NOTE; all comments are notes, and NOTE suggests this one is particularly important.
Further, with the benefit of hindsight I think we could shorten or remove both comments. I might say net/http only adds Content-Length automatically "if the total size of all written data is under a few KB".
Really the comment I wish we had is one that explains why it's important that we set Content-Length here. I think we don't actually need it, and #3366 doesn't explain why it's needed. The purpose of Content-Length is so an HTTP client knows when a response is done and reuse the connection for the next request. However, there's an alternative that Go will use automatically in HTTP/1.1: Transfer-Encoding: chunked. That would work just fine here.
Anyhow, it's about six of one half dozen of the other: my inclination is to keep setting Content-Length explicitly in both sites, and shorten both comments.
There was a problem hiding this comment.
Some additional context in #1320: we set Content-Length because otherwise our responses to HEAD will have the wrong Content-Length, per HTTP spec.
This change updates the WFE GetOrder handler to include a "merkle-certificate" key with a mtc-retrieval URI value in orders returned to ACME clients when the wfe instance is configured with an MTCB service, and the order contains critical MTC fields.
The change also adds a WFE MerkleCertificate handler for the mtc-retrieval URI, which asks the MTCB to return a Standalone certificate and hands it over to the client ( PEM-encoded ).
Claude helped trace the otel span naming bug, and proposed that fix.
Fixes #9035