Merge branch 'wire-acme-extensions' into herman/wire-acme-improvements
commit
79943d2e5e
@ -0,0 +1,87 @@
|
||||
package authority
|
||||
|
||||
import (
|
||||
"crypto"
|
||||
"io"
|
||||
|
||||
"go.step.sm/crypto/kms"
|
||||
kmsapi "go.step.sm/crypto/kms/apiv1"
|
||||
|
||||
"github.com/smallstep/certificates/authority/provisioner"
|
||||
)
|
||||
|
||||
// Meter wraps the set of defined callbacks for metrics gatherers.
|
||||
type Meter interface {
|
||||
// X509Signed is called whenever an X509 certificate is signed.
|
||||
X509Signed(provisioner.Interface, error)
|
||||
|
||||
// X509Renewed is called whenever an X509 certificate is renewed.
|
||||
X509Renewed(provisioner.Interface, error)
|
||||
|
||||
// X509Rekeyed is called whenever an X509 certificate is rekeyed.
|
||||
X509Rekeyed(provisioner.Interface, error)
|
||||
|
||||
// X509WebhookAuthorized is called whenever an X509 authoring webhook is called.
|
||||
X509WebhookAuthorized(provisioner.Interface, error)
|
||||
|
||||
// X509WebhookEnriched is called whenever an X509 enriching webhook is called.
|
||||
X509WebhookEnriched(provisioner.Interface, error)
|
||||
|
||||
// SSHSigned is called whenever an SSH certificate is signed.
|
||||
SSHSigned(provisioner.Interface, error)
|
||||
|
||||
// SSHRenewed is called whenever an SSH certificate is renewed.
|
||||
SSHRenewed(provisioner.Interface, error)
|
||||
|
||||
// SSHRekeyed is called whenever an SSH certificate is rekeyed.
|
||||
SSHRekeyed(provisioner.Interface, error)
|
||||
|
||||
// SSHWebhookAuthorized is called whenever an SSH authoring webhook is called.
|
||||
SSHWebhookAuthorized(provisioner.Interface, error)
|
||||
|
||||
// SSHWebhookEnriched is called whenever an SSH enriching webhook is called.
|
||||
SSHWebhookEnriched(provisioner.Interface, error)
|
||||
|
||||
// KMSSigned is called per KMS signer signature.
|
||||
KMSSigned(error)
|
||||
}
|
||||
|
||||
// noopMeter implements a noop [Meter].
|
||||
type noopMeter struct{}
|
||||
|
||||
func (noopMeter) SSHRekeyed(provisioner.Interface, error) {}
|
||||
func (noopMeter) SSHRenewed(provisioner.Interface, error) {}
|
||||
func (noopMeter) SSHSigned(provisioner.Interface, error) {}
|
||||
func (noopMeter) SSHWebhookAuthorized(provisioner.Interface, error) {}
|
||||
func (noopMeter) SSHWebhookEnriched(provisioner.Interface, error) {}
|
||||
func (noopMeter) X509Rekeyed(provisioner.Interface, error) {}
|
||||
func (noopMeter) X509Renewed(provisioner.Interface, error) {}
|
||||
func (noopMeter) X509Signed(provisioner.Interface, error) {}
|
||||
func (noopMeter) X509WebhookAuthorized(provisioner.Interface, error) {}
|
||||
func (noopMeter) X509WebhookEnriched(provisioner.Interface, error) {}
|
||||
func (noopMeter) KMSSigned(error) {}
|
||||
|
||||
type instrumentedKeyManager struct {
|
||||
kms.KeyManager
|
||||
meter Meter
|
||||
}
|
||||
|
||||
func (i *instrumentedKeyManager) CreateSigner(req *kmsapi.CreateSignerRequest) (s crypto.Signer, err error) {
|
||||
if s, err = i.KeyManager.CreateSigner(req); err == nil {
|
||||
s = &instrumentedKMSSigner{s, i.meter}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
type instrumentedKMSSigner struct {
|
||||
crypto.Signer
|
||||
meter Meter
|
||||
}
|
||||
|
||||
func (i *instrumentedKMSSigner) Sign(rand io.Reader, digest []byte, opts crypto.SignerOpts) (signature []byte, err error) {
|
||||
signature, err = i.Signer.Sign(rand, digest, opts)
|
||||
i.meter.KMSSigned(err)
|
||||
|
||||
return
|
||||
}
|
@ -0,0 +1,196 @@
|
||||
// Package metrix implements stats-related functionality.
|
||||
package metrix
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/smallstep/certificates/authority/provisioner"
|
||||
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
"github.com/prometheus/client_golang/prometheus/promhttp"
|
||||
)
|
||||
|
||||
// New initializes and returns a new [Meter].
|
||||
func New() (m *Meter) {
|
||||
initializedAt := time.Now()
|
||||
|
||||
m = &Meter{
|
||||
uptime: prometheus.NewGaugeFunc(
|
||||
prometheus.GaugeOpts(opts(
|
||||
"",
|
||||
"uptime_seconds",
|
||||
"Number of seconds since service start",
|
||||
)),
|
||||
func() float64 {
|
||||
return float64(time.Since(initializedAt) / time.Second)
|
||||
},
|
||||
),
|
||||
ssh: newProvisionerInstruments("ssh"),
|
||||
x509: newProvisionerInstruments("x509"),
|
||||
kms: &kms{
|
||||
signed: prometheus.NewCounter(prometheus.CounterOpts(opts("kms", "signed", "Number of KMS-backed signatures"))),
|
||||
errors: prometheus.NewCounter(prometheus.CounterOpts(opts("kms", "errors", "Number of KMS-related errors"))),
|
||||
},
|
||||
}
|
||||
|
||||
reg := prometheus.NewRegistry()
|
||||
|
||||
reg.MustRegister(
|
||||
m.uptime,
|
||||
m.ssh.rekeyed,
|
||||
m.ssh.renewed,
|
||||
m.ssh.signed,
|
||||
m.x509.rekeyed,
|
||||
m.x509.renewed,
|
||||
m.x509.signed,
|
||||
m.kms.signed,
|
||||
m.kms.errors,
|
||||
)
|
||||
|
||||
h := promhttp.HandlerFor(reg, promhttp.HandlerOpts{
|
||||
Registry: reg,
|
||||
Timeout: 5 * time.Second,
|
||||
MaxRequestsInFlight: 10,
|
||||
})
|
||||
|
||||
mux := http.NewServeMux()
|
||||
mux.Handle("/metrics", h)
|
||||
m.Handler = mux
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Meter wraps the functionality of a Prometheus-compatible HTTP handler.
|
||||
type Meter struct {
|
||||
http.Handler
|
||||
|
||||
uptime prometheus.GaugeFunc
|
||||
ssh *provisionerInstruments
|
||||
x509 *provisionerInstruments
|
||||
kms *kms
|
||||
}
|
||||
|
||||
// SSHRekeyed implements [authority.Meter] for [Meter].
|
||||
func (m *Meter) SSHRekeyed(p provisioner.Interface, err error) {
|
||||
incrProvisionerCounter(m.ssh.rekeyed, p, err)
|
||||
}
|
||||
|
||||
// SSHRenewed implements [authority.Meter] for [Meter].
|
||||
func (m *Meter) SSHRenewed(p provisioner.Interface, err error) {
|
||||
incrProvisionerCounter(m.ssh.renewed, p, err)
|
||||
}
|
||||
|
||||
// SSHSigned implements [authority.Meter] for [Meter].
|
||||
func (m *Meter) SSHSigned(p provisioner.Interface, err error) {
|
||||
incrProvisionerCounter(m.ssh.signed, p, err)
|
||||
}
|
||||
|
||||
// SSHAuthorized implements [authority.Meter] for [Meter].
|
||||
func (m *Meter) SSHWebhookAuthorized(p provisioner.Interface, err error) {
|
||||
incrProvisionerCounter(m.ssh.webhookAuthorized, p, err)
|
||||
}
|
||||
|
||||
// SSHEnriched implements [authority.Meter] for [Meter].
|
||||
func (m *Meter) SSHWebhookEnriched(p provisioner.Interface, err error) {
|
||||
incrProvisionerCounter(m.ssh.webhookEnriched, p, err)
|
||||
}
|
||||
|
||||
// X509Rekeyed implements [authority.Meter] for [Meter].
|
||||
func (m *Meter) X509Rekeyed(p provisioner.Interface, err error) {
|
||||
incrProvisionerCounter(m.x509.rekeyed, p, err)
|
||||
}
|
||||
|
||||
// X509Renewed implements [authority.Meter] for [Meter].
|
||||
func (m *Meter) X509Renewed(p provisioner.Interface, err error) {
|
||||
incrProvisionerCounter(m.x509.renewed, p, err)
|
||||
}
|
||||
|
||||
// X509Signed implements [authority.Meter] for [Meter].
|
||||
func (m *Meter) X509Signed(p provisioner.Interface, err error) {
|
||||
incrProvisionerCounter(m.x509.signed, p, err)
|
||||
}
|
||||
|
||||
// X509Authorized implements [authority.Meter] for [Meter].
|
||||
func (m *Meter) X509WebhookAuthorized(p provisioner.Interface, err error) {
|
||||
incrProvisionerCounter(m.x509.webhookAuthorized, p, err)
|
||||
}
|
||||
|
||||
// X509Enriched implements [authority.Meter] for [Meter].
|
||||
func (m *Meter) X509WebhookEnriched(p provisioner.Interface, err error) {
|
||||
incrProvisionerCounter(m.x509.webhookEnriched, p, err)
|
||||
}
|
||||
|
||||
func incrProvisionerCounter(cv *prometheus.CounterVec, p provisioner.Interface, err error) {
|
||||
var name string
|
||||
if p != nil {
|
||||
name = p.GetName()
|
||||
}
|
||||
|
||||
cv.WithLabelValues(name, strconv.FormatBool(err == nil)).Inc()
|
||||
}
|
||||
|
||||
// KMSSigned implements [authority.Meter] for [Meter].
|
||||
func (m *Meter) KMSSigned(err error) {
|
||||
if err == nil {
|
||||
m.kms.signed.Inc()
|
||||
} else {
|
||||
m.kms.errors.Inc()
|
||||
}
|
||||
}
|
||||
|
||||
// provisionerInstruments wraps the counters exported by provisioners.
|
||||
type provisionerInstruments struct {
|
||||
rekeyed *prometheus.CounterVec
|
||||
renewed *prometheus.CounterVec
|
||||
signed *prometheus.CounterVec
|
||||
|
||||
webhookAuthorized *prometheus.CounterVec
|
||||
webhookEnriched *prometheus.CounterVec
|
||||
}
|
||||
|
||||
func newProvisionerInstruments(subsystem string) *provisionerInstruments {
|
||||
return &provisionerInstruments{
|
||||
rekeyed: newCounterVec(subsystem, "rekeyed_total", "Number of certificates rekeyed",
|
||||
"provisioner",
|
||||
"success",
|
||||
),
|
||||
renewed: newCounterVec(subsystem, "renewed_total", "Number of certificates renewed",
|
||||
"provisioner",
|
||||
"success",
|
||||
),
|
||||
signed: newCounterVec(subsystem, "signed_total", "Number of certificates signed",
|
||||
"provisioner",
|
||||
"success",
|
||||
),
|
||||
webhookAuthorized: newCounterVec(subsystem, "webhook_authorized_total", "Number of authorizing webhooks called",
|
||||
"provisioner",
|
||||
"success",
|
||||
),
|
||||
webhookEnriched: newCounterVec(subsystem, "webhook_enriched_total", "Number of enriching webhooks called",
|
||||
"provisioner",
|
||||
"success",
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
type kms struct {
|
||||
signed prometheus.Counter
|
||||
errors prometheus.Counter
|
||||
}
|
||||
|
||||
func newCounterVec(subsystem, name, help string, labels ...string) *prometheus.CounterVec {
|
||||
opts := opts(subsystem, name, help)
|
||||
|
||||
return prometheus.NewCounterVec(prometheus.CounterOpts(opts), labels)
|
||||
}
|
||||
|
||||
func opts(subsystem, name, help string) prometheus.Opts {
|
||||
return prometheus.Opts{
|
||||
Namespace: "step_ca",
|
||||
Subsystem: subsystem,
|
||||
Name: name,
|
||||
Help: help,
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue