Skip to content

Commit 65090da

Browse files
authored
Merge pull request smallstep#788 from smallstep/herman/allow-deny
Add allow/deny policy for x509 SANs and SSH Principals
2 parents 88a1bf1 + cc26a0b commit 65090da

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

89 files changed

+16370
-648
lines changed

acme/account.go

Lines changed: 52 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ import (
77
"time"
88

99
"go.step.sm/crypto/jose"
10+
11+
"github.com/smallstep/certificates/authority/policy"
1012
)
1113

1214
// Account is a subset of the internal account type containing only those
@@ -43,15 +45,63 @@ func KeyToID(jwk *jose.JSONWebKey) (string, error) {
4345
return base64.RawURLEncoding.EncodeToString(kid), nil
4446
}
4547

48+
// PolicyNames contains ACME account level policy names
49+
type PolicyNames struct {
50+
DNSNames []string `json:"dns"`
51+
IPRanges []string `json:"ips"`
52+
}
53+
54+
// X509Policy contains ACME account level X.509 policy
55+
type X509Policy struct {
56+
Allowed PolicyNames `json:"allow"`
57+
Denied PolicyNames `json:"deny"`
58+
AllowWildcardNames bool `json:"allowWildcardNames"`
59+
}
60+
61+
// Policy is an ACME Account level policy
62+
type Policy struct {
63+
X509 X509Policy `json:"x509"`
64+
}
65+
66+
func (p *Policy) GetAllowedNameOptions() *policy.X509NameOptions {
67+
if p == nil {
68+
return nil
69+
}
70+
return &policy.X509NameOptions{
71+
DNSDomains: p.X509.Allowed.DNSNames,
72+
IPRanges: p.X509.Allowed.IPRanges,
73+
}
74+
}
75+
func (p *Policy) GetDeniedNameOptions() *policy.X509NameOptions {
76+
if p == nil {
77+
return nil
78+
}
79+
return &policy.X509NameOptions{
80+
DNSDomains: p.X509.Denied.DNSNames,
81+
IPRanges: p.X509.Denied.IPRanges,
82+
}
83+
}
84+
85+
// AreWildcardNamesAllowed returns if wildcard names
86+
// like *.example.com are allowed to be signed.
87+
// Defaults to false.
88+
func (p *Policy) AreWildcardNamesAllowed() bool {
89+
if p == nil {
90+
return false
91+
}
92+
return p.X509.AllowWildcardNames
93+
}
94+
4695
// ExternalAccountKey is an ACME External Account Binding key.
4796
type ExternalAccountKey struct {
4897
ID string `json:"id"`
4998
ProvisionerID string `json:"provisionerID"`
5099
Reference string `json:"reference"`
51100
AccountID string `json:"-"`
52-
KeyBytes []byte `json:"-"`
101+
HmacKey []byte `json:"-"`
53102
CreatedAt time.Time `json:"createdAt"`
54103
BoundAt time.Time `json:"boundAt,omitempty"`
104+
Policy *Policy `json:"policy,omitempty"`
55105
}
56106

57107
// AlreadyBound returns whether this EAK is already bound to
@@ -68,6 +118,6 @@ func (eak *ExternalAccountKey) BindTo(account *Account) error {
68118
}
69119
eak.AccountID = account.ID
70120
eak.BoundAt = time.Now()
71-
eak.KeyBytes = []byte{} // clearing the key bytes; can only be used once
121+
eak.HmacKey = []byte{} // clearing the key bytes; can only be used once
72122
return nil
73123
}

acme/account_test.go

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,9 @@ import (
77
"time"
88

99
"github.com/pkg/errors"
10-
"github.com/smallstep/assert"
1110
"go.step.sm/crypto/jose"
11+
12+
"github.com/smallstep/assert"
1213
)
1314

1415
func TestKeyToID(t *testing.T) {
@@ -95,7 +96,7 @@ func TestExternalAccountKey_BindTo(t *testing.T) {
9596
ID: "eakID",
9697
ProvisionerID: "provID",
9798
Reference: "ref",
98-
KeyBytes: []byte{1, 3, 3, 7},
99+
HmacKey: []byte{1, 3, 3, 7},
99100
},
100101
acct: &Account{
101102
ID: "accountID",
@@ -108,7 +109,7 @@ func TestExternalAccountKey_BindTo(t *testing.T) {
108109
ID: "eakID",
109110
ProvisionerID: "provID",
110111
Reference: "ref",
111-
KeyBytes: []byte{1, 3, 3, 7},
112+
HmacKey: []byte{1, 3, 3, 7},
112113
AccountID: "someAccountID",
113114
BoundAt: boundAt,
114115
},
@@ -138,7 +139,7 @@ func TestExternalAccountKey_BindTo(t *testing.T) {
138139
assert.Equals(t, ae.Subproblems, tt.err.Subproblems)
139140
} else {
140141
assert.Equals(t, eak.AccountID, acct.ID)
141-
assert.Equals(t, eak.KeyBytes, []byte{})
142+
assert.Equals(t, eak.HmacKey, []byte{})
142143
assert.NotNil(t, eak.BoundAt)
143144
}
144145
})

