Skip to content

Commit a7b4e75

Browse files
authored
Merge pull request #2290 from smallstep/mariano/metrics
Add SSH certificate type to metrics
2 parents 4f5c524 + c26c8d7 commit a7b4e75

File tree

4 files changed

+62
-57
lines changed

4 files changed

+62
-57
lines changed

authority/meter.go

Lines changed: 19 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -2,24 +2,26 @@ package authority
22

33
import (
44
"crypto"
5+
"crypto/x509"
56
"io"
67

78
"go.step.sm/crypto/kms"
89
kmsapi "go.step.sm/crypto/kms/apiv1"
10+
"golang.org/x/crypto/ssh"
911

1012
"github.com/smallstep/certificates/authority/provisioner"
1113
)
1214

1315
// Meter wraps the set of defined callbacks for metrics gatherers.
1416
type Meter interface {
1517
// X509Signed is called whenever an X509 certificate is signed.
16-
X509Signed(provisioner.Interface, error)
18+
X509Signed([]*x509.Certificate, provisioner.Interface, error)
1719

1820
// X509Renewed is called whenever an X509 certificate is renewed.
19-
X509Renewed(provisioner.Interface, error)
21+
X509Renewed([]*x509.Certificate, provisioner.Interface, error)
2022

2123
// X509Rekeyed is called whenever an X509 certificate is rekeyed.
22-
X509Rekeyed(provisioner.Interface, error)
24+
X509Rekeyed([]*x509.Certificate, provisioner.Interface, error)
2325

2426
// X509WebhookAuthorized is called whenever an X509 authoring webhook is called.
2527
X509WebhookAuthorized(provisioner.Interface, error)
@@ -28,13 +30,13 @@ type Meter interface {
2830
X509WebhookEnriched(provisioner.Interface, error)
2931

3032
// SSHSigned is called whenever an SSH certificate is signed.
31-
SSHSigned(provisioner.Interface, error)
33+
SSHSigned(*ssh.Certificate, provisioner.Interface, error)
3234

3335
// SSHRenewed is called whenever an SSH certificate is renewed.
34-
SSHRenewed(provisioner.Interface, error)
36+
SSHRenewed(*ssh.Certificate, provisioner.Interface, error)
3537

3638
// SSHRekeyed is called whenever an SSH certificate is rekeyed.
37-
SSHRekeyed(provisioner.Interface, error)
39+
SSHRekeyed(*ssh.Certificate, provisioner.Interface, error)
3840

3941
// SSHWebhookAuthorized is called whenever an SSH authoring webhook is called.
4042
SSHWebhookAuthorized(provisioner.Interface, error)
@@ -49,17 +51,17 @@ type Meter interface {
4951
// noopMeter implements a noop [Meter].
5052
type noopMeter struct{}
5153

52-
func (noopMeter) SSHRekeyed(provisioner.Interface, error) {}
53-
func (noopMeter) SSHRenewed(provisioner.Interface, error) {}
54-
func (noopMeter) SSHSigned(provisioner.Interface, error) {}
55-
func (noopMeter) SSHWebhookAuthorized(provisioner.Interface, error) {}
56-
func (noopMeter) SSHWebhookEnriched(provisioner.Interface, error) {}
57-
func (noopMeter) X509Rekeyed(provisioner.Interface, error) {}
58-
func (noopMeter) X509Renewed(provisioner.Interface, error) {}
59-
func (noopMeter) X509Signed(provisioner.Interface, error) {}
60-
func (noopMeter) X509WebhookAuthorized(provisioner.Interface, error) {}
61-
func (noopMeter) X509WebhookEnriched(provisioner.Interface, error) {}
62-
func (noopMeter) KMSSigned(error) {}
54+
func (noopMeter) SSHRekeyed(*ssh.Certificate, provisioner.Interface, error) {}
55+
func (noopMeter) SSHRenewed(*ssh.Certificate, provisioner.Interface, error) {}
56+
func (noopMeter) SSHSigned(*ssh.Certificate, provisioner.Interface, error) {}
57+
func (noopMeter) SSHWebhookAuthorized(provisioner.Interface, error) {}
58+
func (noopMeter) SSHWebhookEnriched(provisioner.Interface, error) {}
59+
func (noopMeter) X509Rekeyed([]*x509.Certificate, provisioner.Interface, error) {}
60+
func (noopMeter) X509Renewed([]*x509.Certificate, provisioner.Interface, error) {}
61+
func (noopMeter) X509Signed([]*x509.Certificate, provisioner.Interface, error) {}
62+
func (noopMeter) X509WebhookAuthorized(provisioner.Interface, error) {}
63+
func (noopMeter) X509WebhookEnriched(provisioner.Interface, error) {}
64+
func (noopMeter) KMSSigned(error) {}
6365

6466
type instrumentedKeyManager struct {
6567
kms.KeyManager

authority/ssh.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -149,7 +149,7 @@ func (a *Authority) GetSSHBastion(ctx context.Context, user, hostname string) (*
149149
// SignSSH creates a signed SSH certificate with the given public key and options.
150150
func (a *Authority) SignSSH(ctx context.Context, key ssh.PublicKey, opts provisioner.SignSSHOptions, signOpts ...provisioner.SignOption) (*ssh.Certificate, error) {
151151
cert, prov, err := a.signSSH(ctx, key, opts, signOpts...)
152-
a.meter.SSHSigned(prov, err)
152+
a.meter.SSHSigned(cert, prov, err)
153153
return cert, err
154154
}
155155

@@ -337,7 +337,7 @@ func (a *Authority) isAllowedToSignSSHCertificate(cert *ssh.Certificate) error {
337337
// RenewSSH creates a signed SSH certificate using the old SSH certificate as a template.
338338
func (a *Authority) RenewSSH(ctx context.Context, oldCert *ssh.Certificate) (*ssh.Certificate, error) {
339339
cert, prov, err := a.renewSSH(ctx, oldCert)
340-
a.meter.SSHRenewed(prov, err)
340+
a.meter.SSHRenewed(cert, prov, err)
341341
return cert, err
342342
}
343343

@@ -408,7 +408,7 @@ func (a *Authority) renewSSH(ctx context.Context, oldCert *ssh.Certificate) (*ss
408408
// RekeySSH creates a signed SSH certificate using the old SSH certificate as a template.
409409
func (a *Authority) RekeySSH(ctx context.Context, oldCert *ssh.Certificate, pub ssh.PublicKey, signOpts ...provisioner.SignOption) (*ssh.Certificate, error) {
410410
cert, prov, err := a.rekeySSH(ctx, oldCert, pub, signOpts...)
411-
a.meter.SSHRekeyed(prov, err)
411+
a.meter.SSHRekeyed(cert, prov, err)
412412
return cert, err
413413
}
414414

authority/tls.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,7 @@ func (a *Authority) Sign(csr *x509.CertificateRequest, signOpts provisioner.Sign
118118
// request, taking the provided context.Context.
119119
func (a *Authority) SignWithContext(ctx context.Context, csr *x509.CertificateRequest, signOpts provisioner.SignOptions, extraOpts ...provisioner.SignOption) ([]*x509.Certificate, error) {
120120
chain, prov, err := a.signX509(ctx, csr, signOpts, extraOpts...)
121-
a.meter.X509Signed(prov, err)
121+
a.meter.X509Signed(chain, prov, err)
122122
return chain, err
123123
}
124124

@@ -372,9 +372,9 @@ func (a *Authority) Rekey(oldCert *x509.Certificate, pk crypto.PublicKey) ([]*x5
372372
func (a *Authority) RenewContext(ctx context.Context, oldCert *x509.Certificate, pk crypto.PublicKey) ([]*x509.Certificate, error) {
373373
chain, prov, err := a.renewContext(ctx, oldCert, pk)
374374
if pk == nil {
375-
a.meter.X509Renewed(prov, err)
375+
a.meter.X509Renewed(chain, prov, err)
376376
} else {
377-
a.meter.X509Rekeyed(prov, err)
377+
a.meter.X509Rekeyed(chain, prov, err)
378378
}
379379
return chain, err
380380
}

internal/metrix/meter.go

Lines changed: 37 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,13 @@
22
package metrix
33

44
import (
5+
"crypto/x509"
56
"net/http"
67
"strconv"
78
"time"
89

910
"github.com/smallstep/certificates/authority/provisioner"
11+
"golang.org/x/crypto/ssh"
1012

1113
"github.com/prometheus/client_golang/prometheus"
1214
"github.com/prometheus/client_golang/prometheus/promhttp"
@@ -15,6 +17,8 @@ import (
1517
// New initializes and returns a new [Meter].
1618
func New() (m *Meter) {
1719
initializedAt := time.Now()
20+
defaultLabels := []string{"provisioner", "success"}
21+
sshSignLabels := []string{"provisioner", "success", "type"}
1822

1923
m = &Meter{
2024
uptime: prometheus.NewGaugeFunc(
@@ -27,8 +31,8 @@ func New() (m *Meter) {
2731
return float64(time.Since(initializedAt) / time.Second)
2832
},
2933
),
30-
ssh: newProvisionerInstruments("ssh"),
31-
x509: newProvisionerInstruments("x509"),
34+
ssh: newProvisionerInstruments("ssh", sshSignLabels, defaultLabels),
35+
x509: newProvisionerInstruments("x509", defaultLabels, defaultLabels),
3236
kms: &kms{
3337
signed: prometheus.NewCounter(prometheus.CounterOpts(opts("kms", "signed", "Number of KMS-backed signatures"))),
3438
errors: prometheus.NewCounter(prometheus.CounterOpts(opts("kms", "errors", "Number of KMS-related errors"))),
@@ -77,18 +81,18 @@ type Meter struct {
7781
}
7882

7983
// SSHRekeyed implements [authority.Meter] for [Meter].
80-
func (m *Meter) SSHRekeyed(p provisioner.Interface, err error) {
81-
incrProvisionerCounter(m.ssh.rekeyed, p, err)
84+
func (m *Meter) SSHRekeyed(cert *ssh.Certificate, p provisioner.Interface, err error) {
85+
incrProvisionerCounter(m.ssh.rekeyed, p, err, sshCertValues(cert)...)
8286
}
8387

8488
// SSHRenewed implements [authority.Meter] for [Meter].
85-
func (m *Meter) SSHRenewed(p provisioner.Interface, err error) {
86-
incrProvisionerCounter(m.ssh.renewed, p, err)
89+
func (m *Meter) SSHRenewed(cert *ssh.Certificate, p provisioner.Interface, err error) {
90+
incrProvisionerCounter(m.ssh.renewed, p, err, sshCertValues(cert)...)
8791
}
8892

8993
// SSHSigned implements [authority.Meter] for [Meter].
90-
func (m *Meter) SSHSigned(p provisioner.Interface, err error) {
91-
incrProvisionerCounter(m.ssh.signed, p, err)
94+
func (m *Meter) SSHSigned(cert *ssh.Certificate, p provisioner.Interface, err error) {
95+
incrProvisionerCounter(m.ssh.signed, p, err, sshCertValues(cert)...)
9296
}
9397

9498
// SSHWebhookAuthorized implements [authority.Meter] for [Meter].
@@ -102,17 +106,17 @@ func (m *Meter) SSHWebhookEnriched(p provisioner.Interface, err error) {
102106
}
103107

104108
// X509Rekeyed implements [authority.Meter] for [Meter].
105-
func (m *Meter) X509Rekeyed(p provisioner.Interface, err error) {
109+
func (m *Meter) X509Rekeyed(_ []*x509.Certificate, p provisioner.Interface, err error) {
106110
incrProvisionerCounter(m.x509.rekeyed, p, err)
107111
}
108112

109113
// X509Renewed implements [authority.Meter] for [Meter].
110-
func (m *Meter) X509Renewed(p provisioner.Interface, err error) {
114+
func (m *Meter) X509Renewed(_ []*x509.Certificate, p provisioner.Interface, err error) {
111115
incrProvisionerCounter(m.x509.renewed, p, err)
112116
}
113117

114118
// X509Signed implements [authority.Meter] for [Meter].
115-
func (m *Meter) X509Signed(p provisioner.Interface, err error) {
119+
func (m *Meter) X509Signed(_ []*x509.Certificate, p provisioner.Interface, err error) {
116120
incrProvisionerCounter(m.x509.signed, p, err)
117121
}
118122

@@ -126,13 +130,27 @@ func (m *Meter) X509WebhookEnriched(p provisioner.Interface, err error) {
126130
incrProvisionerCounter(m.x509.webhookEnriched, p, err)
127131
}
128132

129-
func incrProvisionerCounter(cv *prometheus.CounterVec, p provisioner.Interface, err error) {
133+
func sshCertValues(cert *ssh.Certificate) []string {
134+
switch cert.CertType {
135+
case ssh.UserCert:
136+
return []string{"user"}
137+
case ssh.HostCert:
138+
return []string{"host"}
139+
default:
140+
return []string{"unknown"}
141+
}
142+
}
143+
144+
func incrProvisionerCounter(cv *prometheus.CounterVec, p provisioner.Interface, err error, extraValues ...string) {
130145
var name string
131146
if p != nil {
132147
name = p.GetName()
133148
}
134149

135-
cv.WithLabelValues(name, strconv.FormatBool(err == nil)).Inc()
150+
values := append([]string{
151+
name, strconv.FormatBool(err == nil),
152+
}, extraValues...)
153+
cv.WithLabelValues(values...).Inc()
136154
}
137155

138156
// KMSSigned implements [authority.Meter] for [Meter].
@@ -154,28 +172,13 @@ type provisionerInstruments struct {
154172
webhookEnriched *prometheus.CounterVec
155173
}
156174

157-
func newProvisionerInstruments(subsystem string) *provisionerInstruments {
175+
func newProvisionerInstruments(subsystem string, signLabels, webhookLabels []string) *provisionerInstruments {
158176
return &provisionerInstruments{
159-
rekeyed: newCounterVec(subsystem, "rekeyed_total", "Number of certificates rekeyed",
160-
"provisioner",
161-
"success",
162-
),
163-
renewed: newCounterVec(subsystem, "renewed_total", "Number of certificates renewed",
164-
"provisioner",
165-
"success",
166-
),
167-
signed: newCounterVec(subsystem, "signed_total", "Number of certificates signed",
168-
"provisioner",
169-
"success",
170-
),
171-
webhookAuthorized: newCounterVec(subsystem, "webhook_authorized_total", "Number of authorizing webhooks called",
172-
"provisioner",
173-
"success",
174-
),
175-
webhookEnriched: newCounterVec(subsystem, "webhook_enriched_total", "Number of enriching webhooks called",
176-
"provisioner",
177-
"success",
178-
),
177+
rekeyed: newCounterVec(subsystem, "rekeyed_total", "Number of certificates rekeyed", signLabels...),
178+
renewed: newCounterVec(subsystem, "renewed_total", "Number of certificates renewed", signLabels...),
179+
signed: newCounterVec(subsystem, "signed_total", "Number of certificates signed", signLabels...),
180+
webhookAuthorized: newCounterVec(subsystem, "webhook_authorized_total", "Number of authorizing webhooks called", webhookLabels...),
181+
webhookEnriched: newCounterVec(subsystem, "webhook_enriched_total", "Number of enriching webhooks called", webhookLabels...),
179182
}
180183
}
181184

0 commit comments

Comments
 (0)