Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implementation of the Prometheus endpoint #1669

Merged
merged 20 commits into from
Jan 26, 2024
Merged
Show file tree
Hide file tree
Changes from 19 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
13 changes: 13 additions & 0 deletions authority/authority.go
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,9 @@ type Authority struct {

// If true, do not output initialization logs
quietInit bool

// Called whenever applicable, in order to instrument the authority.
meter Meter
}

// Info contains information about the authority.
Expand All @@ -126,6 +129,7 @@ func New(cfg *config.Config, opts ...Option) (*Authority, error) {
config: cfg,
certificates: new(sync.Map),
validateSCEP: true,
meter: noopMeter{},
}

// Apply options.
Expand All @@ -134,6 +138,9 @@ func New(cfg *config.Config, opts ...Option) (*Authority, error) {
return nil, err
}
}
if a.keyManager != nil {
a.keyManager = &instrumentedKeyManager{a.keyManager, a.meter}
}

if !a.skipInit {
// Initialize authority from options or configuration.
Expand All @@ -151,6 +158,7 @@ func NewEmbedded(opts ...Option) (*Authority, error) {
a := &Authority{
config: &config.Config{},
certificates: new(sync.Map),
meter: noopMeter{},
}

// Apply options.
Expand All @@ -159,6 +167,9 @@ func NewEmbedded(opts ...Option) (*Authority, error) {
return nil, err
}
}
if a.keyManager != nil {
a.keyManager = &instrumentedKeyManager{a.keyManager, a.meter}
}

// Validate required options
switch {
Expand Down Expand Up @@ -337,6 +348,8 @@ func (a *Authority) init() error {
if err != nil {
return err
}

a.keyManager = &instrumentedKeyManager{a.keyManager, a.meter}
}

// Initialize linkedca client if necessary. On a linked RA, the issuer
Expand Down
12 changes: 6 additions & 6 deletions authority/authorize.go
Original file line number Diff line number Diff line change
Expand Up @@ -286,16 +286,16 @@ func (a *Authority) authorizeRevoke(ctx context.Context, token string) error {
// extra extension cannot be found, authorize the renewal by default.
//
// TODO(mariano): should we authorize by default?
func (a *Authority) authorizeRenew(ctx context.Context, cert *x509.Certificate) error {
func (a *Authority) authorizeRenew(ctx context.Context, cert *x509.Certificate) (provisioner.Interface, error) {
serial := cert.SerialNumber.String()
var opts = []interface{}{errs.WithKeyVal("serialNumber", serial)}

isRevoked, err := a.IsRevoked(serial)
if err != nil {
return errs.Wrap(http.StatusInternalServerError, err, "authority.authorizeRenew", opts...)
return nil, errs.Wrap(http.StatusInternalServerError, err, "authority.authorizeRenew", opts...)
}
if isRevoked {
return errs.Unauthorized("authority.authorizeRenew: certificate has been revoked", opts...)
return nil, errs.Unauthorized("authority.authorizeRenew: certificate has been revoked", opts...)
}
p, err := a.LoadProvisionerByCertificate(cert)
if err != nil {
Expand All @@ -305,13 +305,13 @@ func (a *Authority) authorizeRenew(ctx context.Context, cert *x509.Certificate)
// returns the noop provisioner if this happens, and it allows
// certificate renewals.
if p, ok = a.provisioners.LoadByCertificate(cert); !ok {
return errs.Unauthorized("authority.authorizeRenew: provisioner not found", opts...)
return nil, errs.Unauthorized("authority.authorizeRenew: provisioner not found", opts...)
}
}
if err := p.AuthorizeRenew(ctx, cert); err != nil {
return errs.Wrap(http.StatusInternalServerError, err, "authority.authorizeRenew", opts...)
return nil, errs.Wrap(http.StatusInternalServerError, err, "authority.authorizeRenew", opts...)
}
return nil
return p, nil
}

// authorizeSSHCertificate returns an error if the given certificate is revoked.
Expand Down
2 changes: 1 addition & 1 deletion authority/authorize_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -876,7 +876,7 @@ func TestAuthority_authorizeRenew(t *testing.T) {
t.Run(name, func(t *testing.T) {
tc := genTestCase(t)

err := tc.auth.authorizeRenew(context.Background(), tc.cert)
_, err := tc.auth.authorizeRenew(context.Background(), tc.cert)
if err != nil {
if assert.NotNil(t, tc.err) {
var sc render.StatusCodedError
Expand Down
7 changes: 7 additions & 0 deletions authority/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ type Config struct {
Templates *templates.Templates `json:"templates,omitempty"`
CommonName string `json:"commonName,omitempty"`
CRL *CRLConfig `json:"crl,omitempty"`
MetricsAddress string `json:"metricsAddress,omitempty"`
SkipValidation bool `json:"-"`

// Keeps record of the filename the Config is read from
Expand Down Expand Up @@ -327,6 +328,12 @@ func (c *Config) Validate() error {
return errors.Errorf("invalid address %s", c.Address)
}

if addr := c.MetricsAddress; addr != "" {
if _, _, err := net.SplitHostPort(addr); err != nil {
return errors.Errorf("invalid metrics address %q", c.Address)
}
}

if c.TLS == nil {
c.TLS = &DefaultTLSOptions
} else {
Expand Down
87 changes: 87 additions & 0 deletions authority/meter.go
Original file line number Diff line number Diff line change
@@ -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
}
13 changes: 13 additions & 0 deletions authority/options.go
Original file line number Diff line number Diff line change
Expand Up @@ -381,3 +381,16 @@ func readCertificateBundle(pemCerts []byte) ([]*x509.Certificate, error) {
}
return certs, nil
}

// WithMeter is an option that sets the authority's [Meter] to the provided one.
func WithMeter(m Meter) Option {
if m == nil {
m = noopMeter{}
}

return func(a *Authority) (_ error) {
a.meter = m

return
}
}