acme/api/account.go

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -131,8 +131,7 @@ func (h *Handler) NewAccount(w http.ResponseWriter, r *http.Request) {
131131
}
132132

133133
if eak != nil { // means that we have a (valid) External Account Binding key that should be bound, updated and sent in the response
134-
err := eak.BindTo(acc)
135-
if err != nil {
134+
if err := eak.BindTo(acc); err != nil {
136135
render.Error(w, err)
137136
return
138137
}

acme/api/account_test.go

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,12 @@ import (
1313

1414
"github.com/go-chi/chi"
1515
"github.com/pkg/errors"
16+
17+
"go.step.sm/crypto/jose"
18+
1619
"github.com/smallstep/assert"
1720
"github.com/smallstep/certificates/acme"
1821
"github.com/smallstep/certificates/authority/provisioner"
19-
"go.step.sm/crypto/jose"
2022
)
2123

2224
var (
@@ -41,6 +43,19 @@ func newProv() acme.Provisioner {
4143
return p
4244
}
4345

46+
func newProvWithOptions(options *provisioner.Options) acme.Provisioner {
47+
// Initialize provisioners
48+
p := &provisioner.ACME{
49+
Type: "ACME",
50+
Name: "test@acme-<test>provisioner.com",
51+
Options: options,
52+
}
53+
if err := p.Init(provisioner.Config{Claims: globalProvisionerClaims}); err != nil {
54+
fmt.Printf("%v", err)
55+
}
56+
return p
57+
}
58+
4459
func newACMEProv(t *testing.T) *provisioner.ACME {
4560
p := newProv()
4661
a, ok := p.(*provisioner.ACME)
@@ -50,6 +65,15 @@ func newACMEProv(t *testing.T) *provisioner.ACME {
5065
return a
5166
}
5267

68+
func newACMEProvWithOptions(t *testing.T, options *provisioner.Options) *provisioner.ACME {
69+
p := newProvWithOptions(options)
70+
a, ok := p.(*provisioner.ACME)
71+
if !ok {
72+
t.Fatal("not a valid ACME provisioner")
73+
}
74+
return a
75+
}
76+
5377
func createEABJWS(jwk *jose.JSONWebKey, hmacKey []byte, keyID, u string) (*jose.JSONWebSignature, error) {
5478
signer, err := jose.NewSigner(
5579
jose.SigningKey{
@@ -558,7 +582,7 @@ func TestHandler_NewAccount(t *testing.T) {
558582
ID: "eakID",
559583
ProvisionerID: provID,
560584
Reference: "testeak",
561-
KeyBytes: []byte{1, 3, 3, 7},
585+
HmacKey: []byte{1, 3, 3, 7},
562586
CreatedAt: time.Now(),
563587
}
564588
return test{
@@ -735,7 +759,7 @@ func TestHandler_NewAccount(t *testing.T) {
735759
ID: "eakID",
736760
ProvisionerID: provID,
737761
Reference: "testeak",
738-
KeyBytes: []byte{1, 3, 3, 7},
762+
HmacKey: []byte{1, 3, 3, 7},
739763
CreatedAt: time.Now(),
740764
}, nil
741765
},

acme/api/eab.go

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,9 @@ import (
44
"context"
55
"encoding/json"
66

7-
"github.com/smallstep/certificates/acme"
87
"go.step.sm/crypto/jose"
8+
9+
"github.com/smallstep/certificates/acme"
910
)
1011

1112
// ExternalAccountBinding represents the ACME externalAccountBinding JWS
@@ -55,11 +56,19 @@ func (h *Handler) validateExternalAccountBinding(ctx context.Context, nar *NewAc
5556
return nil, acme.WrapErrorISE(err, "error retrieving external account key")
5657
}
5758

59+
if externalAccountKey == nil {
60+
return nil, acme.NewError(acme.ErrorUnauthorizedType, "the field 'kid' references an unknown key")
61+
}
62+
63+
if len(externalAccountKey.HmacKey) == 0 {
64+
return nil, acme.NewError(acme.ErrorServerInternalType, "external account binding key with id '%s' does not have secret bytes", keyID)
65+
}
66+
5867
if externalAccountKey.AlreadyBound() {
5968
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)
6069
}
6170

62-
payload, err := eabJWS.Verify(externalAccountKey.KeyBytes)
71+
payload, err := eabJWS.Verify(externalAccountKey.HmacKey)
6372
if err != nil {
6473
return nil, acme.WrapErrorISE(err, "error verifying externalAccountBinding signature")
6574
}

0 commit comments

Comments
 (0)