mirror of
https://github.com/smallstep/certificates.git
synced 2024-11-15 18:12:59 +00:00
Merge branch 'wire-acme-extensions' into herman/wire-acme-improvements
This commit is contained in:
commit
79943d2e5e
@ -49,8 +49,13 @@ func (n *NewOrderRequest) Validate() error {
|
|||||||
if id.Value == "" {
|
if id.Value == "" {
|
||||||
return acme.NewError(acme.ErrorMalformedType, "permanent identifier cannot be empty")
|
return acme.NewError(acme.ErrorMalformedType, "permanent identifier cannot be empty")
|
||||||
}
|
}
|
||||||
case acme.WireID:
|
case acme.WireUser:
|
||||||
wireID, err := wire.ParseID([]byte(id.Value))
|
_, err := wire.ParseUserID([]byte(id.Value))
|
||||||
|
if err != nil {
|
||||||
|
return acme.WrapError(acme.ErrorMalformedType, err, "failed parsing Wire ID")
|
||||||
|
}
|
||||||
|
case acme.WireDevice:
|
||||||
|
wireID, err := wire.ParseDeviceID([]byte(id.Value))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return acme.WrapError(acme.ErrorMalformedType, err, "failed parsing Wire ID")
|
return acme.WrapError(acme.ErrorMalformedType, err, "failed parsing Wire ID")
|
||||||
}
|
}
|
||||||
@ -273,10 +278,28 @@ func newAuthorization(ctx context.Context, az *acme.Authorization) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var target string
|
var target string
|
||||||
if az.Identifier.Type == acme.WireID {
|
switch az.Identifier.Type {
|
||||||
wireID, err := wire.ParseID([]byte(az.Identifier.Value))
|
case acme.WireUser:
|
||||||
|
wireOptions, err := prov.GetOptions().GetWireOptions()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return acme.WrapError(acme.ErrorMalformedType, err, "failed parsing WireID")
|
return acme.WrapErrorISE(err, "failed getting Wire options")
|
||||||
|
}
|
||||||
|
var targetProvider interface{ EvaluateTarget(string) (string, error) }
|
||||||
|
switch typ {
|
||||||
|
case acme.WIREOIDC01:
|
||||||
|
targetProvider = wireOptions.GetOIDCOptions()
|
||||||
|
default:
|
||||||
|
return acme.NewError(acme.ErrorMalformedType, "unsupported type %q", typ)
|
||||||
|
}
|
||||||
|
|
||||||
|
target, err = targetProvider.EvaluateTarget("")
|
||||||
|
if err != nil {
|
||||||
|
return acme.WrapError(acme.ErrorMalformedType, err, "invalid Go template registered for 'target'")
|
||||||
|
}
|
||||||
|
case acme.WireDevice:
|
||||||
|
wireID, err := wire.ParseDeviceID([]byte(az.Identifier.Value))
|
||||||
|
if err != nil {
|
||||||
|
return acme.WrapError(acme.ErrorMalformedType, err, "failed parsing WireUser")
|
||||||
}
|
}
|
||||||
clientID, err := wire.ParseClientID(wireID.ClientID)
|
clientID, err := wire.ParseClientID(wireID.ClientID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -288,8 +311,6 @@ func newAuthorization(ctx context.Context, az *acme.Authorization) error {
|
|||||||
}
|
}
|
||||||
var targetProvider interface{ EvaluateTarget(string) (string, error) }
|
var targetProvider interface{ EvaluateTarget(string) (string, error) }
|
||||||
switch typ {
|
switch typ {
|
||||||
case acme.WIREOIDC01:
|
|
||||||
targetProvider = wireOptions.GetOIDCOptions()
|
|
||||||
case acme.WIREDPOP01:
|
case acme.WIREDPOP01:
|
||||||
targetProvider = wireOptions.GetDPOPOptions()
|
targetProvider = wireOptions.GetDPOPOptions()
|
||||||
default:
|
default:
|
||||||
@ -440,8 +461,10 @@ func challengeTypes(az *acme.Authorization) []acme.ChallengeType {
|
|||||||
}
|
}
|
||||||
case acme.PermanentIdentifier:
|
case acme.PermanentIdentifier:
|
||||||
chTypes = []acme.ChallengeType{acme.DEVICEATTEST01}
|
chTypes = []acme.ChallengeType{acme.DEVICEATTEST01}
|
||||||
case acme.WireID:
|
case acme.WireUser:
|
||||||
chTypes = []acme.ChallengeType{acme.WIREOIDC01, acme.WIREDPOP01}
|
chTypes = []acme.ChallengeType{acme.WIREOIDC01}
|
||||||
|
case acme.WireDevice:
|
||||||
|
chTypes = []acme.ChallengeType{acme.WIREDPOP01}
|
||||||
default:
|
default:
|
||||||
chTypes = []acme.ChallengeType{}
|
chTypes = []acme.ChallengeType{}
|
||||||
}
|
}
|
||||||
|
@ -101,7 +101,7 @@ func TestNewOrderRequest_Validate(t *testing.T) {
|
|||||||
return test{
|
return test{
|
||||||
nor: &NewOrderRequest{
|
nor: &NewOrderRequest{
|
||||||
Identifiers: []acme.Identifier{
|
Identifiers: []acme.Identifier{
|
||||||
{Type: "wireapp-id", Value: "{}"},
|
{Type: "wireapp-device", Value: "{}"},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
err: acme.NewError(acme.ErrorMalformedType, `invalid Wire client ID "": invalid Wire client ID URI "": error parsing : scheme is missing`),
|
err: acme.NewError(acme.ErrorMalformedType, `invalid Wire client ID "": invalid Wire client ID URI "": error parsing : scheme is missing`),
|
||||||
@ -111,7 +111,7 @@ func TestNewOrderRequest_Validate(t *testing.T) {
|
|||||||
return test{
|
return test{
|
||||||
nor: &NewOrderRequest{
|
nor: &NewOrderRequest{
|
||||||
Identifiers: []acme.Identifier{
|
Identifiers: []acme.Identifier{
|
||||||
{Type: "wireapp-id", Value: `{"name": "Smith, Alice M (QA)", "domain": "example.com", "client-id": "nowireapp://example.com", "handle": "wireapp://%40alice.smith.qa@example.com"}`},
|
{Type: "wireapp-device", Value: `{"name": "Smith, Alice M (QA)", "domain": "example.com", "client-id": "nowireapp://example.com", "handle": "wireapp://%40alice.smith.qa@example.com"}`},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
err: acme.NewError(acme.ErrorMalformedType, `invalid Wire client ID "nowireapp://example.com": invalid Wire client ID scheme "nowireapp"; expected "wireapp"`),
|
err: acme.NewError(acme.ErrorMalformedType, `invalid Wire client ID "nowireapp://example.com": invalid Wire client ID scheme "nowireapp"; expected "wireapp"`),
|
||||||
@ -121,7 +121,7 @@ func TestNewOrderRequest_Validate(t *testing.T) {
|
|||||||
return test{
|
return test{
|
||||||
nor: &NewOrderRequest{
|
nor: &NewOrderRequest{
|
||||||
Identifiers: []acme.Identifier{
|
Identifiers: []acme.Identifier{
|
||||||
{Type: "wireapp-id", Value: `{"name": "Smith, Alice M (QA)", "domain": "example.com", "client-id": "wireapp://user-device@example.com", "handle": "wireapp://%40alice.smith.qa@example.com"}`},
|
{Type: "wireapp-device", Value: `{"name": "Smith, Alice M (QA)", "domain": "example.com", "client-id": "wireapp://user-device@example.com", "handle": "wireapp://%40alice.smith.qa@example.com"}`},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
err: acme.NewError(acme.ErrorMalformedType, `invalid Wire client ID "wireapp://user-device@example.com": invalid Wire client ID username "user-device"`),
|
err: acme.NewError(acme.ErrorMalformedType, `invalid Wire client ID "wireapp://user-device@example.com": invalid Wire client ID username "user-device"`),
|
||||||
@ -205,13 +205,28 @@ func TestNewOrderRequest_Validate(t *testing.T) {
|
|||||||
naf: naf,
|
naf: naf,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"ok/wireapp-idd": func(t *testing.T) test {
|
"ok/wireapp-user": func(t *testing.T) test {
|
||||||
nbf := time.Now().UTC().Add(time.Minute)
|
nbf := time.Now().UTC().Add(time.Minute)
|
||||||
naf := time.Now().UTC().Add(5 * time.Minute)
|
naf := time.Now().UTC().Add(5 * time.Minute)
|
||||||
return test{
|
return test{
|
||||||
nor: &NewOrderRequest{
|
nor: &NewOrderRequest{
|
||||||
Identifiers: []acme.Identifier{
|
Identifiers: []acme.Identifier{
|
||||||
{Type: "wireapp-id", Value: `{"name": "Smith, Alice M (QA)", "domain": "example.com", "client-id": "wireapp://lJGYPz0ZRq2kvc_XpdaDlA!ed416ce8ecdd9fad@example.com", "handle": "wireapp://%40alice.smith.qa@example.com"}`},
|
{Type: "wireapp-user", Value: `{"name": "Smith, Alice M (QA)", "domain": "example.com", "handle": "wireapp://%40alice.smith.qa@example.com"}`},
|
||||||
|
},
|
||||||
|
NotAfter: naf,
|
||||||
|
NotBefore: nbf,
|
||||||
|
},
|
||||||
|
nbf: nbf,
|
||||||
|
naf: naf,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ok/wireapp-device": func(t *testing.T) test {
|
||||||
|
nbf := time.Now().UTC().Add(time.Minute)
|
||||||
|
naf := time.Now().UTC().Add(5 * time.Minute)
|
||||||
|
return test{
|
||||||
|
nor: &NewOrderRequest{
|
||||||
|
Identifiers: []acme.Identifier{
|
||||||
|
{Type: "wireapp-device", Value: `{"name": "Smith, Alice M (QA)", "domain": "example.com", "client-id": "wireapp://lJGYPz0ZRq2kvc_XpdaDlA!ed416ce8ecdd9fad@example.com", "handle": "wireapp://%40alice.smith.qa@example.com"}`},
|
||||||
},
|
},
|
||||||
NotAfter: naf,
|
NotAfter: naf,
|
||||||
NotBefore: nbf,
|
NotBefore: nbf,
|
||||||
@ -1719,7 +1734,7 @@ MCowBQYDK2VwAyEA5c+4NKZSNQcR1T8qN6SjwgdPZQ0Ge12Ylx/YeGAJ35k=
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"ok/default-naf-nbf-wireapp": func(t *testing.T) test {
|
"ok/default-naf-nbf-wireapp-user": func(t *testing.T) test {
|
||||||
acmeWireProv := newWireProvisionerWithOptions(t, &provisioner.Options{
|
acmeWireProv := newWireProvisionerWithOptions(t, &provisioner.Options{
|
||||||
Wire: &wire.Options{
|
Wire: &wire.Options{
|
||||||
OIDC: &wire.OIDCOptions{
|
OIDC: &wire.OIDCOptions{
|
||||||
@ -1749,7 +1764,7 @@ MCowBQYDK2VwAyEA5c+4NKZSNQcR1T8qN6SjwgdPZQ0Ge12Ylx/YeGAJ35k=
|
|||||||
acc := &acme.Account{ID: "accID"}
|
acc := &acme.Account{ID: "accID"}
|
||||||
nor := &NewOrderRequest{
|
nor := &NewOrderRequest{
|
||||||
Identifiers: []acme.Identifier{
|
Identifiers: []acme.Identifier{
|
||||||
{Type: "wireapp-id", Value: `{"client-id": "wireapp://user!client@domain"}`},
|
{Type: "wireapp-user", Value: `{"name": "Alice Smith", "handle": "wireapp://%40alice.smith.qa@example.com"}`},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
b, err := json.Marshal(nor)
|
b, err := json.Marshal(nor)
|
||||||
@ -1758,9 +1773,8 @@ MCowBQYDK2VwAyEA5c+4NKZSNQcR1T8qN6SjwgdPZQ0Ge12Ylx/YeGAJ35k=
|
|||||||
ctx = context.WithValue(ctx, accContextKey, acc)
|
ctx = context.WithValue(ctx, accContextKey, acc)
|
||||||
ctx = context.WithValue(ctx, payloadContextKey, &payloadInfo{value: b})
|
ctx = context.WithValue(ctx, payloadContextKey, &payloadInfo{value: b})
|
||||||
var (
|
var (
|
||||||
ch1, ch2 **acme.Challenge
|
ch1 **acme.Challenge
|
||||||
az1ID *string
|
az1ID *string
|
||||||
count = 0
|
|
||||||
)
|
)
|
||||||
return test{
|
return test{
|
||||||
ctx: ctx,
|
ctx: ctx,
|
||||||
@ -1769,20 +1783,113 @@ MCowBQYDK2VwAyEA5c+4NKZSNQcR1T8qN6SjwgdPZQ0Ge12Ylx/YeGAJ35k=
|
|||||||
ca: &mockCA{},
|
ca: &mockCA{},
|
||||||
db: &acme.MockDB{
|
db: &acme.MockDB{
|
||||||
MockCreateChallenge: func(ctx context.Context, ch *acme.Challenge) error {
|
MockCreateChallenge: func(ctx context.Context, ch *acme.Challenge) error {
|
||||||
switch count {
|
ch.ID = "wireapp-oidc"
|
||||||
case 0:
|
assert.Equals(t, ch.Type, acme.WIREOIDC01)
|
||||||
ch.ID = "wireapp-oidc"
|
ch1 = &ch
|
||||||
assert.Equals(t, ch.Type, acme.WIREOIDC01)
|
assert.Equals(t, ch.AccountID, "accID")
|
||||||
ch1 = &ch
|
assert.NotEquals(t, ch.Token, "")
|
||||||
case 1:
|
assert.Equals(t, ch.Status, acme.StatusPending)
|
||||||
ch.ID = "wireapp-dpop"
|
assert.Equals(t, ch.Value, `{"name": "Alice Smith", "handle": "wireapp://%40alice.smith.qa@example.com"}`)
|
||||||
assert.Equals(t, ch.Type, acme.WIREDPOP01)
|
return nil
|
||||||
ch2 = &ch
|
},
|
||||||
default:
|
MockCreateAuthorization: func(ctx context.Context, az *acme.Authorization) error {
|
||||||
assert.FatalError(t, errors.New("test logic error"))
|
az.ID = "az1ID"
|
||||||
return errors.New("force")
|
az1ID = &az.ID
|
||||||
}
|
assert.Equals(t, az.AccountID, "accID")
|
||||||
count++
|
assert.NotEquals(t, az.Token, "")
|
||||||
|
assert.Equals(t, az.Status, acme.StatusPending)
|
||||||
|
assert.Equals(t, az.Identifier, nor.Identifiers[0])
|
||||||
|
assert.Equals(t, az.Challenges, []*acme.Challenge{*ch1})
|
||||||
|
assert.Equals(t, az.Wildcard, false)
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
MockCreateOrder: func(ctx context.Context, o *acme.Order) error {
|
||||||
|
o.ID = "ordID"
|
||||||
|
assert.Equals(t, o.AccountID, "accID")
|
||||||
|
assert.Equals(t, o.ProvisionerID, prov.GetID())
|
||||||
|
assert.Equals(t, o.Status, acme.StatusPending)
|
||||||
|
assert.Equals(t, o.Identifiers, nor.Identifiers)
|
||||||
|
assert.Equals(t, o.AuthorizationIDs, []string{*az1ID})
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
MockGetExternalAccountKeyByAccountID: func(ctx context.Context, provisionerID, accountID string) (*acme.ExternalAccountKey, error) {
|
||||||
|
assert.Equals(t, prov.GetID(), provisionerID)
|
||||||
|
assert.Equals(t, "accID", accountID)
|
||||||
|
return nil, nil
|
||||||
|
},
|
||||||
|
},
|
||||||
|
vr: func(t *testing.T, o *acme.Order) {
|
||||||
|
now := clock.Now()
|
||||||
|
testBufferDur := 5 * time.Second
|
||||||
|
orderExpiry := now.Add(defaultOrderExpiry)
|
||||||
|
expNbf := now.Add(-defaultOrderBackdate)
|
||||||
|
expNaf := now.Add(prov.DefaultTLSCertDuration())
|
||||||
|
|
||||||
|
assert.Equals(t, o.ID, "ordID")
|
||||||
|
assert.Equals(t, o.Status, acme.StatusPending)
|
||||||
|
assert.Equals(t, o.Identifiers, nor.Identifiers)
|
||||||
|
assert.Equals(t, o.AuthorizationURLs, []string{fmt.Sprintf("%s/acme/%s/authz/az1ID", baseURL.String(), escProvName)})
|
||||||
|
assert.True(t, o.NotBefore.Add(-testBufferDur).Before(expNbf))
|
||||||
|
assert.True(t, o.NotBefore.Add(testBufferDur).After(expNbf))
|
||||||
|
assert.True(t, o.NotAfter.Add(-testBufferDur).Before(expNaf))
|
||||||
|
assert.True(t, o.NotAfter.Add(testBufferDur).After(expNaf))
|
||||||
|
assert.True(t, o.ExpiresAt.Add(-testBufferDur).Before(orderExpiry))
|
||||||
|
assert.True(t, o.ExpiresAt.Add(testBufferDur).After(orderExpiry))
|
||||||
|
},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ok/default-naf-nbf-wireapp-device": func(t *testing.T) test {
|
||||||
|
acmeWireProv := newWireProvisionerWithOptions(t, &provisioner.Options{
|
||||||
|
Wire: &wire.Options{
|
||||||
|
OIDC: &wire.OIDCOptions{
|
||||||
|
Provider: &wire.Provider{
|
||||||
|
IssuerURL: "https://issuer.example.com",
|
||||||
|
AuthURL: "",
|
||||||
|
TokenURL: "",
|
||||||
|
JWKSURL: "",
|
||||||
|
UserInfoURL: "",
|
||||||
|
Algorithms: []string{"ES256"},
|
||||||
|
},
|
||||||
|
Config: &wire.Config{
|
||||||
|
ClientID: "integration test",
|
||||||
|
SignatureAlgorithms: []string{"ES256"},
|
||||||
|
SkipClientIDCheck: true,
|
||||||
|
SkipExpiryCheck: true,
|
||||||
|
SkipIssuerCheck: true,
|
||||||
|
InsecureSkipSignatureCheck: true,
|
||||||
|
Now: time.Now,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
DPOP: &wire.DPOPOptions{
|
||||||
|
SigningKey: []byte(fakeWireSigningKey),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
acc := &acme.Account{ID: "accID"}
|
||||||
|
nor := &NewOrderRequest{
|
||||||
|
Identifiers: []acme.Identifier{
|
||||||
|
{Type: "wireapp-device", Value: `{"client-id": "wireapp://user!client@domain"}`},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
b, err := json.Marshal(nor)
|
||||||
|
assert.FatalError(t, err)
|
||||||
|
ctx := acme.NewProvisionerContext(context.Background(), acmeWireProv)
|
||||||
|
ctx = context.WithValue(ctx, accContextKey, acc)
|
||||||
|
ctx = context.WithValue(ctx, payloadContextKey, &payloadInfo{value: b})
|
||||||
|
var (
|
||||||
|
ch1 **acme.Challenge
|
||||||
|
az1ID *string
|
||||||
|
)
|
||||||
|
return test{
|
||||||
|
ctx: ctx,
|
||||||
|
statusCode: 201,
|
||||||
|
nor: nor,
|
||||||
|
ca: &mockCA{},
|
||||||
|
db: &acme.MockDB{
|
||||||
|
MockCreateChallenge: func(ctx context.Context, ch *acme.Challenge) error {
|
||||||
|
ch.ID = "wireapp-dpop"
|
||||||
|
assert.Equals(t, ch.Type, acme.WIREDPOP01)
|
||||||
|
ch1 = &ch
|
||||||
assert.Equals(t, ch.AccountID, "accID")
|
assert.Equals(t, ch.AccountID, "accID")
|
||||||
assert.NotEquals(t, ch.Token, "")
|
assert.NotEquals(t, ch.Token, "")
|
||||||
assert.Equals(t, ch.Status, acme.StatusPending)
|
assert.Equals(t, ch.Status, acme.StatusPending)
|
||||||
@ -1796,7 +1903,7 @@ MCowBQYDK2VwAyEA5c+4NKZSNQcR1T8qN6SjwgdPZQ0Ge12Ylx/YeGAJ35k=
|
|||||||
assert.NotEquals(t, az.Token, "")
|
assert.NotEquals(t, az.Token, "")
|
||||||
assert.Equals(t, az.Status, acme.StatusPending)
|
assert.Equals(t, az.Status, acme.StatusPending)
|
||||||
assert.Equals(t, az.Identifier, nor.Identifiers[0])
|
assert.Equals(t, az.Identifier, nor.Identifiers[0])
|
||||||
assert.Equals(t, az.Challenges, []*acme.Challenge{*ch1, *ch2})
|
assert.Equals(t, az.Challenges, []*acme.Challenge{*ch1})
|
||||||
assert.Equals(t, az.Wildcard, false)
|
assert.Equals(t, az.Wildcard, false)
|
||||||
return nil
|
return nil
|
||||||
},
|
},
|
||||||
|
@ -234,7 +234,11 @@ func TestWireIntegration(t *testing.T) {
|
|||||||
nor := &NewOrderRequest{
|
nor := &NewOrderRequest{
|
||||||
Identifiers: []acme.Identifier{
|
Identifiers: []acme.Identifier{
|
||||||
{
|
{
|
||||||
Type: "wireapp-id",
|
Type: "wireapp-user",
|
||||||
|
Value: `{"name": "Smith, Alice M (QA)", "domain": "example.com", "handle": "wireapp://%40alice.smith.qa@example.com"}`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Type: "wireapp-device",
|
||||||
Value: `{"name": "Smith, Alice M (QA)", "domain": "example.com", "client-id": "wireapp://lJGYPz0ZRq2kvc_XpdaDlA!ed416ce8ecdd9fad@example.com", "handle": "wireapp://%40alice.smith.qa@example.com"}`,
|
Value: `{"name": "Smith, Alice M (QA)", "domain": "example.com", "client-id": "wireapp://lJGYPz0ZRq2kvc_XpdaDlA!ed416ce8ecdd9fad@example.com", "handle": "wireapp://%40alice.smith.qa@example.com"}`,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -373,7 +373,7 @@ func wireOIDC01Validate(ctx context.Context, ch *Challenge, db DB, jwk *jose.JSO
|
|||||||
return WrapError(ErrorMalformedType, err, "error unmarshalling Wire OIDC challenge payload")
|
return WrapError(ErrorMalformedType, err, "error unmarshalling Wire OIDC challenge payload")
|
||||||
}
|
}
|
||||||
|
|
||||||
wireID, err := wire.ParseID([]byte(ch.Value))
|
wireID, err := wire.ParseUserID([]byte(ch.Value))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return WrapErrorISE(err, "error unmarshalling challenge data")
|
return WrapErrorISE(err, "error unmarshalling challenge data")
|
||||||
}
|
}
|
||||||
@ -451,7 +451,7 @@ func wireOIDC01Validate(ctx context.Context, ch *Challenge, db DB, jwk *jose.JSO
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func validateWireOIDCClaims(o *wireprovisioner.OIDCOptions, token *oidc.IDToken, wireID wire.ID) (map[string]any, error) {
|
func validateWireOIDCClaims(o *wireprovisioner.OIDCOptions, token *oidc.IDToken, wireID wire.UserID) (map[string]any, error) {
|
||||||
var m map[string]any
|
var m map[string]any
|
||||||
if err := token.Claims(&m); err != nil {
|
if err := token.Claims(&m); err != nil {
|
||||||
return nil, fmt.Errorf("failed extracting OIDC ID token claims: %w", err)
|
return nil, fmt.Errorf("failed extracting OIDC ID token claims: %w", err)
|
||||||
@ -500,7 +500,7 @@ func wireDPOP01Validate(ctx context.Context, ch *Challenge, db DB, accountJWK *j
|
|||||||
return WrapError(ErrorMalformedType, err, "error unmarshalling Wire DPoP challenge payload")
|
return WrapError(ErrorMalformedType, err, "error unmarshalling Wire DPoP challenge payload")
|
||||||
}
|
}
|
||||||
|
|
||||||
wireID, err := wire.ParseID([]byte(ch.Value))
|
wireID, err := wire.ParseDeviceID([]byte(ch.Value))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return WrapErrorISE(err, "error unmarshalling challenge data")
|
return WrapErrorISE(err, "error unmarshalling challenge data")
|
||||||
}
|
}
|
||||||
@ -598,7 +598,7 @@ type wireVerifyParams struct {
|
|||||||
dpopKeyID string
|
dpopKeyID string
|
||||||
issuer string
|
issuer string
|
||||||
audience string
|
audience string
|
||||||
wireID wire.ID
|
wireID wire.DeviceID
|
||||||
chToken string
|
chToken string
|
||||||
t time.Time
|
t time.Time
|
||||||
}
|
}
|
||||||
|
@ -100,7 +100,7 @@ MCowBQYDK2VwAyEA5c+4NKZSNQcR1T8qN6SjwgdPZQ0Ge12Ylx/YeGAJ35k=
|
|||||||
Type: "urn:ietf:params:acme:error:serverInternal",
|
Type: "urn:ietf:params:acme:error:serverInternal",
|
||||||
Detail: "The server experienced an internal error",
|
Detail: "The server experienced an internal error",
|
||||||
Status: 500,
|
Status: 500,
|
||||||
Err: errors.New(`error unmarshalling challenge data: json: cannot unmarshal number into Go value of type wire.ID`),
|
Err: errors.New(`error unmarshalling challenge data: json: cannot unmarshal number into Go value of type wire.DeviceID`),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -1096,7 +1096,7 @@ MCowBQYDK2VwAyEA5c+4NKZSNQcR1T8qN6SjwgdPZQ0Ge12Ylx/YeGAJ35k=
|
|||||||
Type: "urn:ietf:params:acme:error:serverInternal",
|
Type: "urn:ietf:params:acme:error:serverInternal",
|
||||||
Detail: "The server experienced an internal error",
|
Detail: "The server experienced an internal error",
|
||||||
Status: 500,
|
Status: 500,
|
||||||
Err: errors.New(`error unmarshalling challenge data: json: cannot unmarshal number into Go value of type wire.ID`),
|
Err: errors.New(`error unmarshalling challenge data: json: cannot unmarshal number into Go value of type wire.UserID`),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -2030,7 +2030,7 @@ MCowBQYDK2VwAyEAB2IYqBWXAouDt3WcCZgCM3t9gumMEKMlgMsGenSu+fA=
|
|||||||
require.True(t, ok)
|
require.True(t, ok)
|
||||||
|
|
||||||
issuer := "http://wire.com:19983/clients/7a41cf5b79683410/access-token"
|
issuer := "http://wire.com:19983/clients/7a41cf5b79683410/access-token"
|
||||||
wireID := wire.ID{
|
wireID := wire.DeviceID{
|
||||||
ClientID: "wireapp://guVX5xeFS3eTatmXBIyA4A!7a41cf5b79683410@wire.com",
|
ClientID: "wireapp://guVX5xeFS3eTatmXBIyA4A!7a41cf5b79683410@wire.com",
|
||||||
Handle: "wireapp://%40alice_wire@wire.com",
|
Handle: "wireapp://%40alice_wire@wire.com",
|
||||||
}
|
}
|
||||||
@ -2127,7 +2127,7 @@ MCowBQYDK2VwAyEA5c+4NKZSNQcR1T8qN6SjwgdPZQ0Ge12Ylx/YeGAJ35k=
|
|||||||
idToken, err := verifier.Verify(ctx, idTokenString)
|
idToken, err := verifier.Verify(ctx, idTokenString)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
wireID := wire.ID{
|
wireID := wire.UserID{
|
||||||
Name: "Alice Smith",
|
Name: "Alice Smith",
|
||||||
Handle: "wireapp://%40alice_wire@wire.com",
|
Handle: "wireapp://%40alice_wire@wire.com",
|
||||||
}
|
}
|
||||||
|
@ -31,8 +31,10 @@ const (
|
|||||||
// PermanentIdentifier is the ACME permanent-identifier identifier type
|
// PermanentIdentifier is the ACME permanent-identifier identifier type
|
||||||
// defined in https://datatracker.ietf.org/doc/html/draft-bweeks-acme-device-attest-00
|
// defined in https://datatracker.ietf.org/doc/html/draft-bweeks-acme-device-attest-00
|
||||||
PermanentIdentifier IdentifierType = "permanent-identifier"
|
PermanentIdentifier IdentifierType = "permanent-identifier"
|
||||||
// WireID is the Wire user identifier type
|
// WireUser is the Wire user identifier type
|
||||||
WireID IdentifierType = "wireapp-id"
|
WireUser IdentifierType = "wireapp-user"
|
||||||
|
// WireDevice is the Wire device identifier type
|
||||||
|
WireDevice IdentifierType = "wireapp-device"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Identifier encodes the type that an order pertains to.
|
// Identifier encodes the type that an order pertains to.
|
||||||
@ -322,23 +324,23 @@ func (o *Order) Finalize(ctx context.Context, db DB, csr *x509.CertificateReques
|
|||||||
}
|
}
|
||||||
|
|
||||||
// containsWireIdentifiers checks if [Order] contains ACME
|
// containsWireIdentifiers checks if [Order] contains ACME
|
||||||
// identifiers for the WireID type.
|
// identifiers for the WireUser or WireDevice types.
|
||||||
func (o *Order) containsWireIdentifiers() bool {
|
func (o *Order) containsWireIdentifiers() bool {
|
||||||
for _, i := range o.Identifiers {
|
for _, i := range o.Identifiers {
|
||||||
if i.Type == WireID {
|
if i.Type == WireUser || i.Type == WireDevice {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// createWireSubject creates the subject for an [Order] with WireID identifiers.
|
// createWireSubject creates the subject for an [Order] with WireUser identifiers.
|
||||||
func createWireSubject(o *Order, csr *x509.CertificateRequest) (subject x509util.Subject, err error) {
|
func createWireSubject(o *Order, csr *x509.CertificateRequest) (subject x509util.Subject, err error) {
|
||||||
wireIDs, otherIDs := 0, 0
|
wireUserIDs, wireDeviceIDs, otherIDs := 0, 0, 0
|
||||||
for _, identifier := range o.Identifiers {
|
for _, identifier := range o.Identifiers {
|
||||||
switch identifier.Type {
|
switch identifier.Type {
|
||||||
case WireID:
|
case WireUser:
|
||||||
wireID, err := wire.ParseID([]byte(identifier.Value))
|
wireID, err := wire.ParseUserID([]byte(identifier.Value))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return subject, NewErrorISE("unmarshal wireID: %s", err)
|
return subject, NewErrorISE("unmarshal wireID: %s", err)
|
||||||
}
|
}
|
||||||
@ -357,7 +359,7 @@ func createWireSubject(o *Order, csr *x509.CertificateRequest) (subject x509util
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if !foundDisplayName {
|
if !foundDisplayName {
|
||||||
return subject, NewErrorISE("CSR must contain the display name in 2.16.840.1.113730.3.1.241 OID")
|
return subject, NewErrorISE("CSR must contain the display name in '2.16.840.1.113730.3.1.241' OID")
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(csr.Subject.Organization) == 0 || !strings.EqualFold(csr.Subject.Organization[0], wireID.Domain) {
|
if len(csr.Subject.Organization) == 0 || !strings.EqualFold(csr.Subject.Organization[0], wireID.Domain) {
|
||||||
@ -365,14 +367,16 @@ func createWireSubject(o *Order, csr *x509.CertificateRequest) (subject x509util
|
|||||||
}
|
}
|
||||||
subject.CommonName = wireID.Name
|
subject.CommonName = wireID.Name
|
||||||
subject.Organization = []string{wireID.Domain}
|
subject.Organization = []string{wireID.Domain}
|
||||||
wireIDs++
|
wireUserIDs++
|
||||||
|
case WireDevice:
|
||||||
|
wireDeviceIDs++
|
||||||
default:
|
default:
|
||||||
otherIDs++
|
otherIDs++
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if wireIDs > 0 && otherIDs > 0 || wireIDs > 1 {
|
if otherIDs > 0 || wireUserIDs != 1 && wireDeviceIDs != 1 {
|
||||||
return subject, NewErrorISE("at most one WireID can be signed along with no other ID, found %d WireIDs and %d other IDs", wireIDs, otherIDs)
|
return subject, NewErrorISE("order must have exactly one WireUser and WireDevice identifier")
|
||||||
}
|
}
|
||||||
|
|
||||||
return
|
return
|
||||||
@ -385,10 +389,10 @@ func (o *Order) sans(csr *x509.CertificateRequest) ([]x509util.SubjectAlternativ
|
|||||||
}
|
}
|
||||||
|
|
||||||
// order the DNS names and IP addresses, so that they can be compared against the canonicalized CSR
|
// order the DNS names and IP addresses, so that they can be compared against the canonicalized CSR
|
||||||
orderNames := make([]string, numberOfIdentifierType(DNS, o.Identifiers)+2*numberOfIdentifierType(WireID, o.Identifiers))
|
orderNames := make([]string, numberOfIdentifierType(DNS, o.Identifiers))
|
||||||
orderIPs := make([]net.IP, numberOfIdentifierType(IP, o.Identifiers))
|
orderIPs := make([]net.IP, numberOfIdentifierType(IP, o.Identifiers))
|
||||||
orderPIDs := make([]string, numberOfIdentifierType(PermanentIdentifier, o.Identifiers))
|
orderPIDs := make([]string, numberOfIdentifierType(PermanentIdentifier, o.Identifiers))
|
||||||
tmpOrderURIs := make([]*url.URL, 2*numberOfIdentifierType(WireID, o.Identifiers))
|
tmpOrderURIs := make([]*url.URL, numberOfIdentifierType(WireUser, o.Identifiers)+numberOfIdentifierType(WireDevice, o.Identifiers))
|
||||||
indexDNS, indexIP, indexPID, indexURI := 0, 0, 0, 0
|
indexDNS, indexIP, indexPID, indexURI := 0, 0, 0, 0
|
||||||
for _, n := range o.Identifiers {
|
for _, n := range o.Identifiers {
|
||||||
switch n.Type {
|
switch n.Type {
|
||||||
@ -401,8 +405,19 @@ func (o *Order) sans(csr *x509.CertificateRequest) ([]x509util.SubjectAlternativ
|
|||||||
case PermanentIdentifier:
|
case PermanentIdentifier:
|
||||||
orderPIDs[indexPID] = n.Value
|
orderPIDs[indexPID] = n.Value
|
||||||
indexPID++
|
indexPID++
|
||||||
case WireID:
|
case WireUser:
|
||||||
wireID, err := wire.ParseID([]byte(n.Value))
|
wireID, err := wire.ParseUserID([]byte(n.Value))
|
||||||
|
if err != nil {
|
||||||
|
return sans, NewErrorISE("unsupported identifier value in order: %s", n.Value)
|
||||||
|
}
|
||||||
|
handle, err := url.Parse(wireID.Handle)
|
||||||
|
if err != nil {
|
||||||
|
return sans, NewErrorISE("handle must be a URI: %s", wireID.Handle)
|
||||||
|
}
|
||||||
|
tmpOrderURIs[indexURI] = handle
|
||||||
|
indexURI++
|
||||||
|
case WireDevice:
|
||||||
|
wireID, err := wire.ParseDeviceID([]byte(n.Value))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return sans, NewErrorISE("unsupported identifier value in order: %s", n.Value)
|
return sans, NewErrorISE("unsupported identifier value in order: %s", n.Value)
|
||||||
}
|
}
|
||||||
@ -412,12 +427,6 @@ func (o *Order) sans(csr *x509.CertificateRequest) ([]x509util.SubjectAlternativ
|
|||||||
}
|
}
|
||||||
tmpOrderURIs[indexURI] = clientID
|
tmpOrderURIs[indexURI] = clientID
|
||||||
indexURI++
|
indexURI++
|
||||||
handle, err := url.Parse(wireID.Handle)
|
|
||||||
if err != nil {
|
|
||||||
return sans, NewErrorISE("handle must be a URI: %s", wireID.Handle)
|
|
||||||
}
|
|
||||||
tmpOrderURIs[indexURI] = handle
|
|
||||||
indexURI++
|
|
||||||
default:
|
default:
|
||||||
return sans, NewErrorISE("unsupported identifier type in order: %s", n.Type)
|
return sans, NewErrorISE("unsupported identifier type in order: %s", n.Type)
|
||||||
}
|
}
|
||||||
|
@ -8,15 +8,26 @@ import (
|
|||||||
"go.step.sm/crypto/kms/uri"
|
"go.step.sm/crypto/kms/uri"
|
||||||
)
|
)
|
||||||
|
|
||||||
type ID struct {
|
type UserID struct {
|
||||||
|
Name string `json:"name,omitempty"`
|
||||||
|
Domain string `json:"domain,omitempty"`
|
||||||
|
Handle string `json:"handle,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type DeviceID struct {
|
||||||
Name string `json:"name,omitempty"`
|
Name string `json:"name,omitempty"`
|
||||||
Domain string `json:"domain,omitempty"`
|
Domain string `json:"domain,omitempty"`
|
||||||
ClientID string `json:"client-id,omitempty"`
|
ClientID string `json:"client-id,omitempty"`
|
||||||
Handle string `json:"handle,omitempty"`
|
Handle string `json:"handle,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func ParseID(data []byte) (wireID ID, err error) {
|
func ParseUserID(data []byte) (id UserID, err error) {
|
||||||
err = json.Unmarshal(data, &wireID)
|
err = json.Unmarshal(data, &id)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func ParseDeviceID(data []byte) (id DeviceID, err error) {
|
||||||
|
err = json.Unmarshal(data, &id)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -7,19 +7,43 @@ import (
|
|||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestParseID(t *testing.T) {
|
func TestParseUserID(t *testing.T) {
|
||||||
ok := `{"name": "Alice Smith", "domain": "wire.com", "client-id": "wireapp://CzbfFjDOQrenCbDxVmgnFw!594930e9d50bb175@wire.com", "handle": "wireapp://%40alice_wire@wire.com"}`
|
ok := `{"name": "Alice Smith", "domain": "wire.com", "handle": "wireapp://%40alice_wire@wire.com"}`
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
data []byte
|
data []byte
|
||||||
wantWireID ID
|
wantWireID UserID
|
||||||
expectedErr error
|
expectedErr error
|
||||||
}{
|
}{
|
||||||
{name: "ok", data: []byte(ok), wantWireID: ID{Name: "Alice Smith", Domain: "wire.com", ClientID: "wireapp://CzbfFjDOQrenCbDxVmgnFw!594930e9d50bb175@wire.com", Handle: "wireapp://%40alice_wire@wire.com"}},
|
{name: "ok", data: []byte(ok), wantWireID: UserID{Name: "Alice Smith", Domain: "wire.com", Handle: "wireapp://%40alice_wire@wire.com"}},
|
||||||
}
|
}
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
gotWireID, err := ParseID(tt.data)
|
gotWireID, err := ParseUserID(tt.data)
|
||||||
|
if tt.expectedErr != nil {
|
||||||
|
assert.EqualError(t, err, tt.expectedErr.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, tt.wantWireID, gotWireID)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestParseDeviceID(t *testing.T) {
|
||||||
|
ok := `{"name": "device", "domain": "wire.com", "client-id": "wireapp://CzbfFjDOQrenCbDxVmgnFw!594930e9d50bb175@wire.com", "handle": "wireapp://%40alice_wire@wire.com"}`
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
data []byte
|
||||||
|
wantWireID DeviceID
|
||||||
|
expectedErr error
|
||||||
|
}{
|
||||||
|
{name: "ok", data: []byte(ok), wantWireID: DeviceID{Name: "device", Domain: "wire.com", ClientID: "wireapp://CzbfFjDOQrenCbDxVmgnFw!594930e9d50bb175@wire.com", Handle: "wireapp://%40alice_wire@wire.com"}},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
gotWireID, err := ParseDeviceID(tt.data)
|
||||||
if tt.expectedErr != nil {
|
if tt.expectedErr != nil {
|
||||||
assert.EqualError(t, err, tt.expectedErr.Error())
|
assert.EqualError(t, err, tt.expectedErr.Error())
|
||||||
return
|
return
|
||||||
|
@ -104,6 +104,9 @@ type Authority struct {
|
|||||||
|
|
||||||
// If true, do not output initialization logs
|
// If true, do not output initialization logs
|
||||||
quietInit bool
|
quietInit bool
|
||||||
|
|
||||||
|
// Called whenever applicable, in order to instrument the authority.
|
||||||
|
meter Meter
|
||||||
}
|
}
|
||||||
|
|
||||||
// Info contains information about the authority.
|
// Info contains information about the authority.
|
||||||
@ -126,6 +129,7 @@ func New(cfg *config.Config, opts ...Option) (*Authority, error) {
|
|||||||
config: cfg,
|
config: cfg,
|
||||||
certificates: new(sync.Map),
|
certificates: new(sync.Map),
|
||||||
validateSCEP: true,
|
validateSCEP: true,
|
||||||
|
meter: noopMeter{},
|
||||||
}
|
}
|
||||||
|
|
||||||
// Apply options.
|
// Apply options.
|
||||||
@ -134,6 +138,9 @@ func New(cfg *config.Config, opts ...Option) (*Authority, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if a.keyManager != nil {
|
||||||
|
a.keyManager = &instrumentedKeyManager{a.keyManager, a.meter}
|
||||||
|
}
|
||||||
|
|
||||||
if !a.skipInit {
|
if !a.skipInit {
|
||||||
// Initialize authority from options or configuration.
|
// Initialize authority from options or configuration.
|
||||||
@ -151,6 +158,7 @@ func NewEmbedded(opts ...Option) (*Authority, error) {
|
|||||||
a := &Authority{
|
a := &Authority{
|
||||||
config: &config.Config{},
|
config: &config.Config{},
|
||||||
certificates: new(sync.Map),
|
certificates: new(sync.Map),
|
||||||
|
meter: noopMeter{},
|
||||||
}
|
}
|
||||||
|
|
||||||
// Apply options.
|
// Apply options.
|
||||||
@ -159,6 +167,9 @@ func NewEmbedded(opts ...Option) (*Authority, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if a.keyManager != nil {
|
||||||
|
a.keyManager = &instrumentedKeyManager{a.keyManager, a.meter}
|
||||||
|
}
|
||||||
|
|
||||||
// Validate required options
|
// Validate required options
|
||||||
switch {
|
switch {
|
||||||
@ -337,6 +348,8 @@ func (a *Authority) init() error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
a.keyManager = &instrumentedKeyManager{a.keyManager, a.meter}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialize linkedca client if necessary. On a linked RA, the issuer
|
// Initialize linkedca client if necessary. On a linked RA, the issuer
|
||||||
|
@ -286,16 +286,16 @@ func (a *Authority) authorizeRevoke(ctx context.Context, token string) error {
|
|||||||
// extra extension cannot be found, authorize the renewal by default.
|
// extra extension cannot be found, authorize the renewal by default.
|
||||||
//
|
//
|
||||||
// TODO(mariano): should we authorize 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()
|
serial := cert.SerialNumber.String()
|
||||||
var opts = []interface{}{errs.WithKeyVal("serialNumber", serial)}
|
var opts = []interface{}{errs.WithKeyVal("serialNumber", serial)}
|
||||||
|
|
||||||
isRevoked, err := a.IsRevoked(serial)
|
isRevoked, err := a.IsRevoked(serial)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errs.Wrap(http.StatusInternalServerError, err, "authority.authorizeRenew", opts...)
|
return nil, errs.Wrap(http.StatusInternalServerError, err, "authority.authorizeRenew", opts...)
|
||||||
}
|
}
|
||||||
if isRevoked {
|
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)
|
p, err := a.LoadProvisionerByCertificate(cert)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -305,13 +305,13 @@ func (a *Authority) authorizeRenew(ctx context.Context, cert *x509.Certificate)
|
|||||||
// returns the noop provisioner if this happens, and it allows
|
// returns the noop provisioner if this happens, and it allows
|
||||||
// certificate renewals.
|
// certificate renewals.
|
||||||
if p, ok = a.provisioners.LoadByCertificate(cert); !ok {
|
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 {
|
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.
|
// authorizeSSHCertificate returns an error if the given certificate is revoked.
|
||||||
|
@ -876,7 +876,7 @@ func TestAuthority_authorizeRenew(t *testing.T) {
|
|||||||
t.Run(name, func(t *testing.T) {
|
t.Run(name, func(t *testing.T) {
|
||||||
tc := genTestCase(t)
|
tc := genTestCase(t)
|
||||||
|
|
||||||
err := tc.auth.authorizeRenew(context.Background(), tc.cert)
|
_, err := tc.auth.authorizeRenew(context.Background(), tc.cert)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if assert.NotNil(t, tc.err) {
|
if assert.NotNil(t, tc.err) {
|
||||||
var sc render.StatusCodedError
|
var sc render.StatusCodedError
|
||||||
|
@ -83,6 +83,7 @@ type Config struct {
|
|||||||
Templates *templates.Templates `json:"templates,omitempty"`
|
Templates *templates.Templates `json:"templates,omitempty"`
|
||||||
CommonName string `json:"commonName,omitempty"`
|
CommonName string `json:"commonName,omitempty"`
|
||||||
CRL *CRLConfig `json:"crl,omitempty"`
|
CRL *CRLConfig `json:"crl,omitempty"`
|
||||||
|
MetricsAddress string `json:"metricsAddress,omitempty"`
|
||||||
SkipValidation bool `json:"-"`
|
SkipValidation bool `json:"-"`
|
||||||
|
|
||||||
// Keeps record of the filename the Config is read from
|
// Keeps record of the filename the Config is read from
|
||||||
@ -327,6 +328,12 @@ func (c *Config) Validate() error {
|
|||||||
return errors.Errorf("invalid address %s", c.Address)
|
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 {
|
if c.TLS == nil {
|
||||||
c.TLS = &DefaultTLSOptions
|
c.TLS = &DefaultTLSOptions
|
||||||
} else {
|
} else {
|
||||||
|
87
authority/meter.go
Normal file
87
authority/meter.go
Normal file
@ -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
|
||||||
|
}
|
@ -381,3 +381,16 @@ func readCertificateBundle(pemCerts []byte) ([]*x509.Certificate, error) {
|
|||||||
}
|
}
|
||||||
return certs, nil
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -10,9 +10,8 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"go.step.sm/linkedca"
|
|
||||||
|
|
||||||
"github.com/smallstep/certificates/acme/wire"
|
"github.com/smallstep/certificates/acme/wire"
|
||||||
|
"go.step.sm/linkedca"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ACMEChallenge represents the supported acme challenges.
|
// ACMEChallenge represents the supported acme challenges.
|
||||||
@ -224,8 +223,10 @@ const (
|
|||||||
IP ACMEIdentifierType = "ip"
|
IP ACMEIdentifierType = "ip"
|
||||||
// DNS is the ACME dns identifier type
|
// DNS is the ACME dns identifier type
|
||||||
DNS ACMEIdentifierType = "dns"
|
DNS ACMEIdentifierType = "dns"
|
||||||
// WireID is the Wire user identifier type
|
// WireUser is the Wire user identifier type
|
||||||
WireID ACMEIdentifierType = "wireapp-id"
|
WireUser ACMEIdentifierType = "wireapp-user"
|
||||||
|
// WireDevice is the Wire device identifier type
|
||||||
|
WireDevice ACMEIdentifierType = "wireapp-device"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ACMEIdentifier encodes ACME Order Identifiers
|
// ACMEIdentifier encodes ACME Order Identifiers
|
||||||
@ -251,12 +252,18 @@ func (p *ACME) AuthorizeOrderIdentifier(_ context.Context, identifier ACMEIdenti
|
|||||||
err = x509Policy.IsIPAllowed(net.ParseIP(identifier.Value))
|
err = x509Policy.IsIPAllowed(net.ParseIP(identifier.Value))
|
||||||
case DNS:
|
case DNS:
|
||||||
err = x509Policy.IsDNSAllowed(identifier.Value)
|
err = x509Policy.IsDNSAllowed(identifier.Value)
|
||||||
case WireID:
|
case WireUser:
|
||||||
var wireID wire.ID
|
var wireID wire.UserID
|
||||||
if wireID, err = wire.ParseID([]byte(identifier.Value)); err != nil {
|
if wireID, err = wire.ParseUserID([]byte(identifier.Value)); err != nil {
|
||||||
return fmt.Errorf("failed parsing Wire SANs: %w", err)
|
return fmt.Errorf("failed parsing Wire SANs: %w", err)
|
||||||
}
|
}
|
||||||
err = x509Policy.AreSANsAllowed([]string{wireID.ClientID, wireID.Handle})
|
err = x509Policy.AreSANsAllowed([]string{wireID.Handle})
|
||||||
|
case WireDevice:
|
||||||
|
var wireID wire.DeviceID
|
||||||
|
if wireID, err = wire.ParseDeviceID([]byte(identifier.Value)); err != nil {
|
||||||
|
return fmt.Errorf("failed parsing Wire SANs: %w", err)
|
||||||
|
}
|
||||||
|
err = x509Policy.AreSANsAllowed([]string{wireID.ClientID})
|
||||||
default:
|
default:
|
||||||
err = fmt.Errorf("invalid ACME identifier type '%s' provided", identifier.Type)
|
err = fmt.Errorf("invalid ACME identifier type '%s' provided", identifier.Type)
|
||||||
}
|
}
|
||||||
|
150
authority/ssh.go
150
authority/ssh.go
@ -146,7 +146,13 @@ func (a *Authority) GetSSHBastion(ctx context.Context, user, hostname string) (*
|
|||||||
}
|
}
|
||||||
|
|
||||||
// SignSSH creates a signed SSH certificate with the given public key and options.
|
// SignSSH creates a signed SSH certificate with the given public key and options.
|
||||||
func (a *Authority) SignSSH(_ context.Context, key ssh.PublicKey, opts provisioner.SignSSHOptions, signOpts ...provisioner.SignOption) (*ssh.Certificate, error) {
|
func (a *Authority) SignSSH(ctx context.Context, key ssh.PublicKey, opts provisioner.SignSSHOptions, signOpts ...provisioner.SignOption) (*ssh.Certificate, error) {
|
||||||
|
cert, prov, err := a.signSSH(ctx, key, opts, signOpts...)
|
||||||
|
a.meter.SSHSigned(prov, err)
|
||||||
|
return cert, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *Authority) signSSH(_ context.Context, key ssh.PublicKey, opts provisioner.SignSSHOptions, signOpts ...provisioner.SignOption) (*ssh.Certificate, provisioner.Interface, error) {
|
||||||
var (
|
var (
|
||||||
certOptions []sshutil.Option
|
certOptions []sshutil.Option
|
||||||
mods []provisioner.SSHCertModifier
|
mods []provisioner.SSHCertModifier
|
||||||
@ -155,7 +161,7 @@ func (a *Authority) SignSSH(_ context.Context, key ssh.PublicKey, opts provision
|
|||||||
|
|
||||||
// Validate given options.
|
// Validate given options.
|
||||||
if err := opts.Validate(); err != nil {
|
if err := opts.Validate(); err != nil {
|
||||||
return nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set backdate with the configured value
|
// Set backdate with the configured value
|
||||||
@ -184,7 +190,7 @@ func (a *Authority) SignSSH(_ context.Context, key ssh.PublicKey, opts provision
|
|||||||
// validate the given SSHOptions
|
// validate the given SSHOptions
|
||||||
case provisioner.SSHCertOptionsValidator:
|
case provisioner.SSHCertOptionsValidator:
|
||||||
if err := o.Valid(opts); err != nil {
|
if err := o.Valid(opts); err != nil {
|
||||||
return nil, errs.BadRequestErr(err, "error validating ssh certificate options")
|
return nil, prov, errs.BadRequestErr(err, "error validating ssh certificate options")
|
||||||
}
|
}
|
||||||
|
|
||||||
// call webhooks
|
// call webhooks
|
||||||
@ -192,7 +198,7 @@ func (a *Authority) SignSSH(_ context.Context, key ssh.PublicKey, opts provision
|
|||||||
webhookCtl = o
|
webhookCtl = o
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return nil, errs.InternalServer("authority.SignSSH: invalid extra option type %T", o)
|
return nil, prov, errs.InternalServer("authority.SignSSH: invalid extra option type %T", o)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -205,8 +211,8 @@ func (a *Authority) SignSSH(_ context.Context, key ssh.PublicKey, opts provision
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Call enriching webhooks
|
// Call enriching webhooks
|
||||||
if err := callEnrichingWebhooksSSH(webhookCtl, cr); err != nil {
|
if err := a.callEnrichingWebhooksSSH(prov, webhookCtl, cr); err != nil {
|
||||||
return nil, errs.ApplyOptions(
|
return nil, prov, errs.ApplyOptions(
|
||||||
errs.ForbiddenErr(err, err.Error()),
|
errs.ForbiddenErr(err, err.Error()),
|
||||||
errs.WithKeyVal("signOptions", signOpts),
|
errs.WithKeyVal("signOptions", signOpts),
|
||||||
)
|
)
|
||||||
@ -216,20 +222,21 @@ func (a *Authority) SignSSH(_ context.Context, key ssh.PublicKey, opts provision
|
|||||||
certificate, err := sshutil.NewCertificate(cr, certOptions...)
|
certificate, err := sshutil.NewCertificate(cr, certOptions...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
var te *sshutil.TemplateError
|
var te *sshutil.TemplateError
|
||||||
if errors.As(err, &te) {
|
switch {
|
||||||
return nil, errs.ApplyOptions(
|
case errors.As(err, &te):
|
||||||
|
return nil, prov, errs.ApplyOptions(
|
||||||
errs.BadRequestErr(err, err.Error()),
|
errs.BadRequestErr(err, err.Error()),
|
||||||
errs.WithKeyVal("signOptions", signOpts),
|
errs.WithKeyVal("signOptions", signOpts),
|
||||||
)
|
)
|
||||||
}
|
case strings.HasPrefix(err.Error(), "error unmarshaling certificate"):
|
||||||
// explicitly check for unmarshaling errors, which are most probably caused by JSON template syntax errors
|
// explicitly check for unmarshaling errors, which are most probably caused by JSON template syntax errors
|
||||||
if strings.HasPrefix(err.Error(), "error unmarshaling certificate") {
|
return nil, prov, errs.InternalServerErr(templatingError(err),
|
||||||
return nil, errs.InternalServerErr(templatingError(err),
|
|
||||||
errs.WithKeyVal("signOptions", signOpts),
|
errs.WithKeyVal("signOptions", signOpts),
|
||||||
errs.WithMessage("error applying certificate template"),
|
errs.WithMessage("error applying certificate template"),
|
||||||
)
|
)
|
||||||
|
default:
|
||||||
|
return nil, prov, errs.Wrap(http.StatusInternalServerError, err, "authority.SignSSH")
|
||||||
}
|
}
|
||||||
return nil, errs.Wrap(http.StatusInternalServerError, err, "authority.SignSSH")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get actual *ssh.Certificate and continue with provisioner modifiers.
|
// Get actual *ssh.Certificate and continue with provisioner modifiers.
|
||||||
@ -238,13 +245,13 @@ func (a *Authority) SignSSH(_ context.Context, key ssh.PublicKey, opts provision
|
|||||||
// Use SignSSHOptions to modify the certificate validity. It will be later
|
// Use SignSSHOptions to modify the certificate validity. It will be later
|
||||||
// checked or set if not defined.
|
// checked or set if not defined.
|
||||||
if err := opts.ModifyValidity(certTpl); err != nil {
|
if err := opts.ModifyValidity(certTpl); err != nil {
|
||||||
return nil, errs.BadRequestErr(err, err.Error())
|
return nil, prov, errs.BadRequestErr(err, err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
// Use provisioner modifiers.
|
// Use provisioner modifiers.
|
||||||
for _, m := range mods {
|
for _, m := range mods {
|
||||||
if err := m.Modify(certTpl, opts); err != nil {
|
if err := m.Modify(certTpl, opts); err != nil {
|
||||||
return nil, errs.ForbiddenErr(err, "error creating ssh certificate")
|
return nil, prov, errs.ForbiddenErr(err, "error creating ssh certificate")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -253,32 +260,32 @@ func (a *Authority) SignSSH(_ context.Context, key ssh.PublicKey, opts provision
|
|||||||
switch certTpl.CertType {
|
switch certTpl.CertType {
|
||||||
case ssh.UserCert:
|
case ssh.UserCert:
|
||||||
if a.sshCAUserCertSignKey == nil {
|
if a.sshCAUserCertSignKey == nil {
|
||||||
return nil, errs.NotImplemented("authority.SignSSH: user certificate signing is not enabled")
|
return nil, prov, errs.NotImplemented("authority.SignSSH: user certificate signing is not enabled")
|
||||||
}
|
}
|
||||||
signer = a.sshCAUserCertSignKey
|
signer = a.sshCAUserCertSignKey
|
||||||
case ssh.HostCert:
|
case ssh.HostCert:
|
||||||
if a.sshCAHostCertSignKey == nil {
|
if a.sshCAHostCertSignKey == nil {
|
||||||
return nil, errs.NotImplemented("authority.SignSSH: host certificate signing is not enabled")
|
return nil, prov, errs.NotImplemented("authority.SignSSH: host certificate signing is not enabled")
|
||||||
}
|
}
|
||||||
signer = a.sshCAHostCertSignKey
|
signer = a.sshCAHostCertSignKey
|
||||||
default:
|
default:
|
||||||
return nil, errs.InternalServer("authority.SignSSH: unexpected ssh certificate type: %d", certTpl.CertType)
|
return nil, prov, errs.InternalServer("authority.SignSSH: unexpected ssh certificate type: %d", certTpl.CertType)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if authority is allowed to sign the certificate
|
// Check if authority is allowed to sign the certificate
|
||||||
if err := a.isAllowedToSignSSHCertificate(certTpl); err != nil {
|
if err := a.isAllowedToSignSSHCertificate(certTpl); err != nil {
|
||||||
var ee *errs.Error
|
var ee *errs.Error
|
||||||
if errors.As(err, &ee) {
|
if errors.As(err, &ee) {
|
||||||
return nil, ee
|
return nil, prov, ee
|
||||||
}
|
}
|
||||||
return nil, errs.InternalServerErr(err,
|
return nil, prov, errs.InternalServerErr(err,
|
||||||
errs.WithMessage("authority.SignSSH: error creating ssh certificate"),
|
errs.WithMessage("authority.SignSSH: error creating ssh certificate"),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Send certificate to webhooks for authorization
|
// Send certificate to webhooks for authorization
|
||||||
if err := callAuthorizingWebhooksSSH(webhookCtl, certificate, certTpl); err != nil {
|
if err := a.callAuthorizingWebhooksSSH(prov, webhookCtl, certificate, certTpl); err != nil {
|
||||||
return nil, errs.ApplyOptions(
|
return nil, prov, errs.ApplyOptions(
|
||||||
errs.ForbiddenErr(err, "authority.SignSSH: error signing certificate"),
|
errs.ForbiddenErr(err, "authority.SignSSH: error signing certificate"),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -286,21 +293,21 @@ func (a *Authority) SignSSH(_ context.Context, key ssh.PublicKey, opts provision
|
|||||||
// Sign certificate.
|
// Sign certificate.
|
||||||
cert, err := sshutil.CreateCertificate(certTpl, signer)
|
cert, err := sshutil.CreateCertificate(certTpl, signer)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errs.Wrap(http.StatusInternalServerError, err, "authority.SignSSH: error signing certificate")
|
return nil, prov, errs.Wrap(http.StatusInternalServerError, err, "authority.SignSSH: error signing certificate")
|
||||||
}
|
}
|
||||||
|
|
||||||
// User provisioners validators.
|
// User provisioners validators.
|
||||||
for _, v := range validators {
|
for _, v := range validators {
|
||||||
if err := v.Valid(cert, opts); err != nil {
|
if err := v.Valid(cert, opts); err != nil {
|
||||||
return nil, errs.ForbiddenErr(err, "error validating ssh certificate")
|
return nil, prov, errs.ForbiddenErr(err, "error validating ssh certificate")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = a.storeSSHCertificate(prov, cert); err != nil && !errors.Is(err, db.ErrNotImplemented) {
|
if err := a.storeSSHCertificate(prov, cert); err != nil && !errors.Is(err, db.ErrNotImplemented) {
|
||||||
return nil, errs.Wrap(http.StatusInternalServerError, err, "authority.SignSSH: error storing certificate in db")
|
return nil, prov, errs.Wrap(http.StatusInternalServerError, err, "authority.SignSSH: error storing certificate in db")
|
||||||
}
|
}
|
||||||
|
|
||||||
return cert, nil
|
return cert, prov, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// isAllowedToSignSSHCertificate checks if the Authority is allowed to sign the SSH certificate.
|
// isAllowedToSignSSHCertificate checks if the Authority is allowed to sign the SSH certificate.
|
||||||
@ -310,12 +317,18 @@ func (a *Authority) isAllowedToSignSSHCertificate(cert *ssh.Certificate) error {
|
|||||||
|
|
||||||
// RenewSSH creates a signed SSH certificate using the old SSH certificate as a template.
|
// RenewSSH creates a signed SSH certificate using the old SSH certificate as a template.
|
||||||
func (a *Authority) RenewSSH(ctx context.Context, oldCert *ssh.Certificate) (*ssh.Certificate, error) {
|
func (a *Authority) RenewSSH(ctx context.Context, oldCert *ssh.Certificate) (*ssh.Certificate, error) {
|
||||||
|
cert, prov, err := a.renewSSH(ctx, oldCert)
|
||||||
|
a.meter.SSHRenewed(prov, err)
|
||||||
|
return cert, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *Authority) renewSSH(ctx context.Context, oldCert *ssh.Certificate) (*ssh.Certificate, provisioner.Interface, error) {
|
||||||
if oldCert.ValidAfter == 0 || oldCert.ValidBefore == 0 {
|
if oldCert.ValidAfter == 0 || oldCert.ValidBefore == 0 {
|
||||||
return nil, errs.BadRequest("cannot renew a certificate without validity period")
|
return nil, nil, errs.BadRequest("cannot renew a certificate without validity period")
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := a.authorizeSSHCertificate(ctx, oldCert); err != nil {
|
if err := a.authorizeSSHCertificate(ctx, oldCert); err != nil {
|
||||||
return nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Attempt to extract the provisioner from the token.
|
// Attempt to extract the provisioner from the token.
|
||||||
@ -348,36 +361,41 @@ func (a *Authority) RenewSSH(ctx context.Context, oldCert *ssh.Certificate) (*ss
|
|||||||
switch certTpl.CertType {
|
switch certTpl.CertType {
|
||||||
case ssh.UserCert:
|
case ssh.UserCert:
|
||||||
if a.sshCAUserCertSignKey == nil {
|
if a.sshCAUserCertSignKey == nil {
|
||||||
return nil, errs.NotImplemented("renewSSH: user certificate signing is not enabled")
|
return nil, prov, errs.NotImplemented("renewSSH: user certificate signing is not enabled")
|
||||||
}
|
}
|
||||||
signer = a.sshCAUserCertSignKey
|
signer = a.sshCAUserCertSignKey
|
||||||
case ssh.HostCert:
|
case ssh.HostCert:
|
||||||
if a.sshCAHostCertSignKey == nil {
|
if a.sshCAHostCertSignKey == nil {
|
||||||
return nil, errs.NotImplemented("renewSSH: host certificate signing is not enabled")
|
return nil, prov, errs.NotImplemented("renewSSH: host certificate signing is not enabled")
|
||||||
}
|
}
|
||||||
signer = a.sshCAHostCertSignKey
|
signer = a.sshCAHostCertSignKey
|
||||||
default:
|
default:
|
||||||
return nil, errs.InternalServer("renewSSH: unexpected ssh certificate type: %d", certTpl.CertType)
|
return nil, prov, errs.InternalServer("renewSSH: unexpected ssh certificate type: %d", certTpl.CertType)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sign certificate.
|
// Sign certificate.
|
||||||
cert, err := sshutil.CreateCertificate(certTpl, signer)
|
cert, err := sshutil.CreateCertificate(certTpl, signer)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errs.Wrap(http.StatusInternalServerError, err, "signSSH: error signing certificate")
|
return nil, prov, errs.Wrap(http.StatusInternalServerError, err, "signSSH: error signing certificate")
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = a.storeRenewedSSHCertificate(prov, oldCert, cert); err != nil && !errors.Is(err, db.ErrNotImplemented) {
|
if err := a.storeRenewedSSHCertificate(prov, oldCert, cert); err != nil && !errors.Is(err, db.ErrNotImplemented) {
|
||||||
return nil, errs.Wrap(http.StatusInternalServerError, err, "renewSSH: error storing certificate in db")
|
return nil, prov, errs.Wrap(http.StatusInternalServerError, err, "renewSSH: error storing certificate in db")
|
||||||
}
|
}
|
||||||
|
|
||||||
return cert, nil
|
return cert, prov, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// RekeySSH creates a signed SSH certificate using the old SSH certificate as a template.
|
// RekeySSH creates a signed SSH certificate using the old SSH certificate as a template.
|
||||||
func (a *Authority) RekeySSH(ctx context.Context, oldCert *ssh.Certificate, pub ssh.PublicKey, signOpts ...provisioner.SignOption) (*ssh.Certificate, error) {
|
func (a *Authority) RekeySSH(ctx context.Context, oldCert *ssh.Certificate, pub ssh.PublicKey, signOpts ...provisioner.SignOption) (*ssh.Certificate, error) {
|
||||||
var validators []provisioner.SSHCertValidator
|
cert, prov, err := a.rekeySSH(ctx, oldCert, pub, signOpts...)
|
||||||
|
a.meter.SSHRekeyed(prov, err)
|
||||||
|
return cert, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *Authority) rekeySSH(ctx context.Context, oldCert *ssh.Certificate, pub ssh.PublicKey, signOpts ...provisioner.SignOption) (*ssh.Certificate, provisioner.Interface, error) {
|
||||||
var prov provisioner.Interface
|
var prov provisioner.Interface
|
||||||
|
var validators []provisioner.SSHCertValidator
|
||||||
for _, op := range signOpts {
|
for _, op := range signOpts {
|
||||||
switch o := op.(type) {
|
switch o := op.(type) {
|
||||||
// Capture current provisioner
|
// Capture current provisioner
|
||||||
@ -387,16 +405,16 @@ func (a *Authority) RekeySSH(ctx context.Context, oldCert *ssh.Certificate, pub
|
|||||||
case provisioner.SSHCertValidator:
|
case provisioner.SSHCertValidator:
|
||||||
validators = append(validators, o)
|
validators = append(validators, o)
|
||||||
default:
|
default:
|
||||||
return nil, errs.InternalServer("rekeySSH; invalid extra option type %T", o)
|
return nil, prov, errs.InternalServer("rekeySSH; invalid extra option type %T", o)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if oldCert.ValidAfter == 0 || oldCert.ValidBefore == 0 {
|
if oldCert.ValidAfter == 0 || oldCert.ValidBefore == 0 {
|
||||||
return nil, errs.BadRequest("cannot rekey a certificate without validity period")
|
return nil, prov, errs.BadRequest("cannot rekey a certificate without validity period")
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := a.authorizeSSHCertificate(ctx, oldCert); err != nil {
|
if err := a.authorizeSSHCertificate(ctx, oldCert); err != nil {
|
||||||
return nil, err
|
return nil, prov, err
|
||||||
}
|
}
|
||||||
|
|
||||||
backdate := a.config.AuthorityConfig.Backdate.Duration
|
backdate := a.config.AuthorityConfig.Backdate.Duration
|
||||||
@ -423,37 +441,37 @@ func (a *Authority) RekeySSH(ctx context.Context, oldCert *ssh.Certificate, pub
|
|||||||
switch cert.CertType {
|
switch cert.CertType {
|
||||||
case ssh.UserCert:
|
case ssh.UserCert:
|
||||||
if a.sshCAUserCertSignKey == nil {
|
if a.sshCAUserCertSignKey == nil {
|
||||||
return nil, errs.NotImplemented("rekeySSH; user certificate signing is not enabled")
|
return nil, prov, errs.NotImplemented("rekeySSH; user certificate signing is not enabled")
|
||||||
}
|
}
|
||||||
signer = a.sshCAUserCertSignKey
|
signer = a.sshCAUserCertSignKey
|
||||||
case ssh.HostCert:
|
case ssh.HostCert:
|
||||||
if a.sshCAHostCertSignKey == nil {
|
if a.sshCAHostCertSignKey == nil {
|
||||||
return nil, errs.NotImplemented("rekeySSH; host certificate signing is not enabled")
|
return nil, prov, errs.NotImplemented("rekeySSH; host certificate signing is not enabled")
|
||||||
}
|
}
|
||||||
signer = a.sshCAHostCertSignKey
|
signer = a.sshCAHostCertSignKey
|
||||||
default:
|
default:
|
||||||
return nil, errs.BadRequest("unexpected certificate type '%d'", cert.CertType)
|
return nil, prov, errs.BadRequest("unexpected certificate type '%d'", cert.CertType)
|
||||||
}
|
}
|
||||||
|
|
||||||
var err error
|
var err error
|
||||||
// Sign certificate.
|
// Sign certificate.
|
||||||
cert, err = sshutil.CreateCertificate(cert, signer)
|
cert, err = sshutil.CreateCertificate(cert, signer)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errs.Wrap(http.StatusInternalServerError, err, "signSSH: error signing certificate")
|
return nil, prov, errs.Wrap(http.StatusInternalServerError, err, "signSSH: error signing certificate")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Apply validators from provisioner.
|
// Apply validators from provisioner.
|
||||||
for _, v := range validators {
|
for _, v := range validators {
|
||||||
if err := v.Valid(cert, provisioner.SignSSHOptions{Backdate: backdate}); err != nil {
|
if err := v.Valid(cert, provisioner.SignSSHOptions{Backdate: backdate}); err != nil {
|
||||||
return nil, errs.ForbiddenErr(err, "error validating ssh certificate")
|
return nil, prov, errs.ForbiddenErr(err, "error validating ssh certificate")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = a.storeRenewedSSHCertificate(prov, oldCert, cert); err != nil && !errors.Is(err, db.ErrNotImplemented) {
|
if err := a.storeRenewedSSHCertificate(prov, oldCert, cert); err != nil && !errors.Is(err, db.ErrNotImplemented) {
|
||||||
return nil, errs.Wrap(http.StatusInternalServerError, err, "rekeySSH; error storing certificate in db")
|
return nil, prov, errs.Wrap(http.StatusInternalServerError, err, "rekeySSH; error storing certificate in db")
|
||||||
}
|
}
|
||||||
|
|
||||||
return cert, nil
|
return cert, prov, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *Authority) storeSSHCertificate(prov provisioner.Interface, cert *ssh.Certificate) error {
|
func (a *Authority) storeSSHCertificate(prov provisioner.Interface, cert *ssh.Certificate) error {
|
||||||
@ -653,28 +671,36 @@ func (a *Authority) getAddUserCommand(principal string) string {
|
|||||||
return strings.ReplaceAll(cmd, "<principal>", principal)
|
return strings.ReplaceAll(cmd, "<principal>", principal)
|
||||||
}
|
}
|
||||||
|
|
||||||
func callEnrichingWebhooksSSH(webhookCtl webhookController, cr sshutil.CertificateRequest) error {
|
func (a *Authority) callEnrichingWebhooksSSH(prov provisioner.Interface, webhookCtl webhookController, cr sshutil.CertificateRequest) (err error) {
|
||||||
if webhookCtl == nil {
|
if webhookCtl == nil {
|
||||||
return nil
|
return
|
||||||
}
|
}
|
||||||
whEnrichReq, err := webhook.NewRequestBody(
|
|
||||||
|
var whEnrichReq *webhook.RequestBody
|
||||||
|
if whEnrichReq, err = webhook.NewRequestBody(
|
||||||
webhook.WithSSHCertificateRequest(cr),
|
webhook.WithSSHCertificateRequest(cr),
|
||||||
)
|
); err == nil {
|
||||||
if err != nil {
|
err = webhookCtl.Enrich(whEnrichReq)
|
||||||
return err
|
|
||||||
|
a.meter.SSHWebhookEnriched(prov, err)
|
||||||
}
|
}
|
||||||
return webhookCtl.Enrich(whEnrichReq)
|
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func callAuthorizingWebhooksSSH(webhookCtl webhookController, cert *sshutil.Certificate, certTpl *ssh.Certificate) error {
|
func (a *Authority) callAuthorizingWebhooksSSH(prov provisioner.Interface, webhookCtl webhookController, cert *sshutil.Certificate, certTpl *ssh.Certificate) (err error) {
|
||||||
if webhookCtl == nil {
|
if webhookCtl == nil {
|
||||||
return nil
|
return
|
||||||
}
|
}
|
||||||
whAuthBody, err := webhook.NewRequestBody(
|
|
||||||
|
var whAuthBody *webhook.RequestBody
|
||||||
|
if whAuthBody, err = webhook.NewRequestBody(
|
||||||
webhook.WithSSHCertificate(cert, certTpl),
|
webhook.WithSSHCertificate(cert, certTpl),
|
||||||
)
|
); err == nil {
|
||||||
if err != nil {
|
err = webhookCtl.Authorize(whAuthBody)
|
||||||
return err
|
|
||||||
|
a.meter.SSHWebhookAuthorized(prov, err)
|
||||||
}
|
}
|
||||||
return webhookCtl.Authorize(whAuthBody)
|
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
173
authority/tls.go
173
authority/tls.go
@ -93,6 +93,12 @@ func withDefaultASN1DN(def *config.ASN1DN) provisioner.CertificateModifierFunc {
|
|||||||
|
|
||||||
// Sign creates a signed certificate from a certificate signing request.
|
// Sign creates a signed certificate from a certificate signing request.
|
||||||
func (a *Authority) Sign(csr *x509.CertificateRequest, signOpts provisioner.SignOptions, extraOpts ...provisioner.SignOption) ([]*x509.Certificate, error) {
|
func (a *Authority) Sign(csr *x509.CertificateRequest, signOpts provisioner.SignOptions, extraOpts ...provisioner.SignOption) ([]*x509.Certificate, error) {
|
||||||
|
chain, prov, err := a.signX509(csr, signOpts, extraOpts...)
|
||||||
|
a.meter.X509Signed(prov, err)
|
||||||
|
return chain, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *Authority) signX509(csr *x509.CertificateRequest, signOpts provisioner.SignOptions, extraOpts ...provisioner.SignOption) ([]*x509.Certificate, provisioner.Interface, error) {
|
||||||
var (
|
var (
|
||||||
certOptions []x509util.Option
|
certOptions []x509util.Option
|
||||||
certValidators []provisioner.CertificateValidator
|
certValidators []provisioner.CertificateValidator
|
||||||
@ -100,9 +106,9 @@ func (a *Authority) Sign(csr *x509.CertificateRequest, signOpts provisioner.Sign
|
|||||||
certEnforcers []provisioner.CertificateEnforcer
|
certEnforcers []provisioner.CertificateEnforcer
|
||||||
)
|
)
|
||||||
|
|
||||||
opts := []interface{}{errs.WithKeyVal("csr", csr), errs.WithKeyVal("signOptions", signOpts)}
|
opts := []any{errs.WithKeyVal("csr", csr), errs.WithKeyVal("signOptions", signOpts)}
|
||||||
if err := csr.CheckSignature(); err != nil {
|
if err := csr.CheckSignature(); err != nil {
|
||||||
return nil, errs.ApplyOptions(
|
return nil, nil, errs.ApplyOptions(
|
||||||
errs.BadRequestErr(err, "invalid certificate request"),
|
errs.BadRequestErr(err, "invalid certificate request"),
|
||||||
opts...,
|
opts...,
|
||||||
)
|
)
|
||||||
@ -111,10 +117,12 @@ func (a *Authority) Sign(csr *x509.CertificateRequest, signOpts provisioner.Sign
|
|||||||
// Set backdate with the configured value
|
// Set backdate with the configured value
|
||||||
signOpts.Backdate = a.config.AuthorityConfig.Backdate.Duration
|
signOpts.Backdate = a.config.AuthorityConfig.Backdate.Duration
|
||||||
|
|
||||||
var prov provisioner.Interface
|
var (
|
||||||
var pInfo *casapi.ProvisionerInfo
|
prov provisioner.Interface
|
||||||
var attData *provisioner.AttestationData
|
pInfo *casapi.ProvisionerInfo
|
||||||
var webhookCtl webhookController
|
attData *provisioner.AttestationData
|
||||||
|
webhookCtl webhookController
|
||||||
|
)
|
||||||
for _, op := range extraOpts {
|
for _, op := range extraOpts {
|
||||||
switch k := op.(type) {
|
switch k := op.(type) {
|
||||||
// Capture current provisioner
|
// Capture current provisioner
|
||||||
@ -132,7 +140,7 @@ func (a *Authority) Sign(csr *x509.CertificateRequest, signOpts provisioner.Sign
|
|||||||
// Validate the given certificate request.
|
// Validate the given certificate request.
|
||||||
case provisioner.CertificateRequestValidator:
|
case provisioner.CertificateRequestValidator:
|
||||||
if err := k.Valid(csr); err != nil {
|
if err := k.Valid(csr); err != nil {
|
||||||
return nil, errs.ApplyOptions(
|
return nil, prov, errs.ApplyOptions(
|
||||||
errs.ForbiddenErr(err, "error validating certificate request"),
|
errs.ForbiddenErr(err, "error validating certificate request"),
|
||||||
opts...,
|
opts...,
|
||||||
)
|
)
|
||||||
@ -159,45 +167,46 @@ func (a *Authority) Sign(csr *x509.CertificateRequest, signOpts provisioner.Sign
|
|||||||
webhookCtl = k
|
webhookCtl = k
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return nil, errs.InternalServer("authority.Sign; invalid extra option type %T", append([]interface{}{k}, opts...)...)
|
return nil, prov, errs.InternalServer("authority.Sign; invalid extra option type %T", append([]any{k}, opts...)...)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := callEnrichingWebhooksX509(webhookCtl, attData, csr); err != nil {
|
if err := a.callEnrichingWebhooksX509(prov, webhookCtl, attData, csr); err != nil {
|
||||||
return nil, errs.ApplyOptions(
|
return nil, prov, errs.ApplyOptions(
|
||||||
errs.ForbiddenErr(err, err.Error()),
|
errs.ForbiddenErr(err, err.Error()),
|
||||||
errs.WithKeyVal("csr", csr),
|
errs.WithKeyVal("csr", csr),
|
||||||
errs.WithKeyVal("signOptions", signOpts),
|
errs.WithKeyVal("signOptions", signOpts),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
cert, err := x509util.NewCertificate(csr, certOptions...)
|
crt, err := x509util.NewCertificate(csr, certOptions...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
var te *x509util.TemplateError
|
var te *x509util.TemplateError
|
||||||
if errors.As(err, &te) {
|
switch {
|
||||||
return nil, errs.ApplyOptions(
|
case errors.As(err, &te):
|
||||||
|
return nil, prov, errs.ApplyOptions(
|
||||||
errs.BadRequestErr(err, err.Error()),
|
errs.BadRequestErr(err, err.Error()),
|
||||||
errs.WithKeyVal("csr", csr),
|
errs.WithKeyVal("csr", csr),
|
||||||
errs.WithKeyVal("signOptions", signOpts),
|
errs.WithKeyVal("signOptions", signOpts),
|
||||||
)
|
)
|
||||||
}
|
case strings.HasPrefix(err.Error(), "error unmarshaling certificate"):
|
||||||
// explicitly check for unmarshaling errors, which are most probably caused by JSON template (syntax) errors
|
// explicitly check for unmarshaling errors, which are most probably caused by JSON template (syntax) errors
|
||||||
if strings.HasPrefix(err.Error(), "error unmarshaling certificate") {
|
return nil, prov, errs.InternalServerErr(templatingError(err),
|
||||||
return nil, errs.InternalServerErr(templatingError(err),
|
|
||||||
errs.WithKeyVal("csr", csr),
|
errs.WithKeyVal("csr", csr),
|
||||||
errs.WithKeyVal("signOptions", signOpts),
|
errs.WithKeyVal("signOptions", signOpts),
|
||||||
errs.WithMessage("error applying certificate template"),
|
errs.WithMessage("error applying certificate template"),
|
||||||
)
|
)
|
||||||
|
default:
|
||||||
|
return nil, prov, errs.Wrap(http.StatusInternalServerError, err, "authority.Sign", opts...)
|
||||||
}
|
}
|
||||||
return nil, errs.Wrap(http.StatusInternalServerError, err, "authority.Sign", opts...)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Certificate modifiers before validation
|
// Certificate modifiers before validation
|
||||||
leaf := cert.GetCertificate()
|
leaf := crt.GetCertificate()
|
||||||
|
|
||||||
// Set default subject
|
// Set default subject
|
||||||
if err := withDefaultASN1DN(a.config.AuthorityConfig.Template).Modify(leaf, signOpts); err != nil {
|
if err := withDefaultASN1DN(a.config.AuthorityConfig.Template).Modify(leaf, signOpts); err != nil {
|
||||||
return nil, errs.ApplyOptions(
|
return nil, prov, errs.ApplyOptions(
|
||||||
errs.ForbiddenErr(err, "error creating certificate"),
|
errs.ForbiddenErr(err, "error creating certificate"),
|
||||||
opts...,
|
opts...,
|
||||||
)
|
)
|
||||||
@ -205,7 +214,7 @@ func (a *Authority) Sign(csr *x509.CertificateRequest, signOpts provisioner.Sign
|
|||||||
|
|
||||||
for _, m := range certModifiers {
|
for _, m := range certModifiers {
|
||||||
if err := m.Modify(leaf, signOpts); err != nil {
|
if err := m.Modify(leaf, signOpts); err != nil {
|
||||||
return nil, errs.ApplyOptions(
|
return nil, prov, errs.ApplyOptions(
|
||||||
errs.ForbiddenErr(err, "error creating certificate"),
|
errs.ForbiddenErr(err, "error creating certificate"),
|
||||||
opts...,
|
opts...,
|
||||||
)
|
)
|
||||||
@ -215,7 +224,7 @@ func (a *Authority) Sign(csr *x509.CertificateRequest, signOpts provisioner.Sign
|
|||||||
// Certificate validation.
|
// Certificate validation.
|
||||||
for _, v := range certValidators {
|
for _, v := range certValidators {
|
||||||
if err := v.Valid(leaf, signOpts); err != nil {
|
if err := v.Valid(leaf, signOpts); err != nil {
|
||||||
return nil, errs.ApplyOptions(
|
return nil, prov, errs.ApplyOptions(
|
||||||
errs.ForbiddenErr(err, "error validating certificate"),
|
errs.ForbiddenErr(err, "error validating certificate"),
|
||||||
opts...,
|
opts...,
|
||||||
)
|
)
|
||||||
@ -224,8 +233,8 @@ func (a *Authority) Sign(csr *x509.CertificateRequest, signOpts provisioner.Sign
|
|||||||
|
|
||||||
// Certificate modifiers after validation
|
// Certificate modifiers after validation
|
||||||
for _, m := range certEnforcers {
|
for _, m := range certEnforcers {
|
||||||
if err := m.Enforce(leaf); err != nil {
|
if err = m.Enforce(leaf); err != nil {
|
||||||
return nil, errs.ApplyOptions(
|
return nil, prov, errs.ApplyOptions(
|
||||||
errs.ForbiddenErr(err, "error creating certificate"),
|
errs.ForbiddenErr(err, "error creating certificate"),
|
||||||
opts...,
|
opts...,
|
||||||
)
|
)
|
||||||
@ -234,8 +243,8 @@ func (a *Authority) Sign(csr *x509.CertificateRequest, signOpts provisioner.Sign
|
|||||||
|
|
||||||
// Process injected modifiers after validation
|
// Process injected modifiers after validation
|
||||||
for _, m := range a.x509Enforcers {
|
for _, m := range a.x509Enforcers {
|
||||||
if err := m.Enforce(leaf); err != nil {
|
if err = m.Enforce(leaf); err != nil {
|
||||||
return nil, errs.ApplyOptions(
|
return nil, prov, errs.ApplyOptions(
|
||||||
errs.ForbiddenErr(err, "error creating certificate"),
|
errs.ForbiddenErr(err, "error creating certificate"),
|
||||||
opts...,
|
opts...,
|
||||||
)
|
)
|
||||||
@ -243,12 +252,12 @@ func (a *Authority) Sign(csr *x509.CertificateRequest, signOpts provisioner.Sign
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Check if authority is allowed to sign the certificate
|
// Check if authority is allowed to sign the certificate
|
||||||
if err := a.isAllowedToSignX509Certificate(leaf); err != nil {
|
if err = a.isAllowedToSignX509Certificate(leaf); err != nil {
|
||||||
var ee *errs.Error
|
var ee *errs.Error
|
||||||
if errors.As(err, &ee) {
|
if errors.As(err, &ee) {
|
||||||
return nil, errs.ApplyOptions(ee, opts...)
|
return nil, prov, errs.ApplyOptions(ee, opts...)
|
||||||
}
|
}
|
||||||
return nil, errs.InternalServerErr(err,
|
return nil, prov, errs.InternalServerErr(err,
|
||||||
errs.WithKeyVal("csr", csr),
|
errs.WithKeyVal("csr", csr),
|
||||||
errs.WithKeyVal("signOptions", signOpts),
|
errs.WithKeyVal("signOptions", signOpts),
|
||||||
errs.WithMessage("error creating certificate"),
|
errs.WithMessage("error creating certificate"),
|
||||||
@ -256,8 +265,8 @@ func (a *Authority) Sign(csr *x509.CertificateRequest, signOpts provisioner.Sign
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Send certificate to webhooks for authorization
|
// Send certificate to webhooks for authorization
|
||||||
if err := callAuthorizingWebhooksX509(webhookCtl, cert, leaf, attData); err != nil {
|
if err := a.callAuthorizingWebhooksX509(prov, webhookCtl, crt, leaf, attData); err != nil {
|
||||||
return nil, errs.ApplyOptions(
|
return nil, prov, errs.ApplyOptions(
|
||||||
errs.ForbiddenErr(err, "error creating certificate"),
|
errs.ForbiddenErr(err, "error creating certificate"),
|
||||||
opts...,
|
opts...,
|
||||||
)
|
)
|
||||||
@ -265,6 +274,7 @@ func (a *Authority) Sign(csr *x509.CertificateRequest, signOpts provisioner.Sign
|
|||||||
|
|
||||||
// Sign certificate
|
// Sign certificate
|
||||||
lifetime := leaf.NotAfter.Sub(leaf.NotBefore.Add(signOpts.Backdate))
|
lifetime := leaf.NotAfter.Sub(leaf.NotBefore.Add(signOpts.Backdate))
|
||||||
|
|
||||||
resp, err := a.x509CAService.CreateCertificate(&casapi.CreateCertificateRequest{
|
resp, err := a.x509CAService.CreateCertificate(&casapi.CreateCertificateRequest{
|
||||||
Template: leaf,
|
Template: leaf,
|
||||||
CSR: csr,
|
CSR: csr,
|
||||||
@ -273,23 +283,22 @@ func (a *Authority) Sign(csr *x509.CertificateRequest, signOpts provisioner.Sign
|
|||||||
Provisioner: pInfo,
|
Provisioner: pInfo,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errs.Wrap(http.StatusInternalServerError, err, "authority.Sign; error creating certificate", opts...)
|
return nil, prov, errs.Wrap(http.StatusInternalServerError, err, "authority.Sign; error creating certificate", opts...)
|
||||||
}
|
}
|
||||||
|
|
||||||
fullchain := append([]*x509.Certificate{resp.Certificate}, resp.CertificateChain...)
|
chain := append([]*x509.Certificate{resp.Certificate}, resp.CertificateChain...)
|
||||||
|
|
||||||
// Wrap provisioner with extra information.
|
// Wrap provisioner with extra information, if not nil
|
||||||
prov = wrapProvisioner(prov, attData)
|
if prov != nil {
|
||||||
|
prov = wrapProvisioner(prov, attData)
|
||||||
|
}
|
||||||
|
|
||||||
// Store certificate in the db.
|
// Store certificate in the db.
|
||||||
if err = a.storeCertificate(prov, fullchain); err != nil {
|
if err := a.storeCertificate(prov, chain); err != nil && !errors.Is(err, db.ErrNotImplemented) {
|
||||||
if !errors.Is(err, db.ErrNotImplemented) {
|
return nil, prov, errs.Wrap(http.StatusInternalServerError, err, "authority.Sign; error storing certificate in db", opts...)
|
||||||
return nil, errs.Wrap(http.StatusInternalServerError, err,
|
|
||||||
"authority.Sign; error storing certificate in db", opts...)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return fullchain, nil
|
return chain, prov, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// isAllowedToSignX509Certificate checks if the Authority is allowed
|
// isAllowedToSignX509Certificate checks if the Authority is allowed
|
||||||
@ -337,14 +346,25 @@ func (a *Authority) Rekey(oldCert *x509.Certificate, pk crypto.PublicKey) ([]*x5
|
|||||||
// of rekey), and 'NotBefore/NotAfter' (the validity duration of the new
|
// of rekey), and 'NotBefore/NotAfter' (the validity duration of the new
|
||||||
// certificate should be equal to the old one, but starting 'now').
|
// certificate should be equal to the old one, but starting 'now').
|
||||||
func (a *Authority) RenewContext(ctx context.Context, oldCert *x509.Certificate, pk crypto.PublicKey) ([]*x509.Certificate, error) {
|
func (a *Authority) RenewContext(ctx context.Context, oldCert *x509.Certificate, pk crypto.PublicKey) ([]*x509.Certificate, error) {
|
||||||
|
chain, prov, err := a.renewContext(ctx, oldCert, pk)
|
||||||
|
if pk == nil {
|
||||||
|
a.meter.X509Renewed(prov, err)
|
||||||
|
} else {
|
||||||
|
a.meter.X509Rekeyed(prov, err)
|
||||||
|
}
|
||||||
|
return chain, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *Authority) renewContext(ctx context.Context, oldCert *x509.Certificate, pk crypto.PublicKey) ([]*x509.Certificate, provisioner.Interface, error) {
|
||||||
isRekey := (pk != nil)
|
isRekey := (pk != nil)
|
||||||
opts := []errs.Option{
|
opts := []errs.Option{
|
||||||
errs.WithKeyVal("serialNumber", oldCert.SerialNumber.String()),
|
errs.WithKeyVal("serialNumber", oldCert.SerialNumber.String()),
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check step provisioner extensions
|
// Check step provisioner extensions
|
||||||
if err := a.authorizeRenew(ctx, oldCert); err != nil {
|
prov, err := a.authorizeRenew(ctx, oldCert)
|
||||||
return nil, errs.StatusCodeError(http.StatusInternalServerError, err, opts...)
|
if err != nil {
|
||||||
|
return nil, prov, errs.StatusCodeError(http.StatusInternalServerError, err, opts...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Durations
|
// Durations
|
||||||
@ -414,15 +434,17 @@ func (a *Authority) RenewContext(ctx context.Context, oldCert *x509.Certificate,
|
|||||||
//
|
//
|
||||||
// TODO(hslatman,maraino): consider adding policies too and consider if
|
// TODO(hslatman,maraino): consider adding policies too and consider if
|
||||||
// RenewSSH should check policies.
|
// RenewSSH should check policies.
|
||||||
if err := a.constraintsEngine.ValidateCertificate(newCert); err != nil {
|
if err = a.constraintsEngine.ValidateCertificate(newCert); err != nil {
|
||||||
var ee *errs.Error
|
var ee *errs.Error
|
||||||
if errors.As(err, &ee) {
|
switch {
|
||||||
return nil, errs.StatusCodeError(ee.StatusCode(), err, opts...)
|
case errors.As(err, &ee):
|
||||||
|
return nil, prov, errs.StatusCodeError(ee.StatusCode(), err, opts...)
|
||||||
|
default:
|
||||||
|
return nil, prov, errs.InternalServerErr(err,
|
||||||
|
errs.WithKeyVal("serialNumber", oldCert.SerialNumber.String()),
|
||||||
|
errs.WithMessage("error renewing certificate"),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
return nil, errs.InternalServerErr(err,
|
|
||||||
errs.WithKeyVal("serialNumber", oldCert.SerialNumber.String()),
|
|
||||||
errs.WithMessage("error renewing certificate"),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// The token can optionally be in the context. If the CA is running in RA
|
// The token can optionally be in the context. If the CA is running in RA
|
||||||
@ -436,17 +458,16 @@ func (a *Authority) RenewContext(ctx context.Context, oldCert *x509.Certificate,
|
|||||||
Token: token,
|
Token: token,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errs.StatusCodeError(http.StatusInternalServerError, err, opts...)
|
return nil, prov, errs.StatusCodeError(http.StatusInternalServerError, err, opts...)
|
||||||
}
|
}
|
||||||
|
|
||||||
fullchain := append([]*x509.Certificate{resp.Certificate}, resp.CertificateChain...)
|
chain := append([]*x509.Certificate{resp.Certificate}, resp.CertificateChain...)
|
||||||
if err = a.storeRenewedCertificate(oldCert, fullchain); err != nil {
|
|
||||||
if !errors.Is(err, db.ErrNotImplemented) {
|
if err = a.storeRenewedCertificate(oldCert, chain); err != nil && !errors.Is(err, db.ErrNotImplemented) {
|
||||||
return nil, errs.StatusCodeError(http.StatusInternalServerError, err, opts...)
|
return nil, prov, errs.StatusCodeError(http.StatusInternalServerError, err, opts...)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return fullchain, nil
|
return chain, prov, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// storeCertificate allows to use an extension of the db.AuthDB interface that
|
// storeCertificate allows to use an extension of the db.AuthDB interface that
|
||||||
@ -952,42 +973,52 @@ func templatingError(err error) error {
|
|||||||
return errors.Wrap(cause, "error applying certificate template")
|
return errors.Wrap(cause, "error applying certificate template")
|
||||||
}
|
}
|
||||||
|
|
||||||
func callEnrichingWebhooksX509(webhookCtl webhookController, attData *provisioner.AttestationData, csr *x509.CertificateRequest) error {
|
func (a *Authority) callEnrichingWebhooksX509(prov provisioner.Interface, webhookCtl webhookController, attData *provisioner.AttestationData, csr *x509.CertificateRequest) (err error) {
|
||||||
if webhookCtl == nil {
|
if webhookCtl == nil {
|
||||||
return nil
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
var attested *webhook.AttestationData
|
var attested *webhook.AttestationData
|
||||||
if attData != nil {
|
if attData != nil {
|
||||||
attested = &webhook.AttestationData{
|
attested = &webhook.AttestationData{
|
||||||
PermanentIdentifier: attData.PermanentIdentifier,
|
PermanentIdentifier: attData.PermanentIdentifier,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
whEnrichReq, err := webhook.NewRequestBody(
|
|
||||||
|
var whEnrichReq *webhook.RequestBody
|
||||||
|
if whEnrichReq, err = webhook.NewRequestBody(
|
||||||
webhook.WithX509CertificateRequest(csr),
|
webhook.WithX509CertificateRequest(csr),
|
||||||
webhook.WithAttestationData(attested),
|
webhook.WithAttestationData(attested),
|
||||||
)
|
); err == nil {
|
||||||
if err != nil {
|
err = webhookCtl.Enrich(whEnrichReq)
|
||||||
return err
|
|
||||||
|
a.meter.X509WebhookEnriched(prov, err)
|
||||||
}
|
}
|
||||||
return webhookCtl.Enrich(whEnrichReq)
|
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func callAuthorizingWebhooksX509(webhookCtl webhookController, cert *x509util.Certificate, leaf *x509.Certificate, attData *provisioner.AttestationData) error {
|
func (a *Authority) callAuthorizingWebhooksX509(prov provisioner.Interface, webhookCtl webhookController, cert *x509util.Certificate, leaf *x509.Certificate, attData *provisioner.AttestationData) (err error) {
|
||||||
if webhookCtl == nil {
|
if webhookCtl == nil {
|
||||||
return nil
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
var attested *webhook.AttestationData
|
var attested *webhook.AttestationData
|
||||||
if attData != nil {
|
if attData != nil {
|
||||||
attested = &webhook.AttestationData{
|
attested = &webhook.AttestationData{
|
||||||
PermanentIdentifier: attData.PermanentIdentifier,
|
PermanentIdentifier: attData.PermanentIdentifier,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
whAuthBody, err := webhook.NewRequestBody(
|
|
||||||
|
var whAuthBody *webhook.RequestBody
|
||||||
|
if whAuthBody, err = webhook.NewRequestBody(
|
||||||
webhook.WithX509Certificate(cert, leaf),
|
webhook.WithX509Certificate(cert, leaf),
|
||||||
webhook.WithAttestationData(attested),
|
webhook.WithAttestationData(attested),
|
||||||
)
|
); err == nil {
|
||||||
if err != nil {
|
err = webhookCtl.Authorize(whAuthBody)
|
||||||
return err
|
|
||||||
|
a.meter.X509WebhookAuthorized(prov, err)
|
||||||
}
|
}
|
||||||
return webhookCtl.Authorize(whAuthBody)
|
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
31
ca/ca.go
31
ca/ca.go
@ -27,6 +27,7 @@ import (
|
|||||||
adminAPI "github.com/smallstep/certificates/authority/admin/api"
|
adminAPI "github.com/smallstep/certificates/authority/admin/api"
|
||||||
"github.com/smallstep/certificates/authority/config"
|
"github.com/smallstep/certificates/authority/config"
|
||||||
"github.com/smallstep/certificates/db"
|
"github.com/smallstep/certificates/db"
|
||||||
|
"github.com/smallstep/certificates/internal/metrix"
|
||||||
"github.com/smallstep/certificates/logging"
|
"github.com/smallstep/certificates/logging"
|
||||||
"github.com/smallstep/certificates/monitoring"
|
"github.com/smallstep/certificates/monitoring"
|
||||||
"github.com/smallstep/certificates/scep"
|
"github.com/smallstep/certificates/scep"
|
||||||
@ -125,6 +126,7 @@ type CA struct {
|
|||||||
config *config.Config
|
config *config.Config
|
||||||
srv *server.Server
|
srv *server.Server
|
||||||
insecureSrv *server.Server
|
insecureSrv *server.Server
|
||||||
|
metricsSrv *server.Server
|
||||||
opts *options
|
opts *options
|
||||||
renewer *TLSRenewer
|
renewer *TLSRenewer
|
||||||
compactStop chan struct{}
|
compactStop chan struct{}
|
||||||
@ -163,6 +165,13 @@ func (ca *CA) Init(cfg *config.Config) (*CA, error) {
|
|||||||
opts = append(opts, authority.WithQuietInit())
|
opts = append(opts, authority.WithQuietInit())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var meter *metrix.Meter
|
||||||
|
if ca.config.MetricsAddress != "" {
|
||||||
|
meter = metrix.New()
|
||||||
|
|
||||||
|
opts = append(opts, authority.WithMeter(meter))
|
||||||
|
}
|
||||||
|
|
||||||
webhookTransport := http.DefaultTransport.(*http.Transport).Clone()
|
webhookTransport := http.DefaultTransport.(*http.Transport).Clone()
|
||||||
opts = append(opts, authority.WithWebhookClient(&http.Client{Transport: webhookTransport}))
|
opts = append(opts, authority.WithWebhookClient(&http.Client{Transport: webhookTransport}))
|
||||||
|
|
||||||
@ -318,6 +327,13 @@ func (ca *CA) Init(cfg *config.Config) (*CA, error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if meter != nil {
|
||||||
|
ca.metricsSrv = server.New(ca.config.MetricsAddress, meter, nil)
|
||||||
|
ca.metricsSrv.BaseContext = func(net.Listener) context.Context {
|
||||||
|
return baseContext
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return ca, nil
|
return ca, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -404,6 +420,14 @@ func (ca *CA) Run() error {
|
|||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ca.metricsSrv != nil {
|
||||||
|
wg.Add(1)
|
||||||
|
go func() {
|
||||||
|
defer wg.Done()
|
||||||
|
errs <- ca.metricsSrv.ListenAndServe()
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
wg.Add(1)
|
wg.Add(1)
|
||||||
go func() {
|
go func() {
|
||||||
defer wg.Done()
|
defer wg.Done()
|
||||||
@ -480,6 +504,13 @@ func (ca *CA) Reload() error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ca.metricsSrv != nil {
|
||||||
|
if err = ca.metricsSrv.Reload(newCA.metricsSrv); err != nil {
|
||||||
|
logContinue("Reload failed because metrics server could not be replaced.")
|
||||||
|
return errors.Wrap(err, "error reloading metrics server")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if err = ca.srv.Reload(newCA.srv); err != nil {
|
if err = ca.srv.Reload(newCA.srv); err != nil {
|
||||||
logContinue("Reload failed because server could not be replaced.")
|
logContinue("Reload failed because server could not be replaced.")
|
||||||
return errors.Wrap(err, "error reloading server")
|
return errors.Wrap(err, "error reloading server")
|
||||||
|
@ -251,7 +251,8 @@ To get a linked authority token:
|
|||||||
ca.WithSSHUserPassword(sshUserPassword),
|
ca.WithSSHUserPassword(sshUserPassword),
|
||||||
ca.WithIssuerPassword(issuerPassword),
|
ca.WithIssuerPassword(issuerPassword),
|
||||||
ca.WithLinkedCAToken(token),
|
ca.WithLinkedCAToken(token),
|
||||||
ca.WithQuiet(quiet))
|
ca.WithQuiet(quiet),
|
||||||
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fatal(err)
|
fatal(err)
|
||||||
}
|
}
|
||||||
|
33
go.mod
33
go.mod
@ -21,6 +21,7 @@ require (
|
|||||||
github.com/hashicorp/vault/api/auth/kubernetes v0.5.0
|
github.com/hashicorp/vault/api/auth/kubernetes v0.5.0
|
||||||
github.com/newrelic/go-agent/v3 v3.29.0
|
github.com/newrelic/go-agent/v3 v3.29.0
|
||||||
github.com/pkg/errors v0.9.1
|
github.com/pkg/errors v0.9.1
|
||||||
|
github.com/prometheus/client_golang v1.15.1
|
||||||
github.com/rs/xid v1.5.0
|
github.com/rs/xid v1.5.0
|
||||||
github.com/sirupsen/logrus v1.9.3
|
github.com/sirupsen/logrus v1.9.3
|
||||||
github.com/slackhq/nebula v1.6.1
|
github.com/slackhq/nebula v1.6.1
|
||||||
@ -32,12 +33,12 @@ require (
|
|||||||
github.com/stretchr/testify v1.8.4
|
github.com/stretchr/testify v1.8.4
|
||||||
github.com/urfave/cli v1.22.14
|
github.com/urfave/cli v1.22.14
|
||||||
go.step.sm/cli-utils v0.8.0
|
go.step.sm/cli-utils v0.8.0
|
||||||
go.step.sm/crypto v0.41.0
|
go.step.sm/crypto v0.42.0
|
||||||
go.step.sm/linkedca v0.20.1
|
go.step.sm/linkedca v0.20.1
|
||||||
golang.org/x/crypto v0.18.0
|
golang.org/x/crypto v0.18.0
|
||||||
golang.org/x/exp v0.0.0-20240103183307-be819d1f06fc
|
golang.org/x/exp v0.0.0-20240103183307-be819d1f06fc
|
||||||
golang.org/x/net v0.20.0
|
golang.org/x/net v0.20.0
|
||||||
google.golang.org/api v0.156.0
|
google.golang.org/api v0.157.0
|
||||||
google.golang.org/grpc v1.60.1
|
google.golang.org/grpc v1.60.1
|
||||||
google.golang.org/protobuf v1.32.0
|
google.golang.org/protobuf v1.32.0
|
||||||
)
|
)
|
||||||
@ -59,7 +60,21 @@ require (
|
|||||||
github.com/Masterminds/goutils v1.1.1 // indirect
|
github.com/Masterminds/goutils v1.1.1 // indirect
|
||||||
github.com/Masterminds/semver/v3 v3.2.0 // indirect
|
github.com/Masterminds/semver/v3 v3.2.0 // indirect
|
||||||
github.com/ThalesIgnite/crypto11 v1.2.5 // indirect
|
github.com/ThalesIgnite/crypto11 v1.2.5 // indirect
|
||||||
github.com/aws/aws-sdk-go v1.49.17 // indirect
|
github.com/aws/aws-sdk-go-v2 v1.24.1 // indirect
|
||||||
|
github.com/aws/aws-sdk-go-v2/config v1.26.5 // indirect
|
||||||
|
github.com/aws/aws-sdk-go-v2/credentials v1.16.16 // indirect
|
||||||
|
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.14.11 // indirect
|
||||||
|
github.com/aws/aws-sdk-go-v2/internal/configsources v1.2.10 // indirect
|
||||||
|
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.5.10 // indirect
|
||||||
|
github.com/aws/aws-sdk-go-v2/internal/ini v1.7.2 // indirect
|
||||||
|
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.10.4 // indirect
|
||||||
|
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.10.10 // indirect
|
||||||
|
github.com/aws/aws-sdk-go-v2/service/kms v1.27.9 // indirect
|
||||||
|
github.com/aws/aws-sdk-go-v2/service/sso v1.18.7 // indirect
|
||||||
|
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.21.7 // indirect
|
||||||
|
github.com/aws/aws-sdk-go-v2/service/sts v1.26.7 // indirect
|
||||||
|
github.com/aws/smithy-go v1.19.0 // indirect
|
||||||
|
github.com/beorn7/perks v1.0.1 // indirect
|
||||||
github.com/cenkalti/backoff/v3 v3.0.0 // indirect
|
github.com/cenkalti/backoff/v3 v3.0.0 // indirect
|
||||||
github.com/cespare/xxhash v1.1.0 // indirect
|
github.com/cespare/xxhash v1.1.0 // indirect
|
||||||
github.com/cespare/xxhash/v2 v2.2.0 // indirect
|
github.com/cespare/xxhash/v2 v2.2.0 // indirect
|
||||||
@ -108,12 +123,12 @@ require (
|
|||||||
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a // indirect
|
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a // indirect
|
||||||
github.com/jackc/pgtype v1.14.0 // indirect
|
github.com/jackc/pgtype v1.14.0 // indirect
|
||||||
github.com/jackc/pgx/v4 v4.18.0 // indirect
|
github.com/jackc/pgx/v4 v4.18.0 // indirect
|
||||||
github.com/jmespath/go-jmespath v0.4.0 // indirect
|
|
||||||
github.com/klauspost/compress v1.16.3 // indirect
|
github.com/klauspost/compress v1.16.3 // indirect
|
||||||
github.com/kylelemons/godebug v1.1.0 // indirect
|
github.com/kylelemons/godebug v1.1.0 // indirect
|
||||||
github.com/manifoldco/promptui v0.9.0 // indirect
|
github.com/manifoldco/promptui v0.9.0 // indirect
|
||||||
github.com/mattn/go-colorable v0.1.13 // indirect
|
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||||
github.com/mattn/go-isatty v0.0.16 // indirect
|
github.com/mattn/go-isatty v0.0.16 // indirect
|
||||||
|
github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect
|
||||||
github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d // indirect
|
github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d // indirect
|
||||||
github.com/miekg/pkcs11 v1.1.1 // indirect
|
github.com/miekg/pkcs11 v1.1.1 // indirect
|
||||||
github.com/mitchellh/copystructure v1.2.0 // indirect
|
github.com/mitchellh/copystructure v1.2.0 // indirect
|
||||||
@ -123,6 +138,9 @@ require (
|
|||||||
github.com/peterbourgon/diskv/v3 v3.0.1 // indirect
|
github.com/peterbourgon/diskv/v3 v3.0.1 // indirect
|
||||||
github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 // indirect
|
github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 // indirect
|
||||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||||
|
github.com/prometheus/client_model v0.4.0 // indirect
|
||||||
|
github.com/prometheus/common v0.42.0 // indirect
|
||||||
|
github.com/prometheus/procfs v0.9.0 // indirect
|
||||||
github.com/russross/blackfriday/v2 v2.1.0 // indirect
|
github.com/russross/blackfriday/v2 v2.1.0 // indirect
|
||||||
github.com/ryanuber/go-glob v1.0.0 // indirect
|
github.com/ryanuber/go-glob v1.0.0 // indirect
|
||||||
github.com/schollz/jsonstore v1.1.0 // indirect
|
github.com/schollz/jsonstore v1.1.0 // indirect
|
||||||
@ -144,10 +162,9 @@ require (
|
|||||||
golang.org/x/text v0.14.0 // indirect
|
golang.org/x/text v0.14.0 // indirect
|
||||||
golang.org/x/time v0.5.0 // indirect
|
golang.org/x/time v0.5.0 // indirect
|
||||||
google.golang.org/appengine v1.6.8 // indirect
|
google.golang.org/appengine v1.6.8 // indirect
|
||||||
google.golang.org/genproto v0.0.0-20231212172506-995d672761c0 // indirect
|
google.golang.org/genproto v0.0.0-20240102182953-50ed04b92917 // indirect
|
||||||
google.golang.org/genproto/googleapis/api v0.0.0-20231212172506-995d672761c0 // indirect
|
google.golang.org/genproto/googleapis/api v0.0.0-20240102182953-50ed04b92917 // indirect
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240102182953-50ed04b92917 // indirect
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20240116215550-a9fa1716bcac // indirect
|
||||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect
|
|
||||||
gopkg.in/square/go-jose.v2 v2.6.0 // indirect
|
gopkg.in/square/go-jose.v2 v2.6.0 // indirect
|
||||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||||
)
|
)
|
||||||
|
71
go.sum
71
go.sum
@ -101,8 +101,36 @@ github.com/ThalesIgnite/crypto11 v1.2.5/go.mod h1:ILDKtnCKiQ7zRoNxcp36Y1ZR8LBPmR
|
|||||||
github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
|
github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
|
||||||
github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
|
github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
|
||||||
github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
|
github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
|
||||||
github.com/aws/aws-sdk-go v1.49.17 h1:Cc+7LgPjKeJkF2SdNo1IkpQ5Dfl9HCZEVw9OP3CPuEI=
|
github.com/aws/aws-sdk-go-v2 v1.24.1 h1:xAojnj+ktS95YZlDf0zxWBkbFtymPeDP+rvUQIH3uAU=
|
||||||
github.com/aws/aws-sdk-go v1.49.17/go.mod h1:LF8svs817+Nz+DmiMQKTO3ubZ/6IaTpq3TjupRn3Eqk=
|
github.com/aws/aws-sdk-go-v2 v1.24.1/go.mod h1:LNh45Br1YAkEKaAqvmE1m8FUx6a5b/V0oAKV7of29b4=
|
||||||
|
github.com/aws/aws-sdk-go-v2/config v1.26.5 h1:lodGSevz7d+kkFJodfauThRxK9mdJbyutUxGq1NNhvw=
|
||||||
|
github.com/aws/aws-sdk-go-v2/config v1.26.5/go.mod h1:DxHrz6diQJOc9EwDslVRh84VjjrE17g+pVZXUeSxaDU=
|
||||||
|
github.com/aws/aws-sdk-go-v2/credentials v1.16.16 h1:8q6Rliyv0aUFAVtzaldUEcS+T5gbadPbWdV1WcAddK8=
|
||||||
|
github.com/aws/aws-sdk-go-v2/credentials v1.16.16/go.mod h1:UHVZrdUsv63hPXFo1H7c5fEneoVo9UXiz36QG1GEPi0=
|
||||||
|
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.14.11 h1:c5I5iH+DZcH3xOIMlz3/tCKJDaHFwYEmxvlh2fAcFo8=
|
||||||
|
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.14.11/go.mod h1:cRrYDYAMUohBJUtUnOhydaMHtiK/1NZ0Otc9lIb6O0Y=
|
||||||
|
github.com/aws/aws-sdk-go-v2/internal/configsources v1.2.10 h1:vF+Zgd9s+H4vOXd5BMaPWykta2a6Ih0AKLq/X6NYKn4=
|
||||||
|
github.com/aws/aws-sdk-go-v2/internal/configsources v1.2.10/go.mod h1:6BkRjejp/GR4411UGqkX8+wFMbFbqsUIimfK4XjOKR4=
|
||||||
|
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.5.10 h1:nYPe006ktcqUji8S2mqXf9c/7NdiKriOwMvWQHgYztw=
|
||||||
|
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.5.10/go.mod h1:6UV4SZkVvmODfXKql4LCbaZUpF7HO2BX38FgBf9ZOLw=
|
||||||
|
github.com/aws/aws-sdk-go-v2/internal/ini v1.7.2 h1:GrSw8s0Gs/5zZ0SX+gX4zQjRnRsMJDJ2sLur1gRBhEM=
|
||||||
|
github.com/aws/aws-sdk-go-v2/internal/ini v1.7.2/go.mod h1:6fQQgfuGmw8Al/3M2IgIllycxV7ZW7WCdVSqfBeUiCY=
|
||||||
|
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.10.4 h1:/b31bi3YVNlkzkBrm9LfpaKoaYZUxIAj4sHfOTmLfqw=
|
||||||
|
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.10.4/go.mod h1:2aGXHFmbInwgP9ZfpmdIfOELL79zhdNYNmReK8qDfdQ=
|
||||||
|
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.10.10 h1:DBYTXwIGQSGs9w4jKm60F5dmCQ3EEruxdc0MFh+3EY4=
|
||||||
|
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.10.10/go.mod h1:wohMUQiFdzo0NtxbBg0mSRGZ4vL3n0dKjLTINdcIino=
|
||||||
|
github.com/aws/aws-sdk-go-v2/service/kms v1.27.9 h1:W9PbZAZAEcelhhjb7KuwUtf+Lbc+i7ByYJRuWLlnxyQ=
|
||||||
|
github.com/aws/aws-sdk-go-v2/service/kms v1.27.9/go.mod h1:2tFmR7fQnOdQlM2ZCEPpFnBIQD1U8wmXmduBgZbOag0=
|
||||||
|
github.com/aws/aws-sdk-go-v2/service/sso v1.18.7 h1:eajuO3nykDPdYicLlP3AGgOyVN3MOlFmZv7WGTuJPow=
|
||||||
|
github.com/aws/aws-sdk-go-v2/service/sso v1.18.7/go.mod h1:+mJNDdF+qiUlNKNC3fxn74WWNN+sOiGOEImje+3ScPM=
|
||||||
|
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.21.7 h1:QPMJf+Jw8E1l7zqhZmMlFw6w1NmfkfiSK8mS4zOx3BA=
|
||||||
|
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.21.7/go.mod h1:ykf3COxYI0UJmxcfcxcVuz7b6uADi1FkiUz6Eb7AgM8=
|
||||||
|
github.com/aws/aws-sdk-go-v2/service/sts v1.26.7 h1:NzO4Vrau795RkUdSHKEwiR01FaGzGOH1EETJ+5QHnm0=
|
||||||
|
github.com/aws/aws-sdk-go-v2/service/sts v1.26.7/go.mod h1:6h2YuIoxaMSCFf5fi1EgZAwdfkGMgDY+DVfa61uLe4U=
|
||||||
|
github.com/aws/smithy-go v1.19.0 h1:KWFKQV80DpP3vJrrA9sVAHQ5gc2z8i4EzrLhLlWXcBM=
|
||||||
|
github.com/aws/smithy-go v1.19.0/go.mod h1:NukqUGpCZIILqqiV0NIjeFh24kd/FAa4beRb6nbIUPE=
|
||||||
|
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
||||||
|
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
|
||||||
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
|
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
|
||||||
github.com/cenkalti/backoff/v3 v3.0.0 h1:ske+9nBpD9qZsTBoF41nW5L+AIuFBKMeze18XQ3eG1c=
|
github.com/cenkalti/backoff/v3 v3.0.0 h1:ske+9nBpD9qZsTBoF41nW5L+AIuFBKMeze18XQ3eG1c=
|
||||||
github.com/cenkalti/backoff/v3 v3.0.0/go.mod h1:cIeZDE3IrqwwJl6VUwCN6trj1oXrTS4rc0ij+ULvLYs=
|
github.com/cenkalti/backoff/v3 v3.0.0/go.mod h1:cIeZDE3IrqwwJl6VUwCN6trj1oXrTS4rc0ij+ULvLYs=
|
||||||
@ -417,10 +445,6 @@ github.com/jackc/puddle v0.0.0-20190413234325-e4ced69a3a2b/go.mod h1:m4B5Dj62Y0f
|
|||||||
github.com/jackc/puddle v0.0.0-20190608224051-11cab39313c9/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
|
github.com/jackc/puddle v0.0.0-20190608224051-11cab39313c9/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
|
||||||
github.com/jackc/puddle v1.1.3/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
|
github.com/jackc/puddle v1.1.3/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
|
||||||
github.com/jackc/puddle v1.3.0/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
|
github.com/jackc/puddle v1.3.0/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
|
||||||
github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg=
|
|
||||||
github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo=
|
|
||||||
github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8=
|
|
||||||
github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U=
|
|
||||||
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
|
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
|
||||||
github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
|
github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
|
||||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||||
@ -431,7 +455,6 @@ github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxv
|
|||||||
github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||||
github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
||||||
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
|
||||||
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||||
github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw=
|
github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw=
|
||||||
@ -461,6 +484,8 @@ github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcME
|
|||||||
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
||||||
github.com/mattn/go-isatty v0.0.16 h1:bq3VjFmv/sOjHtdEhmkEV4x1AJtvUvOJ2PFAZ5+peKQ=
|
github.com/mattn/go-isatty v0.0.16 h1:bq3VjFmv/sOjHtdEhmkEV4x1AJtvUvOJ2PFAZ5+peKQ=
|
||||||
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
||||||
|
github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo=
|
||||||
|
github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4=
|
||||||
github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d h1:5PJl274Y63IEHC+7izoQE9x6ikvDFZS2mDVS3drnohI=
|
github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d h1:5PJl274Y63IEHC+7izoQE9x6ikvDFZS2mDVS3drnohI=
|
||||||
github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE=
|
github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE=
|
||||||
github.com/miekg/pkcs11 v1.0.3-0.20190429190417-a667d056470f/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WTFxgs=
|
github.com/miekg/pkcs11 v1.0.3-0.20190429190417-a667d056470f/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WTFxgs=
|
||||||
@ -494,7 +519,15 @@ github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE
|
|||||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=
|
github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=
|
||||||
|
github.com/prometheus/client_golang v1.15.1 h1:8tXpTmJbyH5lydzFPoxSIJ0J46jdh3tylbvM1xCv0LI=
|
||||||
|
github.com/prometheus/client_golang v1.15.1/go.mod h1:e9yaBhRPU2pPNsZwE+JdQl0KEt1N9XgF6zxWmaC0xOk=
|
||||||
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||||
|
github.com/prometheus/client_model v0.4.0 h1:5lQXD3cAg1OXBf4Wq03gTrXHeaV0TQvGfUooCfx1yqY=
|
||||||
|
github.com/prometheus/client_model v0.4.0/go.mod h1:oMQmHW1/JoDwqLtg57MGgP/Fb1CJEYF2imWWhWtMkYU=
|
||||||
|
github.com/prometheus/common v0.42.0 h1:EKsfXEYo4JpWMHH5cg+KOUWeuJSov1Id8zGR8eeI1YM=
|
||||||
|
github.com/prometheus/common v0.42.0/go.mod h1:xBwqVerjNdUDjgODMpudtOMwlOwf2SaTr1yjz4b7Zbc=
|
||||||
|
github.com/prometheus/procfs v0.9.0 h1:wzCHvIvM5SxWqYvwgVL7yJY8Lz3PKn49KQtpgMYJfhI=
|
||||||
|
github.com/prometheus/procfs v0.9.0/go.mod h1:+pB4zwohETzFnmlpe6yd2lSc+0/46IYZRB/chUwxUZY=
|
||||||
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
|
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
|
||||||
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||||
github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
|
github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
|
||||||
@ -595,14 +628,14 @@ go.opentelemetry.io/otel v1.21.0 h1:hzLeKBZEL7Okw2mGzZ0cc4k/A7Fta0uoPgaJCr8fsFc=
|
|||||||
go.opentelemetry.io/otel v1.21.0/go.mod h1:QZzNPQPm1zLX4gZK4cMi+71eaorMSGT3A4znnUvNNEo=
|
go.opentelemetry.io/otel v1.21.0/go.mod h1:QZzNPQPm1zLX4gZK4cMi+71eaorMSGT3A4znnUvNNEo=
|
||||||
go.opentelemetry.io/otel/metric v1.21.0 h1:tlYWfeo+Bocx5kLEloTjbcDwBuELRrIFxwdQ36PlJu4=
|
go.opentelemetry.io/otel/metric v1.21.0 h1:tlYWfeo+Bocx5kLEloTjbcDwBuELRrIFxwdQ36PlJu4=
|
||||||
go.opentelemetry.io/otel/metric v1.21.0/go.mod h1:o1p3CA8nNHW8j5yuQLdc1eeqEaPfzug24uvsyIEJRWM=
|
go.opentelemetry.io/otel/metric v1.21.0/go.mod h1:o1p3CA8nNHW8j5yuQLdc1eeqEaPfzug24uvsyIEJRWM=
|
||||||
go.opentelemetry.io/otel/sdk v1.19.0 h1:6USY6zH+L8uMH8L3t1enZPR3WFEmSTADlqldyHtJi3o=
|
go.opentelemetry.io/otel/sdk v1.21.0 h1:FTt8qirL1EysG6sTQRZ5TokkU8d0ugCj8htOgThZXQ8=
|
||||||
go.opentelemetry.io/otel/trace v1.21.0 h1:WD9i5gzvoUPuXIXH24ZNBudiarZDKuekPqi/E8fpfLc=
|
go.opentelemetry.io/otel/trace v1.21.0 h1:WD9i5gzvoUPuXIXH24ZNBudiarZDKuekPqi/E8fpfLc=
|
||||||
go.opentelemetry.io/otel/trace v1.21.0/go.mod h1:LGbsEB0f9LGjN+OZaQQ26sohbOmiMR+BaslueVtS/qQ=
|
go.opentelemetry.io/otel/trace v1.21.0/go.mod h1:LGbsEB0f9LGjN+OZaQQ26sohbOmiMR+BaslueVtS/qQ=
|
||||||
go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=
|
go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=
|
||||||
go.step.sm/cli-utils v0.8.0 h1:b/Tc1/m3YuQq+u3ghTFP7Dz5zUekZj6GUmd5pCvkEXQ=
|
go.step.sm/cli-utils v0.8.0 h1:b/Tc1/m3YuQq+u3ghTFP7Dz5zUekZj6GUmd5pCvkEXQ=
|
||||||
go.step.sm/cli-utils v0.8.0/go.mod h1:S77aISrC0pKuflqiDfxxJlUbiXcAanyJ4POOnzFSxD4=
|
go.step.sm/cli-utils v0.8.0/go.mod h1:S77aISrC0pKuflqiDfxxJlUbiXcAanyJ4POOnzFSxD4=
|
||||||
go.step.sm/crypto v0.41.0 h1:cv1zSLsAUKurAmoG559fmtMTCaK0CrbLgSI1pmI/ITc=
|
go.step.sm/crypto v0.42.0 h1:1yPpg+v2c+fqKTLb5mTl45xdJ4gh1MXF0/X3dar71aU=
|
||||||
go.step.sm/crypto v0.41.0/go.mod h1:BBkqzupJdsSZ8LrPNyfqN81DMtahOenTk66tVgPmDvI=
|
go.step.sm/crypto v0.42.0/go.mod h1:PHgVNnxqQnhOKT6yx/0faP82VCeC3g/nJRlBMIQ8G64=
|
||||||
go.step.sm/linkedca v0.20.1 h1:bHDn1+UG1NgRrERkWbbCiAIvv4lD5NOFaswPDTyO5vU=
|
go.step.sm/linkedca v0.20.1 h1:bHDn1+UG1NgRrERkWbbCiAIvv4lD5NOFaswPDTyO5vU=
|
||||||
go.step.sm/linkedca v0.20.1/go.mod h1:Vaq4+Umtjh7DLFI1KuIxeo598vfBzgSYZUjgVJ7Syxw=
|
go.step.sm/linkedca v0.20.1/go.mod h1:Vaq4+Umtjh7DLFI1KuIxeo598vfBzgSYZUjgVJ7Syxw=
|
||||||
go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
|
go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
|
||||||
@ -970,8 +1003,8 @@ google.golang.org/api v0.75.0/go.mod h1:pU9QmyHLnzlpar1Mjt4IbapUCy8J+6HD6GeELN69
|
|||||||
google.golang.org/api v0.78.0/go.mod h1:1Sg78yoMLOhlQTeF+ARBoytAcH1NNyyl390YMy6rKmw=
|
google.golang.org/api v0.78.0/go.mod h1:1Sg78yoMLOhlQTeF+ARBoytAcH1NNyyl390YMy6rKmw=
|
||||||
google.golang.org/api v0.80.0/go.mod h1:xY3nI94gbvBrE0J6NHXhxOmW97HG7Khjkku6AFB3Hyg=
|
google.golang.org/api v0.80.0/go.mod h1:xY3nI94gbvBrE0J6NHXhxOmW97HG7Khjkku6AFB3Hyg=
|
||||||
google.golang.org/api v0.84.0/go.mod h1:NTsGnUFJMYROtiquksZHBWtHfeMC7iYthki7Eq3pa8o=
|
google.golang.org/api v0.84.0/go.mod h1:NTsGnUFJMYROtiquksZHBWtHfeMC7iYthki7Eq3pa8o=
|
||||||
google.golang.org/api v0.156.0 h1:yloYcGbBtVYjLKQe4enCunxvwn3s2w/XPrrhVf6MsvQ=
|
google.golang.org/api v0.157.0 h1:ORAeqmbrrozeyw5NjnMxh7peHO0UzV4wWYSwZeCUb20=
|
||||||
google.golang.org/api v0.156.0/go.mod h1:bUSmn4KFO0Q+69zo9CNIDp4Psi6BqM0np0CbzKRSiSY=
|
google.golang.org/api v0.157.0/go.mod h1:+z4v4ufbZ1WEpld6yMGHyggs+PmAHiaLNj5ytP3N01g=
|
||||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||||
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||||
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||||
@ -1059,12 +1092,12 @@ google.golang.org/genproto v0.0.0-20220518221133-4f43b3371335/go.mod h1:RAyBrSAP
|
|||||||
google.golang.org/genproto v0.0.0-20220523171625-347a074981d8/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4=
|
google.golang.org/genproto v0.0.0-20220523171625-347a074981d8/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4=
|
||||||
google.golang.org/genproto v0.0.0-20220608133413-ed9918b62aac/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA=
|
google.golang.org/genproto v0.0.0-20220608133413-ed9918b62aac/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA=
|
||||||
google.golang.org/genproto v0.0.0-20220616135557-88e70c0c3a90/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA=
|
google.golang.org/genproto v0.0.0-20220616135557-88e70c0c3a90/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA=
|
||||||
google.golang.org/genproto v0.0.0-20231212172506-995d672761c0 h1:YJ5pD9rF8o9Qtta0Cmy9rdBwkSjrTCT6XTiUQVOtIos=
|
google.golang.org/genproto v0.0.0-20240102182953-50ed04b92917 h1:nz5NESFLZbJGPFxDT/HCn+V1mZ8JGNoY4nUpmW/Y2eg=
|
||||||
google.golang.org/genproto v0.0.0-20231212172506-995d672761c0/go.mod h1:l/k7rMz0vFTBPy+tFSGvXEd3z+BcoG1k7EHbqm+YBsY=
|
google.golang.org/genproto v0.0.0-20240102182953-50ed04b92917/go.mod h1:pZqR+glSb11aJ+JQcczCvgf47+duRuzNSKqE8YAQnV0=
|
||||||
google.golang.org/genproto/googleapis/api v0.0.0-20231212172506-995d672761c0 h1:s1w3X6gQxwrLEpxnLd/qXTVLgQE2yXwaOaoa6IlY/+o=
|
google.golang.org/genproto/googleapis/api v0.0.0-20240102182953-50ed04b92917 h1:rcS6EyEaoCO52hQDupoSfrxI3R6C2Tq741is7X8OvnM=
|
||||||
google.golang.org/genproto/googleapis/api v0.0.0-20231212172506-995d672761c0/go.mod h1:CAny0tYF+0/9rmDB9fahA9YLzX3+AEVl1qXbv5hhj6c=
|
google.golang.org/genproto/googleapis/api v0.0.0-20240102182953-50ed04b92917/go.mod h1:CmlNWB9lSezaYELKS5Ym1r44VrrbPUa7JTvw+6MbpJ0=
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240102182953-50ed04b92917 h1:6G8oQ016D88m1xAKljMlBOOGWDZkes4kMhgGFlf8WcQ=
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20240116215550-a9fa1716bcac h1:nUQEQmH/csSvFECKYRv6HWEyypysidKl2I6Qpsglq/0=
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240102182953-50ed04b92917/go.mod h1:xtjpI3tXFPP051KaWnhvxkiubL/6dJ18vLVf7q2pTOU=
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20240116215550-a9fa1716bcac/go.mod h1:daQN87bsDqDoe316QbbvX60nMoJQa4r6Ds0ZuoAe5yA=
|
||||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||||
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
|
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
|
||||||
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
|
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
|
||||||
@ -1119,14 +1152,12 @@ gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8
|
|||||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
|
||||||
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||||
gopkg.in/inconshreveable/log15.v2 v2.0.0-20180818164646-67afb5ed74ec/go.mod h1:aPpfJ7XW+gOuirDoZ8gHhLh3kZ1B08FtV2bbmy7Jv3s=
|
gopkg.in/inconshreveable/log15.v2 v2.0.0-20180818164646-67afb5ed74ec/go.mod h1:aPpfJ7XW+gOuirDoZ8gHhLh3kZ1B08FtV2bbmy7Jv3s=
|
||||||
gopkg.in/square/go-jose.v2 v2.6.0 h1:NGk74WTnPKBNUhNzQX7PYcTLUjoq7mzKk2OKbvwk2iI=
|
gopkg.in/square/go-jose.v2 v2.6.0 h1:NGk74WTnPKBNUhNzQX7PYcTLUjoq7mzKk2OKbvwk2iI=
|
||||||
gopkg.in/square/go-jose.v2 v2.6.0/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI=
|
gopkg.in/square/go-jose.v2 v2.6.0/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI=
|
||||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
|
||||||
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||||
|
196
internal/metrix/meter.go
Normal file
196
internal/metrix/meter.go
Normal file
@ -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
Block a user