forked from TrueCloudLab/certificates
Improve tests for ACME account policy
This commit is contained in:
parent
0bb15e16f9
commit
256fe113f7
10 changed files with 571 additions and 102 deletions
|
@ -7,6 +7,8 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"go.step.sm/crypto/jose"
|
"go.step.sm/crypto/jose"
|
||||||
|
|
||||||
|
"github.com/smallstep/certificates/authority/policy"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Account is a subset of the internal account type containing only those
|
// Account is a subset of the internal account type containing only those
|
||||||
|
@ -60,6 +62,25 @@ type Policy struct {
|
||||||
X509 X509Policy `json:"x509"`
|
X509 X509Policy `json:"x509"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (p *Policy) GetAllowedNameOptions() *policy.X509NameOptions {
|
||||||
|
if p == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return &policy.X509NameOptions{
|
||||||
|
DNSDomains: p.X509.Allowed.DNSNames,
|
||||||
|
IPRanges: p.X509.Allowed.IPRanges,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
func (p *Policy) GetDeniedNameOptions() *policy.X509NameOptions {
|
||||||
|
if p == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return &policy.X509NameOptions{
|
||||||
|
DNSDomains: p.X509.Denied.DNSNames,
|
||||||
|
IPRanges: p.X509.Denied.IPRanges,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// ExternalAccountKey is an ACME External Account Binding key.
|
// ExternalAccountKey is an ACME External Account Binding key.
|
||||||
type ExternalAccountKey struct {
|
type ExternalAccountKey struct {
|
||||||
ID string `json:"id"`
|
ID string `json:"id"`
|
||||||
|
|
|
@ -2,7 +2,6 @@ package api
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"github.com/go-chi/chi"
|
"github.com/go-chi/chi"
|
||||||
|
@ -131,14 +130,11 @@ func (h *Handler) NewAccount(w http.ResponseWriter, r *http.Request) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Println("BEFORE EAK BINDING")
|
|
||||||
|
|
||||||
if eak != nil { // means that we have a (valid) External Account Binding key that should be bound, updated and sent in the response
|
if eak != nil { // means that we have a (valid) External Account Binding key that should be bound, updated and sent in the response
|
||||||
if err := eak.BindTo(acc); err != nil {
|
if err := eak.BindTo(acc); err != nil {
|
||||||
render.Error(w, err)
|
render.Error(w, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
fmt.Println("AFTER EAK BINDING")
|
|
||||||
if err := h.db.UpdateExternalAccountKey(ctx, prov.ID, eak); err != nil {
|
if err := h.db.UpdateExternalAccountKey(ctx, prov.ID, eak); err != nil {
|
||||||
render.Error(w, acme.WrapErrorISE(err, "error updating external account binding key"))
|
render.Error(w, acme.WrapErrorISE(err, "error updating external account binding key"))
|
||||||
return
|
return
|
||||||
|
|
|
@ -13,10 +13,12 @@ import (
|
||||||
|
|
||||||
"github.com/go-chi/chi"
|
"github.com/go-chi/chi"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
|
|
||||||
|
"go.step.sm/crypto/jose"
|
||||||
|
|
||||||
"github.com/smallstep/assert"
|
"github.com/smallstep/assert"
|
||||||
"github.com/smallstep/certificates/acme"
|
"github.com/smallstep/certificates/acme"
|
||||||
"github.com/smallstep/certificates/authority/provisioner"
|
"github.com/smallstep/certificates/authority/provisioner"
|
||||||
"go.step.sm/crypto/jose"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
@ -41,6 +43,19 @@ func newProv() acme.Provisioner {
|
||||||
return p
|
return p
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func newProvWithOptions(options *provisioner.Options) acme.Provisioner {
|
||||||
|
// Initialize provisioners
|
||||||
|
p := &provisioner.ACME{
|
||||||
|
Type: "ACME",
|
||||||
|
Name: "test@acme-<test>provisioner.com",
|
||||||
|
Options: options,
|
||||||
|
}
|
||||||
|
if err := p.Init(provisioner.Config{Claims: globalProvisionerClaims}); err != nil {
|
||||||
|
fmt.Printf("%v", err)
|
||||||
|
}
|
||||||
|
return p
|
||||||
|
}
|
||||||
|
|
||||||
func newACMEProv(t *testing.T) *provisioner.ACME {
|
func newACMEProv(t *testing.T) *provisioner.ACME {
|
||||||
p := newProv()
|
p := newProv()
|
||||||
a, ok := p.(*provisioner.ACME)
|
a, ok := p.(*provisioner.ACME)
|
||||||
|
|
|
@ -56,12 +56,16 @@ func (h *Handler) validateExternalAccountBinding(ctx context.Context, nar *NewAc
|
||||||
return nil, acme.WrapErrorISE(err, "error retrieving external account key")
|
return nil, acme.WrapErrorISE(err, "error retrieving external account key")
|
||||||
}
|
}
|
||||||
|
|
||||||
if externalAccountKey.AlreadyBound() {
|
if externalAccountKey == nil {
|
||||||
return nil, acme.NewError(acme.ErrorUnauthorizedType, "external account binding key with id '%s' was already bound to account '%s' on %s", keyID, externalAccountKey.AccountID, externalAccountKey.BoundAt)
|
return nil, acme.NewError(acme.ErrorUnauthorizedType, "the field 'kid' references an unknown key")
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(externalAccountKey.KeyBytes) == 0 {
|
if len(externalAccountKey.KeyBytes) == 0 {
|
||||||
return nil, acme.NewError(acme.ErrorServerInternalType, "no key bytes") // TODO(hs): improve error message
|
return nil, acme.NewError(acme.ErrorServerInternalType, "external account binding key with id '%s' does not have secret bytes", keyID)
|
||||||
|
}
|
||||||
|
|
||||||
|
if externalAccountKey.AlreadyBound() {
|
||||||
|
return nil, acme.NewError(acme.ErrorUnauthorizedType, "external account binding key with id '%s' was already bound to account '%s' on %s", keyID, externalAccountKey.AccountID, externalAccountKey.BoundAt)
|
||||||
}
|
}
|
||||||
|
|
||||||
payload, err := eabJWS.Verify(externalAccountKey.KeyBytes)
|
payload, err := eabJWS.Verify(externalAccountKey.KeyBytes)
|
||||||
|
|
|
@ -428,6 +428,114 @@ func TestHandler_validateExternalAccountBinding(t *testing.T) {
|
||||||
err: acme.NewErrorISE("error retrieving external account key"),
|
err: acme.NewErrorISE("error retrieving external account key"),
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"fail/db.GetExternalAccountKey-nil": func(t *testing.T) test {
|
||||||
|
jwk, err := jose.GenerateJWK("EC", "P-256", "ES256", "sig", "", 0)
|
||||||
|
assert.FatalError(t, err)
|
||||||
|
url := fmt.Sprintf("%s/acme/%s/account/new-account", baseURL.String(), escProvName)
|
||||||
|
rawEABJWS, err := createRawEABJWS(jwk, []byte{1, 3, 3, 7}, "eakID", url)
|
||||||
|
assert.FatalError(t, err)
|
||||||
|
eab := &ExternalAccountBinding{}
|
||||||
|
err = json.Unmarshal(rawEABJWS, &eab)
|
||||||
|
assert.FatalError(t, err)
|
||||||
|
nar := &NewAccountRequest{
|
||||||
|
Contact: []string{"foo", "bar"},
|
||||||
|
ExternalAccountBinding: eab,
|
||||||
|
}
|
||||||
|
payloadBytes, err := json.Marshal(nar)
|
||||||
|
assert.FatalError(t, err)
|
||||||
|
so := new(jose.SignerOptions)
|
||||||
|
so.WithHeader("alg", jose.SignatureAlgorithm(jwk.Algorithm))
|
||||||
|
so.WithHeader("url", url)
|
||||||
|
signer, err := jose.NewSigner(jose.SigningKey{
|
||||||
|
Algorithm: jose.SignatureAlgorithm(jwk.Algorithm),
|
||||||
|
Key: jwk.Key,
|
||||||
|
}, so)
|
||||||
|
assert.FatalError(t, err)
|
||||||
|
jws, err := signer.Sign(payloadBytes)
|
||||||
|
assert.FatalError(t, err)
|
||||||
|
raw, err := jws.CompactSerialize()
|
||||||
|
assert.FatalError(t, err)
|
||||||
|
parsedJWS, err := jose.ParseJWS(raw)
|
||||||
|
assert.FatalError(t, err)
|
||||||
|
prov := newACMEProv(t)
|
||||||
|
prov.RequireEAB = true
|
||||||
|
ctx := context.WithValue(context.Background(), jwkContextKey, jwk)
|
||||||
|
ctx = context.WithValue(ctx, baseURLContextKey, baseURL)
|
||||||
|
ctx = context.WithValue(ctx, provisionerContextKey, prov)
|
||||||
|
ctx = context.WithValue(ctx, jwsContextKey, parsedJWS)
|
||||||
|
return test{
|
||||||
|
db: &acme.MockDB{
|
||||||
|
MockGetExternalAccountKey: func(ctx context.Context, provisionerName, keyID string) (*acme.ExternalAccountKey, error) {
|
||||||
|
return nil, nil
|
||||||
|
},
|
||||||
|
},
|
||||||
|
ctx: ctx,
|
||||||
|
nar: &NewAccountRequest{
|
||||||
|
Contact: []string{"foo", "bar"},
|
||||||
|
ExternalAccountBinding: eab,
|
||||||
|
},
|
||||||
|
eak: nil,
|
||||||
|
err: acme.NewError(acme.ErrorUnauthorizedType, "the field 'kid' references an unknown key"),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"fail/db.GetExternalAccountKey-no-keybytes": func(t *testing.T) test {
|
||||||
|
jwk, err := jose.GenerateJWK("EC", "P-256", "ES256", "sig", "", 0)
|
||||||
|
assert.FatalError(t, err)
|
||||||
|
url := fmt.Sprintf("%s/acme/%s/account/new-account", baseURL.String(), escProvName)
|
||||||
|
rawEABJWS, err := createRawEABJWS(jwk, []byte{1, 3, 3, 7}, "eakID", url)
|
||||||
|
assert.FatalError(t, err)
|
||||||
|
eab := &ExternalAccountBinding{}
|
||||||
|
err = json.Unmarshal(rawEABJWS, &eab)
|
||||||
|
assert.FatalError(t, err)
|
||||||
|
nar := &NewAccountRequest{
|
||||||
|
Contact: []string{"foo", "bar"},
|
||||||
|
ExternalAccountBinding: eab,
|
||||||
|
}
|
||||||
|
payloadBytes, err := json.Marshal(nar)
|
||||||
|
assert.FatalError(t, err)
|
||||||
|
so := new(jose.SignerOptions)
|
||||||
|
so.WithHeader("alg", jose.SignatureAlgorithm(jwk.Algorithm))
|
||||||
|
so.WithHeader("url", url)
|
||||||
|
signer, err := jose.NewSigner(jose.SigningKey{
|
||||||
|
Algorithm: jose.SignatureAlgorithm(jwk.Algorithm),
|
||||||
|
Key: jwk.Key,
|
||||||
|
}, so)
|
||||||
|
assert.FatalError(t, err)
|
||||||
|
jws, err := signer.Sign(payloadBytes)
|
||||||
|
assert.FatalError(t, err)
|
||||||
|
raw, err := jws.CompactSerialize()
|
||||||
|
assert.FatalError(t, err)
|
||||||
|
parsedJWS, err := jose.ParseJWS(raw)
|
||||||
|
assert.FatalError(t, err)
|
||||||
|
prov := newACMEProv(t)
|
||||||
|
prov.RequireEAB = true
|
||||||
|
ctx := context.WithValue(context.Background(), jwkContextKey, jwk)
|
||||||
|
ctx = context.WithValue(ctx, baseURLContextKey, baseURL)
|
||||||
|
ctx = context.WithValue(ctx, provisionerContextKey, prov)
|
||||||
|
ctx = context.WithValue(ctx, jwsContextKey, parsedJWS)
|
||||||
|
createdAt := time.Now()
|
||||||
|
return test{
|
||||||
|
db: &acme.MockDB{
|
||||||
|
MockGetExternalAccountKey: func(ctx context.Context, provisionerName, keyID string) (*acme.ExternalAccountKey, error) {
|
||||||
|
return &acme.ExternalAccountKey{
|
||||||
|
ID: "eakID",
|
||||||
|
ProvisionerID: provID,
|
||||||
|
Reference: "testeak",
|
||||||
|
CreatedAt: createdAt,
|
||||||
|
AccountID: "some-account-id",
|
||||||
|
KeyBytes: []byte{},
|
||||||
|
}, nil
|
||||||
|
},
|
||||||
|
},
|
||||||
|
ctx: ctx,
|
||||||
|
nar: &NewAccountRequest{
|
||||||
|
Contact: []string{"foo", "bar"},
|
||||||
|
ExternalAccountBinding: eab,
|
||||||
|
},
|
||||||
|
eak: nil,
|
||||||
|
err: acme.NewError(acme.ErrorServerInternalType, "external account binding key with id 'eakID' does not have secret bytes"),
|
||||||
|
}
|
||||||
|
},
|
||||||
"fail/db.GetExternalAccountKey-wrong-provisioner": func(t *testing.T) test {
|
"fail/db.GetExternalAccountKey-wrong-provisioner": func(t *testing.T) test {
|
||||||
jwk, err := jose.GenerateJWK("EC", "P-256", "ES256", "sig", "", 0)
|
jwk, err := jose.GenerateJWK("EC", "P-256", "ES256", "sig", "", 0)
|
||||||
assert.FatalError(t, err)
|
assert.FatalError(t, err)
|
||||||
|
@ -522,6 +630,7 @@ func TestHandler_validateExternalAccountBinding(t *testing.T) {
|
||||||
Reference: "testeak",
|
Reference: "testeak",
|
||||||
CreatedAt: createdAt,
|
CreatedAt: createdAt,
|
||||||
AccountID: "some-account-id",
|
AccountID: "some-account-id",
|
||||||
|
KeyBytes: []byte{1, 3, 3, 7},
|
||||||
BoundAt: boundAt,
|
BoundAt: boundAt,
|
||||||
}, nil
|
}, nil
|
||||||
},
|
},
|
||||||
|
|
|
@ -17,6 +17,7 @@ import (
|
||||||
|
|
||||||
"github.com/smallstep/certificates/acme"
|
"github.com/smallstep/certificates/acme"
|
||||||
"github.com/smallstep/certificates/api/render"
|
"github.com/smallstep/certificates/api/render"
|
||||||
|
"github.com/smallstep/certificates/authority/policy"
|
||||||
"github.com/smallstep/certificates/authority/provisioner"
|
"github.com/smallstep/certificates/authority/provisioner"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -102,29 +103,35 @@ func (h *Handler) NewOrder(w http.ResponseWriter, r *http.Request) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO(hs): the policy evaluation below should also verify rules set in the Account (i.e. allowed/denied
|
|
||||||
// DNS and IPs). It's probably good to connect those to the EAB credentials and management? Or
|
|
||||||
// should we do it fully properly and connect them to the Account directly? The latter would allow
|
|
||||||
// management of allowed/denied names based on just the name, without having bound to EAB. Still,
|
|
||||||
// EAB is not illogical, because that's the way Accounts are connected to an external system and
|
|
||||||
// thus make sense to also set the allowed/denied names based on that info.
|
|
||||||
// TODO(hs): gather all errors, so that we can build one response with subproblems; include the nor.Validate()
|
// TODO(hs): gather all errors, so that we can build one response with subproblems; include the nor.Validate()
|
||||||
// error here too, like in example?
|
// error here too, like in example?
|
||||||
|
|
||||||
eak, err := h.db.GetExternalAccountKeyByAccountID(ctx, prov.GetID(), acc.ID)
|
eak, err := h.db.GetExternalAccountKeyByAccountID(ctx, prov.GetID(), acc.ID)
|
||||||
fmt.Println("EAK: ", eak, err)
|
if err != nil {
|
||||||
|
render.Error(w, acme.WrapErrorISE(err, "error retrieving external account binding key"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
acmePolicy, err := newACMEPolicyEngine(eak)
|
||||||
|
if err != nil {
|
||||||
|
render.Error(w, acme.WrapErrorISE(err, "error creating ACME policy engine"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
for _, identifier := range nor.Identifiers {
|
for _, identifier := range nor.Identifiers {
|
||||||
|
// evalue the ACME account level policy
|
||||||
|
if err = isIdentifierAllowed(acmePolicy, identifier); err != nil {
|
||||||
|
render.Error(w, acme.WrapError(acme.ErrorRejectedIdentifierType, err, "not authorized"))
|
||||||
|
return
|
||||||
|
}
|
||||||
// evaluate the provisioner level policy
|
// evaluate the provisioner level policy
|
||||||
orderIdentifier := provisioner.ACMEIdentifier{Type: provisioner.ACMEIdentifierType(identifier.Type), Value: identifier.Value}
|
orderIdentifier := provisioner.ACMEIdentifier{Type: provisioner.ACMEIdentifierType(identifier.Type), Value: identifier.Value}
|
||||||
err = prov.AuthorizeOrderIdentifier(ctx, orderIdentifier)
|
if err = prov.AuthorizeOrderIdentifier(ctx, orderIdentifier); err != nil {
|
||||||
if err != nil {
|
|
||||||
render.Error(w, acme.WrapError(acme.ErrorRejectedIdentifierType, err, "not authorized"))
|
render.Error(w, acme.WrapError(acme.ErrorRejectedIdentifierType, err, "not authorized"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// evaluate the authority level policy
|
// evaluate the authority level policy
|
||||||
err = h.ca.AreSANsAllowed(ctx, []string{identifier.Value})
|
if err = h.ca.AreSANsAllowed(ctx, []string{identifier.Value}); err != nil {
|
||||||
if err != nil {
|
|
||||||
render.Error(w, acme.WrapError(acme.ErrorRejectedIdentifierType, err, "not authorized"))
|
render.Error(w, acme.WrapError(acme.ErrorRejectedIdentifierType, err, "not authorized"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -180,6 +187,27 @@ func (h *Handler) NewOrder(w http.ResponseWriter, r *http.Request) {
|
||||||
render.JSONStatus(w, o, http.StatusCreated)
|
render.JSONStatus(w, o, http.StatusCreated)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func isIdentifierAllowed(acmePolicy policy.X509Policy, identifier acme.Identifier) error {
|
||||||
|
if acmePolicy == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
allowed, err := acmePolicy.AreSANsAllowed([]string{identifier.Value})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if !allowed {
|
||||||
|
return fmt.Errorf("acme identifier '%s' not allowed", identifier.Value)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func newACMEPolicyEngine(eak *acme.ExternalAccountKey) (policy.X509Policy, error) {
|
||||||
|
if eak == nil {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
return policy.NewX509PolicyEngine(eak.Policy)
|
||||||
|
}
|
||||||
|
|
||||||
func (h *Handler) newAuthorization(ctx context.Context, az *acme.Authorization) error {
|
func (h *Handler) newAuthorization(ctx context.Context, az *acme.Authorization) error {
|
||||||
if strings.HasPrefix(az.Identifier.Value, "*.") {
|
if strings.HasPrefix(az.Identifier.Value, "*.") {
|
||||||
az.Wildcard = true
|
az.Wildcard = true
|
||||||
|
|
|
@ -16,9 +16,13 @@ import (
|
||||||
|
|
||||||
"github.com/go-chi/chi"
|
"github.com/go-chi/chi"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
|
|
||||||
|
"go.step.sm/crypto/pemutil"
|
||||||
|
|
||||||
"github.com/smallstep/assert"
|
"github.com/smallstep/assert"
|
||||||
"github.com/smallstep/certificates/acme"
|
"github.com/smallstep/certificates/acme"
|
||||||
"go.step.sm/crypto/pemutil"
|
"github.com/smallstep/certificates/authority/policy"
|
||||||
|
"github.com/smallstep/certificates/authority/provisioner"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestNewOrderRequest_Validate(t *testing.T) {
|
func TestNewOrderRequest_Validate(t *testing.T) {
|
||||||
|
@ -757,6 +761,188 @@ func TestHandler_NewOrder(t *testing.T) {
|
||||||
err: acme.NewError(acme.ErrorMalformedType, "identifiers list cannot be empty"),
|
err: acme.NewError(acme.ErrorMalformedType, "identifiers list cannot be empty"),
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"fail/db.GetExternalAccountKeyByAccountID-error": func(t *testing.T) test {
|
||||||
|
acc := &acme.Account{ID: "accID"}
|
||||||
|
fr := &NewOrderRequest{
|
||||||
|
Identifiers: []acme.Identifier{
|
||||||
|
{Type: "dns", Value: "zap.internal"},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
b, err := json.Marshal(fr)
|
||||||
|
assert.FatalError(t, err)
|
||||||
|
ctx := context.WithValue(context.Background(), provisionerContextKey, prov)
|
||||||
|
ctx = context.WithValue(ctx, accContextKey, acc)
|
||||||
|
ctx = context.WithValue(ctx, payloadContextKey, &payloadInfo{value: b})
|
||||||
|
return test{
|
||||||
|
ctx: ctx,
|
||||||
|
statusCode: 500,
|
||||||
|
ca: &mockCA{},
|
||||||
|
db: &acme.MockDB{
|
||||||
|
MockGetExternalAccountKeyByAccountID: func(ctx context.Context, provisionerID, accountID string) (*acme.ExternalAccountKey, error) {
|
||||||
|
assert.Equals(t, prov.GetID(), provisionerID)
|
||||||
|
assert.Equals(t, "accID", accountID)
|
||||||
|
return nil, errors.New("force")
|
||||||
|
},
|
||||||
|
},
|
||||||
|
err: acme.NewErrorISE("error retrieving external account binding key: force"),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"fail/newACMEPolicyEngine-error": func(t *testing.T) test {
|
||||||
|
acc := &acme.Account{ID: "accID"}
|
||||||
|
fr := &NewOrderRequest{
|
||||||
|
Identifiers: []acme.Identifier{
|
||||||
|
{Type: "dns", Value: "zap.internal"},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
b, err := json.Marshal(fr)
|
||||||
|
assert.FatalError(t, err)
|
||||||
|
ctx := context.WithValue(context.Background(), provisionerContextKey, prov)
|
||||||
|
ctx = context.WithValue(ctx, accContextKey, acc)
|
||||||
|
ctx = context.WithValue(ctx, payloadContextKey, &payloadInfo{value: b})
|
||||||
|
return test{
|
||||||
|
ctx: ctx,
|
||||||
|
statusCode: 500,
|
||||||
|
ca: &mockCA{},
|
||||||
|
db: &acme.MockDB{
|
||||||
|
MockGetExternalAccountKeyByAccountID: func(ctx context.Context, provisionerID, accountID string) (*acme.ExternalAccountKey, error) {
|
||||||
|
assert.Equals(t, prov.GetID(), provisionerID)
|
||||||
|
assert.Equals(t, "accID", accountID)
|
||||||
|
return &acme.ExternalAccountKey{
|
||||||
|
Policy: &acme.Policy{
|
||||||
|
X509: acme.X509Policy{
|
||||||
|
Allowed: acme.PolicyNames{
|
||||||
|
DNSNames: []string{"**.local"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}, nil
|
||||||
|
},
|
||||||
|
},
|
||||||
|
err: acme.NewErrorISE("error creating ACME policy engine"),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"fail/isIdentifierAllowed-error": func(t *testing.T) test {
|
||||||
|
acc := &acme.Account{ID: "accID"}
|
||||||
|
fr := &NewOrderRequest{
|
||||||
|
Identifiers: []acme.Identifier{
|
||||||
|
{Type: "dns", Value: "zap.internal"},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
b, err := json.Marshal(fr)
|
||||||
|
assert.FatalError(t, err)
|
||||||
|
ctx := context.WithValue(context.Background(), provisionerContextKey, prov)
|
||||||
|
ctx = context.WithValue(ctx, accContextKey, acc)
|
||||||
|
ctx = context.WithValue(ctx, payloadContextKey, &payloadInfo{value: b})
|
||||||
|
return test{
|
||||||
|
ctx: ctx,
|
||||||
|
statusCode: 400,
|
||||||
|
ca: &mockCA{},
|
||||||
|
db: &acme.MockDB{
|
||||||
|
MockGetExternalAccountKeyByAccountID: func(ctx context.Context, provisionerID, accountID string) (*acme.ExternalAccountKey, error) {
|
||||||
|
assert.Equals(t, prov.GetID(), provisionerID)
|
||||||
|
assert.Equals(t, "accID", accountID)
|
||||||
|
return &acme.ExternalAccountKey{
|
||||||
|
Policy: &acme.Policy{
|
||||||
|
X509: acme.X509Policy{
|
||||||
|
Allowed: acme.PolicyNames{
|
||||||
|
DNSNames: []string{"*.local"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}, nil
|
||||||
|
},
|
||||||
|
},
|
||||||
|
err: acme.NewError(acme.ErrorRejectedIdentifierType, "not authorized"),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"fail/prov.AuthorizeOrderIdentifier-error": func(t *testing.T) test {
|
||||||
|
options := &provisioner.Options{
|
||||||
|
X509: &provisioner.X509Options{
|
||||||
|
AllowedNames: &policy.X509NameOptions{
|
||||||
|
DNSDomains: []string{"*.local"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
provWithPolicy := newProvWithOptions(options)
|
||||||
|
acc := &acme.Account{ID: "accID"}
|
||||||
|
fr := &NewOrderRequest{
|
||||||
|
Identifiers: []acme.Identifier{
|
||||||
|
{Type: "dns", Value: "zap.internal"},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
b, err := json.Marshal(fr)
|
||||||
|
assert.FatalError(t, err)
|
||||||
|
ctx := context.WithValue(context.Background(), provisionerContextKey, provWithPolicy)
|
||||||
|
ctx = context.WithValue(ctx, accContextKey, acc)
|
||||||
|
ctx = context.WithValue(ctx, payloadContextKey, &payloadInfo{value: b})
|
||||||
|
return test{
|
||||||
|
ctx: ctx,
|
||||||
|
statusCode: 400,
|
||||||
|
ca: &mockCA{},
|
||||||
|
db: &acme.MockDB{
|
||||||
|
MockGetExternalAccountKeyByAccountID: func(ctx context.Context, provisionerID, accountID string) (*acme.ExternalAccountKey, error) {
|
||||||
|
assert.Equals(t, prov.GetID(), provisionerID)
|
||||||
|
assert.Equals(t, "accID", accountID)
|
||||||
|
return &acme.ExternalAccountKey{
|
||||||
|
Policy: &acme.Policy{
|
||||||
|
X509: acme.X509Policy{
|
||||||
|
Allowed: acme.PolicyNames{
|
||||||
|
DNSNames: []string{"*.internal"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}, nil
|
||||||
|
},
|
||||||
|
},
|
||||||
|
err: acme.NewError(acme.ErrorRejectedIdentifierType, "not authorized"),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"fail/ca.AreSANsAllowed-error": func(t *testing.T) test {
|
||||||
|
options := &provisioner.Options{
|
||||||
|
X509: &provisioner.X509Options{
|
||||||
|
AllowedNames: &policy.X509NameOptions{
|
||||||
|
DNSDomains: []string{"*.internal"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
provWithPolicy := newProvWithOptions(options)
|
||||||
|
acc := &acme.Account{ID: "accID"}
|
||||||
|
fr := &NewOrderRequest{
|
||||||
|
Identifiers: []acme.Identifier{
|
||||||
|
{Type: "dns", Value: "zap.internal"},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
b, err := json.Marshal(fr)
|
||||||
|
assert.FatalError(t, err)
|
||||||
|
ctx := context.WithValue(context.Background(), provisionerContextKey, provWithPolicy)
|
||||||
|
ctx = context.WithValue(ctx, accContextKey, acc)
|
||||||
|
ctx = context.WithValue(ctx, payloadContextKey, &payloadInfo{value: b})
|
||||||
|
return test{
|
||||||
|
ctx: ctx,
|
||||||
|
statusCode: 400,
|
||||||
|
ca: &mockCA{
|
||||||
|
MockAreSANsallowed: func(ctx context.Context, sans []string) error {
|
||||||
|
return errors.New("force: not authorized by authority")
|
||||||
|
},
|
||||||
|
},
|
||||||
|
db: &acme.MockDB{
|
||||||
|
MockGetExternalAccountKeyByAccountID: func(ctx context.Context, provisionerID, accountID string) (*acme.ExternalAccountKey, error) {
|
||||||
|
assert.Equals(t, prov.GetID(), provisionerID)
|
||||||
|
assert.Equals(t, "accID", accountID)
|
||||||
|
return &acme.ExternalAccountKey{
|
||||||
|
Policy: &acme.Policy{
|
||||||
|
X509: acme.X509Policy{
|
||||||
|
Allowed: acme.PolicyNames{
|
||||||
|
DNSNames: []string{"*.internal"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}, nil
|
||||||
|
},
|
||||||
|
},
|
||||||
|
err: acme.NewError(acme.ErrorRejectedIdentifierType, "not authorized"),
|
||||||
|
}
|
||||||
|
},
|
||||||
"fail/error-h.newAuthorization": func(t *testing.T) test {
|
"fail/error-h.newAuthorization": func(t *testing.T) test {
|
||||||
acc := &acme.Account{ID: "accID"}
|
acc := &acme.Account{ID: "accID"}
|
||||||
fr := &NewOrderRequest{
|
fr := &NewOrderRequest{
|
||||||
|
@ -1360,6 +1546,109 @@ func TestHandler_NewOrder(t *testing.T) {
|
||||||
testBufferDur := 5 * time.Second
|
testBufferDur := 5 * time.Second
|
||||||
orderExpiry := now.Add(defaultOrderExpiry)
|
orderExpiry := now.Add(defaultOrderExpiry)
|
||||||
|
|
||||||
|
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-with-policy": func(t *testing.T) test {
|
||||||
|
options := &provisioner.Options{
|
||||||
|
X509: &provisioner.X509Options{
|
||||||
|
AllowedNames: &policy.X509NameOptions{
|
||||||
|
DNSDomains: []string{"*.internal"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
provWithPolicy := newProvWithOptions(options)
|
||||||
|
acc := &acme.Account{ID: "accID"}
|
||||||
|
nor := &NewOrderRequest{
|
||||||
|
Identifiers: []acme.Identifier{
|
||||||
|
{Type: "dns", Value: "zap.internal"},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
b, err := json.Marshal(nor)
|
||||||
|
assert.FatalError(t, err)
|
||||||
|
ctx := context.WithValue(context.Background(), provisionerContextKey, provWithPolicy)
|
||||||
|
ctx = context.WithValue(ctx, accContextKey, acc)
|
||||||
|
ctx = context.WithValue(ctx, payloadContextKey, &payloadInfo{value: b})
|
||||||
|
ctx = context.WithValue(ctx, baseURLContextKey, baseURL)
|
||||||
|
var (
|
||||||
|
ch1, ch2, ch3 **acme.Challenge
|
||||||
|
az1ID *string
|
||||||
|
count = 0
|
||||||
|
)
|
||||||
|
return test{
|
||||||
|
ctx: ctx,
|
||||||
|
statusCode: 201,
|
||||||
|
nor: nor,
|
||||||
|
ca: &mockCA{},
|
||||||
|
db: &acme.MockDB{
|
||||||
|
MockCreateChallenge: func(ctx context.Context, ch *acme.Challenge) error {
|
||||||
|
switch count {
|
||||||
|
case 0:
|
||||||
|
ch.ID = "dns"
|
||||||
|
assert.Equals(t, ch.Type, acme.DNS01)
|
||||||
|
ch1 = &ch
|
||||||
|
case 1:
|
||||||
|
ch.ID = "http"
|
||||||
|
assert.Equals(t, ch.Type, acme.HTTP01)
|
||||||
|
ch2 = &ch
|
||||||
|
case 2:
|
||||||
|
ch.ID = "tls"
|
||||||
|
assert.Equals(t, ch.Type, acme.TLSALPN01)
|
||||||
|
ch3 = &ch
|
||||||
|
default:
|
||||||
|
assert.FatalError(t, errors.New("test logic error"))
|
||||||
|
return errors.New("force")
|
||||||
|
}
|
||||||
|
count++
|
||||||
|
assert.Equals(t, ch.AccountID, "accID")
|
||||||
|
assert.NotEquals(t, ch.Token, "")
|
||||||
|
assert.Equals(t, ch.Status, acme.StatusPending)
|
||||||
|
assert.Equals(t, ch.Value, "zap.internal")
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
MockCreateAuthorization: func(ctx context.Context, az *acme.Authorization) error {
|
||||||
|
az.ID = "az1ID"
|
||||||
|
az1ID = &az.ID
|
||||||
|
assert.Equals(t, az.AccountID, "accID")
|
||||||
|
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, *ch2, *ch3})
|
||||||
|
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.ID, "ordID")
|
||||||
assert.Equals(t, o.Status, acme.StatusPending)
|
assert.Equals(t, o.Status, acme.StatusPending)
|
||||||
assert.Equals(t, o.Identifiers, nor.Identifiers)
|
assert.Equals(t, o.Identifiers, nor.Identifiers)
|
||||||
|
|
|
@ -24,14 +24,16 @@ import (
|
||||||
"github.com/go-chi/chi"
|
"github.com/go-chi/chi"
|
||||||
"github.com/google/go-cmp/cmp"
|
"github.com/google/go-cmp/cmp"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
|
"golang.org/x/crypto/ocsp"
|
||||||
|
|
||||||
|
"go.step.sm/crypto/jose"
|
||||||
|
"go.step.sm/crypto/keyutil"
|
||||||
|
"go.step.sm/crypto/x509util"
|
||||||
|
|
||||||
"github.com/smallstep/assert"
|
"github.com/smallstep/assert"
|
||||||
"github.com/smallstep/certificates/acme"
|
"github.com/smallstep/certificates/acme"
|
||||||
"github.com/smallstep/certificates/authority"
|
"github.com/smallstep/certificates/authority"
|
||||||
"github.com/smallstep/certificates/authority/provisioner"
|
"github.com/smallstep/certificates/authority/provisioner"
|
||||||
"go.step.sm/crypto/jose"
|
|
||||||
"go.step.sm/crypto/keyutil"
|
|
||||||
"go.step.sm/crypto/x509util"
|
|
||||||
"golang.org/x/crypto/ocsp"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// v is a utility function to return the pointer to an integer
|
// v is a utility function to return the pointer to an integer
|
||||||
|
@ -274,8 +276,9 @@ func jwsFinal(sha crypto.Hash, sig []byte, phead, payload string) ([]byte, error
|
||||||
}
|
}
|
||||||
|
|
||||||
type mockCA struct {
|
type mockCA struct {
|
||||||
MockIsRevoked func(sn string) (bool, error)
|
MockIsRevoked func(sn string) (bool, error)
|
||||||
MockRevoke func(ctx context.Context, opts *authority.RevokeOptions) error
|
MockRevoke func(ctx context.Context, opts *authority.RevokeOptions) error
|
||||||
|
MockAreSANsallowed func(ctx context.Context, sans []string) error
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *mockCA) Sign(cr *x509.CertificateRequest, opts provisioner.SignOptions, signOpts ...provisioner.SignOption) ([]*x509.Certificate, error) {
|
func (m *mockCA) Sign(cr *x509.CertificateRequest, opts provisioner.SignOptions, signOpts ...provisioner.SignOption) ([]*x509.Certificate, error) {
|
||||||
|
@ -283,6 +286,9 @@ func (m *mockCA) Sign(cr *x509.CertificateRequest, opts provisioner.SignOptions,
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *mockCA) AreSANsAllowed(ctx context.Context, sans []string) error {
|
func (m *mockCA) AreSANsAllowed(ctx context.Context, sans []string) error {
|
||||||
|
if m.MockAreSANsallowed != nil {
|
||||||
|
return m.MockAreSANsallowed(ctx, sans)
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,9 @@ import (
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"go.step.sm/linkedca"
|
"go.step.sm/linkedca"
|
||||||
|
"google.golang.org/protobuf/types/known/timestamppb"
|
||||||
|
|
||||||
|
"github.com/smallstep/certificates/acme"
|
||||||
"github.com/smallstep/certificates/api/render"
|
"github.com/smallstep/certificates/api/render"
|
||||||
"github.com/smallstep/certificates/authority/admin"
|
"github.com/smallstep/certificates/authority/admin"
|
||||||
)
|
)
|
||||||
|
@ -85,3 +87,78 @@ func (h *ACMEAdminResponder) CreateExternalAccountKey(w http.ResponseWriter, r *
|
||||||
func (h *ACMEAdminResponder) DeleteExternalAccountKey(w http.ResponseWriter, r *http.Request) {
|
func (h *ACMEAdminResponder) DeleteExternalAccountKey(w http.ResponseWriter, r *http.Request) {
|
||||||
render.Error(w, admin.NewError(admin.ErrorNotImplementedType, "this functionality is currently only available in Certificate Manager: https://u.step.sm/cm"))
|
render.Error(w, admin.NewError(admin.ErrorNotImplementedType, "this functionality is currently only available in Certificate Manager: https://u.step.sm/cm"))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func eakToLinked(k *acme.ExternalAccountKey) *linkedca.EABKey {
|
||||||
|
|
||||||
|
if k == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
eak := &linkedca.EABKey{
|
||||||
|
Id: k.ID,
|
||||||
|
HmacKey: k.KeyBytes,
|
||||||
|
Provisioner: k.ProvisionerID,
|
||||||
|
Reference: k.Reference,
|
||||||
|
Account: k.AccountID,
|
||||||
|
CreatedAt: timestamppb.New(k.CreatedAt),
|
||||||
|
BoundAt: timestamppb.New(k.BoundAt),
|
||||||
|
}
|
||||||
|
|
||||||
|
if k.Policy != nil {
|
||||||
|
eak.Policy = &linkedca.Policy{
|
||||||
|
X509: &linkedca.X509Policy{
|
||||||
|
Allow: &linkedca.X509Names{},
|
||||||
|
Deny: &linkedca.X509Names{},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
eak.Policy.X509.Allow.Dns = k.Policy.X509.Allowed.DNSNames
|
||||||
|
eak.Policy.X509.Allow.Ips = k.Policy.X509.Allowed.IPRanges
|
||||||
|
eak.Policy.X509.Deny.Dns = k.Policy.X509.Denied.DNSNames
|
||||||
|
eak.Policy.X509.Deny.Ips = k.Policy.X509.Denied.IPRanges
|
||||||
|
}
|
||||||
|
|
||||||
|
return eak
|
||||||
|
}
|
||||||
|
|
||||||
|
func linkedEAKToCertificates(k *linkedca.EABKey) *acme.ExternalAccountKey {
|
||||||
|
if k == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
eak := &acme.ExternalAccountKey{
|
||||||
|
ID: k.Id,
|
||||||
|
ProvisionerID: k.Provisioner,
|
||||||
|
Reference: k.Reference,
|
||||||
|
AccountID: k.Account,
|
||||||
|
KeyBytes: k.HmacKey,
|
||||||
|
CreatedAt: k.CreatedAt.AsTime(),
|
||||||
|
BoundAt: k.BoundAt.AsTime(),
|
||||||
|
}
|
||||||
|
|
||||||
|
if k.Policy == nil {
|
||||||
|
return eak
|
||||||
|
}
|
||||||
|
|
||||||
|
eak.Policy = &acme.Policy{}
|
||||||
|
|
||||||
|
if k.Policy.X509 == nil {
|
||||||
|
return eak
|
||||||
|
}
|
||||||
|
|
||||||
|
eak.Policy.X509 = acme.X509Policy{
|
||||||
|
Allowed: acme.PolicyNames{},
|
||||||
|
Denied: acme.PolicyNames{},
|
||||||
|
}
|
||||||
|
|
||||||
|
if k.Policy.X509.Allow != nil {
|
||||||
|
eak.Policy.X509.Allowed.DNSNames = k.Policy.X509.Allow.Dns
|
||||||
|
eak.Policy.X509.Allowed.IPRanges = k.Policy.X509.Allow.Ips
|
||||||
|
}
|
||||||
|
|
||||||
|
if k.Policy.X509.Deny != nil {
|
||||||
|
eak.Policy.X509.Denied.DNSNames = k.Policy.X509.Deny.Dns
|
||||||
|
eak.Policy.X509.Denied.IPRanges = k.Policy.X509.Deny.Ips
|
||||||
|
}
|
||||||
|
|
||||||
|
return eak
|
||||||
|
}
|
||||||
|
|
|
@ -4,7 +4,6 @@ import (
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"github.com/go-chi/chi"
|
"github.com/go-chi/chi"
|
||||||
"google.golang.org/protobuf/types/known/timestamppb"
|
|
||||||
|
|
||||||
"go.step.sm/linkedca"
|
"go.step.sm/linkedca"
|
||||||
|
|
||||||
|
@ -148,78 +147,3 @@ func (h *Handler) loadExternalAccountKey(next http.HandlerFunc) http.HandlerFunc
|
||||||
next(w, r.WithContext(ctx))
|
next(w, r.WithContext(ctx))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func eakToLinked(k *acme.ExternalAccountKey) *linkedca.EABKey {
|
|
||||||
|
|
||||||
if k == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
eak := &linkedca.EABKey{
|
|
||||||
Id: k.ID,
|
|
||||||
HmacKey: k.KeyBytes,
|
|
||||||
Provisioner: k.ProvisionerID,
|
|
||||||
Reference: k.Reference,
|
|
||||||
Account: k.AccountID,
|
|
||||||
CreatedAt: timestamppb.New(k.CreatedAt),
|
|
||||||
BoundAt: timestamppb.New(k.BoundAt),
|
|
||||||
}
|
|
||||||
|
|
||||||
if k.Policy != nil {
|
|
||||||
eak.Policy = &linkedca.Policy{
|
|
||||||
X509: &linkedca.X509Policy{
|
|
||||||
Allow: &linkedca.X509Names{},
|
|
||||||
Deny: &linkedca.X509Names{},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
eak.Policy.X509.Allow.Dns = k.Policy.X509.Allowed.DNSNames
|
|
||||||
eak.Policy.X509.Allow.Ips = k.Policy.X509.Allowed.IPRanges
|
|
||||||
eak.Policy.X509.Deny.Dns = k.Policy.X509.Denied.DNSNames
|
|
||||||
eak.Policy.X509.Deny.Ips = k.Policy.X509.Denied.IPRanges
|
|
||||||
}
|
|
||||||
|
|
||||||
return eak
|
|
||||||
}
|
|
||||||
|
|
||||||
func linkedEAKToCertificates(k *linkedca.EABKey) *acme.ExternalAccountKey {
|
|
||||||
if k == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
eak := &acme.ExternalAccountKey{
|
|
||||||
ID: k.Id,
|
|
||||||
ProvisionerID: k.Provisioner,
|
|
||||||
Reference: k.Reference,
|
|
||||||
AccountID: k.Account,
|
|
||||||
KeyBytes: k.HmacKey,
|
|
||||||
CreatedAt: k.CreatedAt.AsTime(),
|
|
||||||
BoundAt: k.BoundAt.AsTime(),
|
|
||||||
}
|
|
||||||
|
|
||||||
if k.Policy == nil {
|
|
||||||
return eak
|
|
||||||
}
|
|
||||||
|
|
||||||
eak.Policy = &acme.Policy{}
|
|
||||||
|
|
||||||
if k.Policy.X509 == nil {
|
|
||||||
return eak
|
|
||||||
}
|
|
||||||
|
|
||||||
eak.Policy.X509 = acme.X509Policy{
|
|
||||||
Allowed: acme.PolicyNames{},
|
|
||||||
Denied: acme.PolicyNames{},
|
|
||||||
}
|
|
||||||
|
|
||||||
if k.Policy.X509.Allow != nil {
|
|
||||||
eak.Policy.X509.Allowed.DNSNames = k.Policy.X509.Allow.Dns
|
|
||||||
eak.Policy.X509.Allowed.IPRanges = k.Policy.X509.Allow.Ips
|
|
||||||
}
|
|
||||||
|
|
||||||
if k.Policy.X509.Deny != nil {
|
|
||||||
eak.Policy.X509.Denied.DNSNames = k.Policy.X509.Deny.Dns
|
|
||||||
eak.Policy.X509.Denied.IPRanges = k.Policy.X509.Deny.Ips
|
|
||||||
}
|
|
||||||
|
|
||||||
return eak
|
|
||||||
}
|
|
||||||
|
|
Loading…
Reference in a new issue