Skip to content

Commit 602f3e4

Browse files
authored
Fix reference bug in CA.noteSignError (#7534)
In the process of writing #7533 I discovered that the method for detecting pkcs11.Error errors is broken: it attempts to unwrap the returned error into a pointer-to-a-pointer type, which doesn't work because only `pkcs11.Error` implements the Error interface, while `*pkcs11.Error` does not. Add a test which shows that the current noteSignError implementation is broken. Then fix noteSignError and the two locations which duplicate that code by removing the extra layer of indirection. And since the same code exists in three locations, refactor how the caImpl, ocspImpl, and crlImpl share metrics so that it only has to exist in one place. A minimal reproduction case of this type of breakage can be seen here: https://go.dev/play/p/qCLDQ1SFiWu
1 parent de8401e commit 602f3e4

File tree

6 files changed

+131
-160
lines changed

6 files changed

+131
-160
lines changed

ca/ca.go

Lines changed: 48 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,46 @@ type certProfilesMaps struct {
7979
profileByName map[string]*certProfileWithID
8080
}
8181

82+
// caMetrics holds various metrics which are shared between caImpl, ocspImpl,
83+
// and crlImpl.
84+
type caMetrics struct {
85+
signatureCount *prometheus.CounterVec
86+
signErrorCount *prometheus.CounterVec
87+
lintErrorCount prometheus.Counter
88+
}
89+
90+
func NewCAMetrics(stats prometheus.Registerer) *caMetrics {
91+
signatureCount := prometheus.NewCounterVec(
92+
prometheus.CounterOpts{
93+
Name: "signatures",
94+
Help: "Number of signatures",
95+
},
96+
[]string{"purpose", "issuer"})
97+
stats.MustRegister(signatureCount)
98+
99+
signErrorCount := prometheus.NewCounterVec(prometheus.CounterOpts{
100+
Name: "signature_errors",
101+
Help: "A counter of signature errors labelled by error type",
102+
}, []string{"type"})
103+
stats.MustRegister(signErrorCount)
104+
105+
lintErrorCount := prometheus.NewCounter(
106+
prometheus.CounterOpts{
107+
Name: "lint_errors",
108+
Help: "Number of issuances that were halted by linting errors",
109+
})
110+
stats.MustRegister(lintErrorCount)
111+
112+
return &caMetrics{signatureCount, signErrorCount, lintErrorCount}
113+
}
114+
115+
func (m *caMetrics) noteSignError(err error) {
116+
var pkcs11Error pkcs11.Error
117+
if errors.As(err, &pkcs11Error) {
118+
m.signErrorCount.WithLabelValues("HSM").Inc()
119+
}
120+
}
121+
82122
// certificateAuthorityImpl represents a CA that signs certificates.
83123
// It can sign OCSP responses as well, but only via delegation to an ocspImpl.
84124
type certificateAuthorityImpl struct {
@@ -98,9 +138,7 @@ type certificateAuthorityImpl struct {
98138
keyPolicy goodkey.KeyPolicy
99139
clk clock.Clock
100140
log blog.Logger
101-
signatureCount *prometheus.CounterVec
102-
signErrorCount *prometheus.CounterVec
103-
lintErrorCount prometheus.Counter
141+
metrics *caMetrics
104142
}
105143

106144
var _ capb.CertificateAuthorityServer = (*certificateAuthorityImpl)(nil)
@@ -219,9 +257,7 @@ func NewCertificateAuthorityImpl(
219257
maxNames int,
220258
keyPolicy goodkey.KeyPolicy,
221259
logger blog.Logger,
222-
stats prometheus.Registerer,
223-
signatureCount *prometheus.CounterVec,
224-
signErrorCount *prometheus.CounterVec,
260+
metrics *caMetrics,
225261
clk clock.Clock,
226262
) (*certificateAuthorityImpl, error) {
227263
var ca *certificateAuthorityImpl
@@ -253,13 +289,6 @@ func NewCertificateAuthorityImpl(
253289
return nil, err
254290
}
255291

256-
lintErrorCount := prometheus.NewCounter(
257-
prometheus.CounterOpts{
258-
Name: "lint_errors",
259-
Help: "Number of issuances that were halted by linting errors",
260-
})
261-
stats.MustRegister(lintErrorCount)
262-
263292
ca = &certificateAuthorityImpl{
264293
sa: sa,
265294
pa: pa,
@@ -271,24 +300,14 @@ func NewCertificateAuthorityImpl(
271300
maxNames: maxNames,
272301
keyPolicy: keyPolicy,
273302
log: logger,
274-
signatureCount: signatureCount,
275-
signErrorCount: signErrorCount,
276-
lintErrorCount: lintErrorCount,
303+
metrics: metrics,
277304
clk: clk,
278305
ecdsaAllowList: ecdsaAllowList,
279306
}
280307

281308
return ca, nil
282309
}
283310

284-
// noteSignError is called after operations that may cause a PKCS11 signing error.
285-
func (ca *certificateAuthorityImpl) noteSignError(err error) {
286-
var pkcs11Error *pkcs11.Error
287-
if errors.As(err, &pkcs11Error) {
288-
ca.signErrorCount.WithLabelValues("HSM").Inc()
289-
}
290-
}
291-
292311
var ocspStatusToCode = map[string]int{
293312
"good": ocsp.Good,
294313
"revoked": ocsp.Revoked,
@@ -432,7 +451,7 @@ func (ca *certificateAuthorityImpl) IssueCertificateForPrecertificate(ctx contex
432451

433452
certDER, err := issuer.Issue(issuanceToken)
434453
if err != nil {
435-
ca.noteSignError(err)
454+
ca.metrics.noteSignError(err)
436455
ca.log.AuditErrf("Signing cert failed: issuer=[%s] serial=[%s] regID=[%d] names=[%s] certProfileName=[%s] certProfileHash=[%x] err=[%v]",
437456
issuer.Name(), serialHex, req.RegistrationID, names, certProfile.name, certProfile.hash, err)
438457
return nil, berrors.InternalServerError("failed to sign certificate: %s", err)
@@ -443,7 +462,7 @@ func (ca *certificateAuthorityImpl) IssueCertificateForPrecertificate(ctx contex
443462
return nil, err
444463
}
445464

446-
ca.signatureCount.With(prometheus.Labels{"purpose": string(certType), "issuer": issuer.Name()}).Inc()
465+
ca.metrics.signatureCount.With(prometheus.Labels{"purpose": string(certType), "issuer": issuer.Name()}).Inc()
447466
ca.log.AuditInfof("Signing cert success: issuer=[%s] serial=[%s] regID=[%d] names=[%s] certificate=[%s] certProfileName=[%s] certProfileHash=[%x]",
448467
issuer.Name(), serialHex, req.RegistrationID, names, hex.EncodeToString(certDER), certProfile.name, certProfile.hash)
449468

@@ -594,7 +613,7 @@ func (ca *certificateAuthorityImpl) issuePrecertificateInner(ctx context.Context
594613
ca.log.AuditErrf("Preparing precert failed: issuer=[%s] serial=[%s] regID=[%d] names=[%s] certProfileName=[%s] certProfileHash=[%x] err=[%v]",
595614
issuer.Name(), serialHex, issueReq.RegistrationID, strings.Join(csr.DNSNames, ", "), certProfile.name, certProfile.hash, err)
596615
if errors.Is(err, linter.ErrLinting) {
597-
ca.lintErrorCount.Inc()
616+
ca.metrics.lintErrorCount.Inc()
598617
}
599618
return nil, nil, berrors.InternalServerError("failed to prepare precertificate signing: %s", err)
600619
}
@@ -612,7 +631,7 @@ func (ca *certificateAuthorityImpl) issuePrecertificateInner(ctx context.Context
612631

613632
certDER, err := issuer.Issue(issuanceToken)
614633
if err != nil {
615-
ca.noteSignError(err)
634+
ca.metrics.noteSignError(err)
616635
ca.log.AuditErrf("Signing precert failed: issuer=[%s] serial=[%s] regID=[%d] names=[%s] certProfileName=[%s] certProfileHash=[%x] err=[%v]",
617636
issuer.Name(), serialHex, issueReq.RegistrationID, strings.Join(csr.DNSNames, ", "), certProfile.name, certProfile.hash, err)
618637
return nil, nil, berrors.InternalServerError("failed to sign precertificate: %s", err)
@@ -623,7 +642,7 @@ func (ca *certificateAuthorityImpl) issuePrecertificateInner(ctx context.Context
623642
return nil, nil, err
624643
}
625644

626-
ca.signatureCount.With(prometheus.Labels{"purpose": string(precertType), "issuer": issuer.Name()}).Inc()
645+
ca.metrics.signatureCount.With(prometheus.Labels{"purpose": string(precertType), "issuer": issuer.Name()}).Inc()
627646
ca.log.AuditInfof("Signing precert success: issuer=[%s] serial=[%s] regID=[%d] names=[%s] precertificate=[%s] certProfileName=[%s] certProfileHash=[%x]",
628647
issuer.Name(), serialHex, issueReq.RegistrationID, strings.Join(csr.DNSNames, ", "), hex.EncodeToString(certDER), certProfile.name, certProfile.hash)
629648

0 commit comments

Comments
 (0)