Hi folks,
At Let's Encrypt we would like to build optional pre-issuance certificate linting into CFSSL. Pre-issuance linting is repeatedly cited as a best-practice in incident post-mortems in the web PKI community. Adding support to CFSSL will help interested parties add more safe-guards to their issuance pipeline. I'd be happy to commit to doing the work provided the proposed design outlined below sounds good.
Boulder already uses zlint, a Go based X.509 Certificate Linter, as a library for post-issuance linting. I think it makes sense to integrate this project as a CFSSL dependency to accomplish pre-issuance linting.
High Level
At a high level we would like to:
- Add a dependency on
zlint
- Update the
config.SigningProfile struct to accept two new optional fields:
lintErrLevel int
ignoredLints []string
- Update the
signer.local.Signer struct to generate one throw-away private key at creation for linting.
- Update the
signer.local.Signer's sign(*x509.Certificate) function to:
- Sign the TBSCert template with the Signer's throw-away key.
- Pass the the resulting cert to zlint for linting.
- Handle any lint results above the lintErrLevel as errors (iff the lint is not in the ignoredLints list)
Adding a dep.
We think integrating zlint directly so it can work out-of-box with a config change rather than building in a callback for the same purpose and supplying the linting externally on a project-specific basis will help more folks benefit from the linting. zlint has a bias towards CA/B reqs and RFC 5280 but it is an extensible way to add additional lints and many of the existing lints vet best-practices that are broadly applicable.
If it helps you feel better about a new dependency I'm also a committer/maintainer of zlint so you would have a contact on that side of the fence from the get-go.
Config changes
The lintErrLevel will default to 0 for existing configs. We'll match this up with the zlint.LintResult.LintStatus in the local signer to know when to treat a result as an error. Any LintStatus > the lintErrLevel will be an error, except in the case where lintErrLevel is zero and linting is disabled.
- 0 =
zlint.lints.Reserved -> no linting will be done. no results considered. Existing CFSSL behaviour.
- 1, 2 =
zlint.lints.NA and zlint.lints.NE -> these don't really make sense in this context.
- 3 =
zlint.lints.Pass -> all lint results except pass will be considered errors.
- 4 =
zlint.lints.Notice -> all lint results except pass and notice will be considered errors.
- 5 =
zlint.lints.Warn -> all lint results except pass, notice and warning will be considered errors.
- 6 =
zlint.lints.Error -> all lint results except pass, notice, warning, and error will be considered errors (e.g. fatal errors only)
- 7+ =
zlint.lints.Fatal -> no errors will be treated as errors.
The ignoredLints map offers a more fine-grained way to ignore specific lints by name. E.g.:
ignoredLints: [
"n_subject_common_name_included",
"subject_contains_reserved_arpa_ip"
]
What do you folks think? CC @cbroglie
Hi folks,
At Let's Encrypt we would like to build optional pre-issuance certificate linting into CFSSL. Pre-issuance linting is repeatedly cited as a best-practice in incident post-mortems in the web PKI community. Adding support to CFSSL will help interested parties add more safe-guards to their issuance pipeline. I'd be happy to commit to doing the work provided the proposed design outlined below sounds good.
Boulder already uses zlint, a Go based X.509 Certificate Linter, as a library for post-issuance linting. I think it makes sense to integrate this project as a CFSSL dependency to accomplish pre-issuance linting.
High Level
At a high level we would like to:
zlintconfig.SigningProfilestruct to accept two new optional fields:lintErrLevel intignoredLints []stringsigner.local.Signerstruct to generate one throw-away private key at creation for linting.signer.local.Signer'ssign(*x509.Certificate)function to:Adding a dep.
We think integrating
zlintdirectly so it can work out-of-box with a config change rather than building in a callback for the same purpose and supplying the linting externally on a project-specific basis will help more folks benefit from the linting. zlint has a bias towards CA/B reqs and RFC 5280 but it is an extensible way to add additional lints and many of the existing lints vet best-practices that are broadly applicable.If it helps you feel better about a new dependency I'm also a committer/maintainer of
zlintso you would have a contact on that side of the fence from the get-go.Config changes
The
lintErrLevelwill default to 0 for existing configs. We'll match this up with thezlint.LintResult.LintStatusin the local signer to know when to treat a result as an error. AnyLintStatus> thelintErrLevelwill be an error, except in the case wherelintErrLevelis zero and linting is disabled.zlint.lints.Reserved-> no linting will be done. no results considered. Existing CFSSL behaviour.zlint.lints.NAandzlint.lints.NE-> these don't really make sense in this context.zlint.lints.Pass-> all lint results except pass will be considered errors.zlint.lints.Notice-> all lint results except pass and notice will be considered errors.zlint.lints.Warn-> all lint results except pass, notice and warning will be considered errors.zlint.lints.Error-> all lint results except pass, notice, warning, and error will be considered errors (e.g. fatal errors only)zlint.lints.Fatal-> no errors will be treated as errors.The
ignoredLintsmap offers a more fine-grained way to ignore specific lints by name. E.g.:What do you folks think? CC @cbroglie