Skip to content

Commit 6e1f8dd

Browse files
committed
Refactor policy engines into container
1 parent 2a76206 commit 6e1f8dd

16 files changed

+485
-473
lines changed

Diff for: acme/api/order.go

+1-9
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ import (
55
"crypto/x509"
66
"encoding/base64"
77
"encoding/json"
8-
"fmt"
98
"net"
109
"net/http"
1110
"strings"
@@ -199,14 +198,7 @@ func isIdentifierAllowed(acmePolicy policy.X509Policy, identifier acme.Identifie
199198
if acmePolicy == nil {
200199
return nil
201200
}
202-
allowed, err := acmePolicy.AreSANsAllowed([]string{identifier.Value})
203-
if err != nil {
204-
return err
205-
}
206-
if !allowed {
207-
return fmt.Errorf("acme identifier '%s' not allowed", identifier.Value)
208-
}
209-
return nil
201+
return acmePolicy.AreSANsAllowed([]string{identifier.Value})
210202
}
211203

212204
func newACMEPolicyEngine(eak *acme.ExternalAccountKey) (policy.X509Policy, error) {

Diff for: authority/authority.go

+1-3
Original file line numberDiff line numberDiff line change
@@ -81,9 +81,7 @@ type Authority struct {
8181
authorizeSSHRenewFunc provisioner.AuthorizeSSHRenewFunc
8282

8383
// Policy engines
84-
x509Policy policy.X509Policy
85-
sshUserPolicy policy.UserPolicy
86-
sshHostPolicy policy.HostPolicy
84+
policyEngine *policy.Engine
8785

8886
adminMutex sync.RWMutex
8987
}

Diff for: authority/policy.go

+5-43
Original file line numberDiff line numberDiff line change
@@ -227,50 +227,19 @@ func (a *Authority) reloadPolicyEngines(ctx context.Context) error {
227227
policyOptions = a.config.AuthorityConfig.Policy
228228
}
229229

230-
// if no new or updated policy option is set, clear policy engines that (may have)
231-
// been configured before and return early
232-
if policyOptions == nil {
233-
a.x509Policy = nil
234-
a.sshHostPolicy = nil
235-
a.sshUserPolicy = nil
236-
return nil
237-
}
238-
239-
var (
240-
x509Policy authPolicy.X509Policy
241-
sshHostPolicy authPolicy.HostPolicy
242-
sshUserPolicy authPolicy.UserPolicy
243-
)
244-
245-
// initialize the x509 allow/deny policy engine
246-
if x509Policy, err = authPolicy.NewX509PolicyEngine(policyOptions.GetX509Options()); err != nil {
247-
return err
248-
}
249-
250-
// initialize the SSH allow/deny policy engine for host certificates
251-
if sshHostPolicy, err = authPolicy.NewSSHHostPolicyEngine(policyOptions.GetSSHOptions()); err != nil {
252-
return err
253-
}
254-
255-
// initialize the SSH allow/deny policy engine for user certificates
256-
if sshUserPolicy, err = authPolicy.NewSSHUserPolicyEngine(policyOptions.GetSSHOptions()); err != nil {
230+
engine, err := authPolicy.New(policyOptions)
231+
if err != nil {
257232
return err
258233
}
259234

260-
// set all policy engines; all or nothing
261-
a.x509Policy = x509Policy
262-
a.sshHostPolicy = sshHostPolicy
263-
a.sshUserPolicy = sshUserPolicy
235+
// only update the policy engine when no error was returned
236+
a.policyEngine = engine
264237

265238
return nil
266239
}
267240

268241
func isAllowed(engine authPolicy.X509Policy, sans []string) error {
269-
var (
270-
allowed bool
271-
err error
272-
)
273-
if allowed, err = engine.AreSANsAllowed(sans); err != nil {
242+
if err := engine.AreSANsAllowed(sans); err != nil {
274243
var policyErr *policy.NamePolicyError
275244
isNamePolicyError := errors.As(err, &policyErr)
276245
if isNamePolicyError && policyErr.Reason == policy.NotAllowed {
@@ -285,13 +254,6 @@ func isAllowed(engine authPolicy.X509Policy, sans []string) error {
285254
}
286255
}
287256

288-
if !allowed {
289-
return &PolicyError{
290-
Typ: AdminLockOut,
291-
Err: fmt.Errorf("the provided policy would lock out %s from the CA. Please update your policy to include %s as an allowed name", sans, sans),
292-
}
293-
}
294-
295257
return nil
296258
}
297259

Diff for: authority/policy/engine.go

+114
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
package policy
2+
3+
import (
4+
"crypto/x509"
5+
"errors"
6+
"fmt"
7+
8+
"golang.org/x/crypto/ssh"
9+
)
10+
11+
// Engine is a container for multiple policies.
12+
type Engine struct {
13+
x509Policy X509Policy
14+
sshUserPolicy UserPolicy
15+
sshHostPolicy HostPolicy
16+
}
17+
18+
// New returns a new Engine using Options.
19+
func New(options *Options) (*Engine, error) {
20+
21+
// if no options provided, return early
22+
if options == nil {
23+
return nil, nil
24+
}
25+
26+
var (
27+
x509Policy X509Policy
28+
sshHostPolicy HostPolicy
29+
sshUserPolicy UserPolicy
30+
err error
31+
)
32+
33+
// initialize the x509 allow/deny policy engine
34+
if x509Policy, err = NewX509PolicyEngine(options.GetX509Options()); err != nil {
35+
return nil, err
36+
}
37+
38+
// initialize the SSH allow/deny policy engine for host certificates
39+
if sshHostPolicy, err = NewSSHHostPolicyEngine(options.GetSSHOptions()); err != nil {
40+
return nil, err
41+
}
42+
43+
// initialize the SSH allow/deny policy engine for user certificates
44+
if sshUserPolicy, err = NewSSHUserPolicyEngine(options.GetSSHOptions()); err != nil {
45+
return nil, err
46+
}
47+
48+
return &Engine{
49+
x509Policy: x509Policy,
50+
sshHostPolicy: sshHostPolicy,
51+
sshUserPolicy: sshUserPolicy,
52+
}, nil
53+
}
54+
55+
// IsX509CertificateAllowed evaluates an X.509 certificate against
56+
// the X.509 policy (if available) and returns an error if one of the
57+
// names in the certificate is not allowed.
58+
func (e *Engine) IsX509CertificateAllowed(cert *x509.Certificate) error {
59+
60+
// return early if there's no policy to evaluate
61+
if e == nil || e.x509Policy == nil {
62+
return nil
63+
}
64+
65+
// return result of X.509 policy evaluation
66+
return e.x509Policy.IsX509CertificateAllowed(cert)
67+
}
68+
69+
// AreSANsAllowed evaluates the slice of SANs against the X.509 policy
70+
// (if available) and returns an error if one of the SANs is not allowed.
71+
func (e *Engine) AreSANsAllowed(sans []string) error {
72+
73+
// return early if there's no policy to evaluate
74+
if e == nil || e.x509Policy == nil {
75+
return nil
76+
}
77+
78+
// return result of X.509 policy evaluation
79+
return e.x509Policy.AreSANsAllowed(sans)
80+
}
81+
82+
// IsSSHCertificateAllowed evaluates an SSH certificate against the
83+
// user or host policy (if configured) and returns an error if one of the
84+
// principals in the certificate is not allowed.
85+
func (e *Engine) IsSSHCertificateAllowed(cert *ssh.Certificate) error {
86+
87+
// return early if there's no policy to evaluate
88+
if e == nil || (e.sshHostPolicy == nil && e.sshUserPolicy == nil) {
89+
return nil
90+
}
91+
92+
switch cert.CertType {
93+
case ssh.HostCert:
94+
// when no host policy engine is configured, but a user policy engine is
95+
// configured, the host certificate is denied.
96+
if e.sshHostPolicy == nil && e.sshUserPolicy != nil {
97+
return errors.New("authority not allowed to sign ssh host certificates")
98+
}
99+
100+
// return result of SSH host policy evaluation
101+
return e.sshHostPolicy.IsSSHCertificateAllowed(cert)
102+
case ssh.UserCert:
103+
// when no user policy engine is configured, but a host policy engine is
104+
// configured, the user certificate is denied.
105+
if e.sshUserPolicy == nil && e.sshHostPolicy != nil {
106+
return errors.New("authority not allowed to sign ssh user certificates")
107+
}
108+
109+
// return result of SSH user policy evaluation
110+
return e.sshUserPolicy.IsSSHCertificateAllowed(cert)
111+
default:
112+
return fmt.Errorf("unexpected ssh certificate type %q", cert.CertType)
113+
}
114+
}

0 commit comments

Comments
 (0)