crl_override (and extensions) in /api/v1/sign request body are silently ignored by the HTTP API
- Dominant language
- Go
- Stars
- 9.5k
- Forks
- 1.2k
- PR merge metrics
- No merged PRs in 30d
Description
Version: 1.6.5 (also present on master as of this writing) · Go 1.20.14
## Summary
signer.SignRequest defines a CRLOverride field (JSON tag crl_override) that the local signer honors to set a per-request CRL Distribution Point. However, when signing through the HTTP /api/v1/sign (and /api/v1/authsign) API, the field is silently dropped. The issued certificate contains no CRL Distribution Points extension (2.5.29.31), and no error or warning is returned. The same applies to the extensions field.
## Expected behavior
Posting crl_override to /api/v1/sign embeds a CRLDistributionPoints (2.5.29.31) extension in the issued certificate, matching the behavior of the local signer library and the documented SignRequest schema.
## Actual behavior
The request succeeds, but the returned certificate has no 2.5.29.31 extension. crl_override is accepted (no validation error) yet has no effect.
## Root cause
The HTTP handler does not unmarshal the body into `signer.SignRequest`. It uses an intermediate `jsonSignRequest` struct, then `jsonReqToTrue()` copies only a fixed subset of fields into the real SignRequest. Neither `crl_override` nor extensions is part of that struct or the copy:
api/signhandler/signhandler.go:
```
// This type is meant to be unmarshalled from JSON so that there can be a
// hostname field in the API
// TODO: Change the API such that the normal struct can be used.
type jsonSignRequest struct {
Hostname string `json:"hostname"`
Hosts []string `json:"hosts"`
Request string `json:"certificate_request"`
Subject *signer.Subject `json:"subject,omitempty"`
Profile string `json:"profile"`
Label string `json:"label"`
Serial *big.Int `json:"serial,omitempty"`
Bundle bool `json:"bundle"`
}
func jsonReqToTrue(js jsonSignRequest) signer.SignRequest {
// ... never sets CRLOverride or Extensions ...
return signer.SignRequest{
Hosts: js.Hosts,
Subject: sub,
Request: js.Request,
Profile: js.Profile,
Label: js.Label,
Serial: js.Serial,
}
}
```
By contrast, signer/local/local.go does honor CRLOverride (so it works when CFSSL is used as a Go library), which makes the API behavior surprising:
```
if req.CRLOverride != "" {
safeTemplate.CRLDistributionPoints = []string{req.CRLOverride}
}
// ... preserved across FillTemplate via save/restore of distPoints ...
```
The existing `TODO: Change the API such that the normal struct can be used.` comment acknowledges this divergence.
## Steps to reproduce
Run a CFSSL server with any signing profile (no crl_url set in config.json):
```
cfssl serve -address 0.0.0.0 -port 8888 -ca ca.pem -ca-key ca-key.pem -config config.json
```
Sign a CSR via the HTTP API with crl_override:
```
curl -s http://localhost:8888/api/v1/cfssl/sign -d '{
"certificate_request": "'"$(awk 'NF {sub(/\r/,""); printf "%s\\n",$0}' test.csr)"'",
"crl_override": "http://example.com/my.crl"
}' | jq -r '.result.certificate' > out.pem
```
Inspect the issued certificate:
```
openssl x509 -in out.pem -noout -text | grep -A2 "CRL Distribution"
# (no output — extension is absent)
```
Observed: no X509v3 CRL Distribution Points in the certificate.
Expected: the extension present with URI http://example.com/my.crl.
## Workaround
Set crl_url in the signing profile (config.json), which FillTemplate applies server-side:
```
{ "signing": { "default": { "crl_url": "http://example.com/my.crl", "expiry": "8760h", "usages": ["signing","server auth"] } } }
```
This works but is static per-profile and cannot vary per request.
## Suggested fix
Add CRLOverride string \json:"crl_override"`(andExtensions) to jsonSignRequestand copy them in both branches ofjsonReqToTrue(), or follow the existing TODO and unmarshal directly into signer.SignRequest`. Alternatively, if per-request override is intentionally unsupported over HTTP, document it and return a validation error when these fields are supplied.
Contributor guide
Assessment
This issue has not been assessed yet.