mirror of https://github.com/status-im/consul.git
Add workload identity ACL rules (#18769)
This commit is contained in:
parent
a55c4a1c25
commit
d090668c37
|
@ -0,0 +1,3 @@
|
|||
```release-note:feature
|
||||
acl: Adds a new ACL rule for workload identities
|
||||
```
|
|
@ -59,6 +59,31 @@ func (m *MockAuthorizer) EventWrite(segment string, ctx *AuthorizerContext) Enfo
|
|||
return ret.Get(0).(EnforcementDecision)
|
||||
}
|
||||
|
||||
// IdentityRead checks for permission to read a given workload identity.
|
||||
func (m *MockAuthorizer) IdentityRead(segment string, ctx *AuthorizerContext) EnforcementDecision {
|
||||
ret := m.Called(segment, ctx)
|
||||
return ret.Get(0).(EnforcementDecision)
|
||||
}
|
||||
|
||||
// IdentityReadAll checks for permission to read all workload identities.
|
||||
func (m *MockAuthorizer) IdentityReadAll(ctx *AuthorizerContext) EnforcementDecision {
|
||||
ret := m.Called(ctx)
|
||||
return ret.Get(0).(EnforcementDecision)
|
||||
}
|
||||
|
||||
// IdentityWrite checks for permission to create or update a given
|
||||
// workload identity.
|
||||
func (m *MockAuthorizer) IdentityWrite(segment string, ctx *AuthorizerContext) EnforcementDecision {
|
||||
ret := m.Called(segment, ctx)
|
||||
return ret.Get(0).(EnforcementDecision)
|
||||
}
|
||||
|
||||
// IdentityWriteAny checks for write permission on any workload identity.
|
||||
func (m *MockAuthorizer) IdentityWriteAny(ctx *AuthorizerContext) EnforcementDecision {
|
||||
ret := m.Called(ctx)
|
||||
return ret.Get(0).(EnforcementDecision)
|
||||
}
|
||||
|
||||
// IntentionDefaultAllow determines the default authorized behavior
|
||||
// when no intentions match a Connect request.
|
||||
func (m *MockAuthorizer) IntentionDefaultAllow(ctx *AuthorizerContext) EnforcementDecision {
|
||||
|
|
191
acl/acl_test.go
191
acl/acl_test.go
|
@ -40,6 +40,22 @@ func checkAllowEventWrite(t *testing.T, authz Authorizer, prefix string, entCtx
|
|||
require.Equal(t, Allow, authz.EventWrite(prefix, entCtx))
|
||||
}
|
||||
|
||||
func checkAllowIdentityRead(t *testing.T, authz Authorizer, prefix string, entCtx *AuthorizerContext) {
|
||||
require.Equal(t, Allow, authz.IdentityRead(prefix, entCtx))
|
||||
}
|
||||
|
||||
func checkAllowIdentityReadAll(t *testing.T, authz Authorizer, _ string, entCtx *AuthorizerContext) {
|
||||
require.Equal(t, Allow, authz.IdentityReadAll(entCtx))
|
||||
}
|
||||
|
||||
func checkAllowIdentityWrite(t *testing.T, authz Authorizer, prefix string, entCtx *AuthorizerContext) {
|
||||
require.Equal(t, Allow, authz.IdentityWrite(prefix, entCtx))
|
||||
}
|
||||
|
||||
func checkAllowIdentityWriteAny(t *testing.T, authz Authorizer, _ string, entCtx *AuthorizerContext) {
|
||||
require.Equal(t, Allow, authz.IdentityWriteAny(entCtx))
|
||||
}
|
||||
|
||||
func checkAllowIntentionDefaultAllow(t *testing.T, authz Authorizer, prefix string, entCtx *AuthorizerContext) {
|
||||
require.Equal(t, Allow, authz.IntentionDefaultAllow(entCtx))
|
||||
}
|
||||
|
@ -172,6 +188,22 @@ func checkDenyEventWrite(t *testing.T, authz Authorizer, prefix string, entCtx *
|
|||
require.Equal(t, Deny, authz.EventWrite(prefix, entCtx))
|
||||
}
|
||||
|
||||
func checkDenyIdentityRead(t *testing.T, authz Authorizer, prefix string, entCtx *AuthorizerContext) {
|
||||
require.Equal(t, Deny, authz.IdentityRead(prefix, entCtx))
|
||||
}
|
||||
|
||||
func checkDenyIdentityReadAll(t *testing.T, authz Authorizer, _ string, entCtx *AuthorizerContext) {
|
||||
require.Equal(t, Deny, authz.IdentityReadAll(entCtx))
|
||||
}
|
||||
|
||||
func checkDenyIdentityWrite(t *testing.T, authz Authorizer, prefix string, entCtx *AuthorizerContext) {
|
||||
require.Equal(t, Deny, authz.IdentityWrite(prefix, entCtx))
|
||||
}
|
||||
|
||||
func checkDenyIdentityWriteAny(t *testing.T, authz Authorizer, _ string, entCtx *AuthorizerContext) {
|
||||
require.Equal(t, Deny, authz.IdentityWriteAny(entCtx))
|
||||
}
|
||||
|
||||
func checkDenyIntentionDefaultAllow(t *testing.T, authz Authorizer, prefix string, entCtx *AuthorizerContext) {
|
||||
require.Equal(t, Deny, authz.IntentionDefaultAllow(entCtx))
|
||||
}
|
||||
|
@ -304,6 +336,22 @@ func checkDefaultEventWrite(t *testing.T, authz Authorizer, prefix string, entCt
|
|||
require.Equal(t, Default, authz.EventWrite(prefix, entCtx))
|
||||
}
|
||||
|
||||
func checkDefaultIdentityRead(t *testing.T, authz Authorizer, prefix string, entCtx *AuthorizerContext) {
|
||||
require.Equal(t, Default, authz.IdentityRead(prefix, entCtx))
|
||||
}
|
||||
|
||||
func checkDefaultIdentityReadAll(t *testing.T, authz Authorizer, _ string, entCtx *AuthorizerContext) {
|
||||
require.Equal(t, Default, authz.IdentityReadAll(entCtx))
|
||||
}
|
||||
|
||||
func checkDefaultIdentityWrite(t *testing.T, authz Authorizer, prefix string, entCtx *AuthorizerContext) {
|
||||
require.Equal(t, Default, authz.IdentityWrite(prefix, entCtx))
|
||||
}
|
||||
|
||||
func checkDefaultIdentityWriteAny(t *testing.T, authz Authorizer, _ string, entCtx *AuthorizerContext) {
|
||||
require.Equal(t, Default, authz.IdentityWriteAny(entCtx))
|
||||
}
|
||||
|
||||
func checkDefaultIntentionDefaultAllow(t *testing.T, authz Authorizer, prefix string, entCtx *AuthorizerContext) {
|
||||
require.Equal(t, Default, authz.IntentionDefaultAllow(entCtx))
|
||||
}
|
||||
|
@ -440,6 +488,10 @@ func TestACL(t *testing.T) {
|
|||
{name: "DenyIntentionDefaultAllow", check: checkDenyIntentionDefaultAllow},
|
||||
{name: "DenyIntentionRead", check: checkDenyIntentionRead},
|
||||
{name: "DenyIntentionWrite", check: checkDenyIntentionWrite},
|
||||
{name: "DenyIdentityRead", check: checkDenyIdentityRead},
|
||||
{name: "DenyIdentityReadAll", check: checkDenyIdentityReadAll},
|
||||
{name: "DenyIdentityWrite", check: checkDenyIdentityWrite},
|
||||
{name: "DenyIdentityWriteAny", check: checkDenyIdentityWriteAny},
|
||||
{name: "DenyKeyRead", check: checkDenyKeyRead},
|
||||
{name: "DenyKeyringRead", check: checkDenyKeyringRead},
|
||||
{name: "DenyKeyringWrite", check: checkDenyKeyringWrite},
|
||||
|
@ -458,6 +510,7 @@ func TestACL(t *testing.T) {
|
|||
{name: "DenyServiceRead", check: checkDenyServiceRead},
|
||||
{name: "DenyServiceReadAll", check: checkDenyServiceReadAll},
|
||||
{name: "DenyServiceWrite", check: checkDenyServiceWrite},
|
||||
{name: "DenyServiceWriteAny", check: checkDenyServiceWriteAny},
|
||||
{name: "DenySessionRead", check: checkDenySessionRead},
|
||||
{name: "DenySessionWrite", check: checkDenySessionWrite},
|
||||
{name: "DenySnapshot", check: checkDenySnapshot},
|
||||
|
@ -473,6 +526,10 @@ func TestACL(t *testing.T) {
|
|||
{name: "AllowAgentWrite", check: checkAllowAgentWrite},
|
||||
{name: "AllowEventRead", check: checkAllowEventRead},
|
||||
{name: "AllowEventWrite", check: checkAllowEventWrite},
|
||||
{name: "AllowIdentityRead", check: checkAllowIdentityRead},
|
||||
{name: "AllowIdentityReadAll", check: checkAllowIdentityReadAll},
|
||||
{name: "AllowIdentityWrite", check: checkAllowIdentityWrite},
|
||||
{name: "AllowIdentityWriteAny", check: checkAllowIdentityWriteAny},
|
||||
{name: "AllowIntentionDefaultAllow", check: checkAllowIntentionDefaultAllow},
|
||||
{name: "AllowIntentionRead", check: checkAllowIntentionRead},
|
||||
{name: "AllowIntentionWrite", check: checkAllowIntentionWrite},
|
||||
|
@ -494,6 +551,7 @@ func TestACL(t *testing.T) {
|
|||
{name: "AllowServiceRead", check: checkAllowServiceRead},
|
||||
{name: "AllowServiceReadAll", check: checkAllowServiceReadAll},
|
||||
{name: "AllowServiceWrite", check: checkAllowServiceWrite},
|
||||
{name: "AllowServiceWriteAny", check: checkAllowServiceWriteAny},
|
||||
{name: "AllowSessionRead", check: checkAllowSessionRead},
|
||||
{name: "AllowSessionWrite", check: checkAllowSessionWrite},
|
||||
{name: "DenySnapshot", check: checkDenySnapshot},
|
||||
|
@ -509,6 +567,10 @@ func TestACL(t *testing.T) {
|
|||
{name: "AllowAgentWrite", check: checkAllowAgentWrite},
|
||||
{name: "AllowEventRead", check: checkAllowEventRead},
|
||||
{name: "AllowEventWrite", check: checkAllowEventWrite},
|
||||
{name: "AllowIdentityRead", check: checkAllowIdentityRead},
|
||||
{name: "AllowIdentityReadAll", check: checkAllowIdentityReadAll},
|
||||
{name: "AllowIdentityWrite", check: checkAllowIdentityWrite},
|
||||
{name: "AllowIdentityWriteAny", check: checkAllowIdentityWriteAny},
|
||||
{name: "AllowIntentionDefaultAllow", check: checkAllowIntentionDefaultAllow},
|
||||
{name: "AllowIntentionRead", check: checkAllowIntentionRead},
|
||||
{name: "AllowIntentionWrite", check: checkAllowIntentionWrite},
|
||||
|
@ -530,6 +592,7 @@ func TestACL(t *testing.T) {
|
|||
{name: "AllowServiceRead", check: checkAllowServiceRead},
|
||||
{name: "AllowServiceReadAll", check: checkAllowServiceReadAll},
|
||||
{name: "AllowServiceWrite", check: checkAllowServiceWrite},
|
||||
{name: "AllowServiceWriteAny", check: checkAllowServiceWriteAny},
|
||||
{name: "AllowSessionRead", check: checkAllowSessionRead},
|
||||
{name: "AllowSessionWrite", check: checkAllowSessionWrite},
|
||||
{name: "AllowSnapshot", check: checkAllowSnapshot},
|
||||
|
@ -905,6 +968,134 @@ func TestACL(t *testing.T) {
|
|||
{name: "ChildOverrideWriteAllowed", prefix: "override", check: checkAllowAgentWrite},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "IdentityDefaultAllowPolicyDeny",
|
||||
defaultPolicy: AllowAll(),
|
||||
policyStack: []*Policy{
|
||||
{
|
||||
PolicyRules: PolicyRules{
|
||||
Identities: []*IdentityRule{
|
||||
{
|
||||
Name: "foo",
|
||||
Policy: PolicyDeny,
|
||||
},
|
||||
},
|
||||
IdentityPrefixes: []*IdentityRule{
|
||||
{
|
||||
Name: "prefix",
|
||||
Policy: PolicyDeny,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
checks: []aclCheck{
|
||||
{name: "IdentityFooReadDenied", prefix: "foo", check: checkDenyIdentityRead},
|
||||
{name: "IdentityFooWriteDenied", prefix: "foo", check: checkDenyIdentityWrite},
|
||||
{name: "IdentityPrefixReadDenied", prefix: "prefix", check: checkDenyIdentityRead},
|
||||
{name: "IdentityPrefixWriteDenied", prefix: "prefix", check: checkDenyIdentityWrite},
|
||||
{name: "IdentityBarReadAllowed", prefix: "fail", check: checkAllowIdentityRead},
|
||||
{name: "IdentityBarWriteAllowed", prefix: "fail", check: checkAllowIdentityWrite},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "IdentityDefaultDenyPolicyAllow",
|
||||
defaultPolicy: DenyAll(),
|
||||
policyStack: []*Policy{
|
||||
{
|
||||
PolicyRules: PolicyRules{
|
||||
Identities: []*IdentityRule{
|
||||
{
|
||||
Name: "foo",
|
||||
Policy: PolicyWrite,
|
||||
},
|
||||
},
|
||||
IdentityPrefixes: []*IdentityRule{
|
||||
{
|
||||
Name: "prefix",
|
||||
Policy: PolicyRead,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
checks: []aclCheck{
|
||||
{name: "IdentityFooReadAllowed", prefix: "foo", check: checkAllowIdentityRead},
|
||||
{name: "IdentityFooWriteAllowed", prefix: "foo", check: checkAllowIdentityWrite},
|
||||
{name: "IdentityPrefixReadAllowed", prefix: "prefix", check: checkAllowIdentityRead},
|
||||
{name: "IdentityPrefixWriteDenied", prefix: "prefix", check: checkDenyIdentityWrite},
|
||||
{name: "IdentityBarReadDenied", prefix: "fail", check: checkDenyIdentityRead},
|
||||
{name: "IdentityBarWriteDenied", prefix: "fail", check: checkDenyIdentityWrite},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "IdentityDefaultDenyPolicyComplex",
|
||||
defaultPolicy: DenyAll(),
|
||||
policyStack: []*Policy{
|
||||
{
|
||||
PolicyRules: PolicyRules{
|
||||
Identities: []*IdentityRule{
|
||||
{
|
||||
Name: "football",
|
||||
Policy: PolicyRead,
|
||||
},
|
||||
{
|
||||
Name: "prefix-forbidden",
|
||||
Policy: PolicyDeny,
|
||||
Intentions: PolicyDeny,
|
||||
},
|
||||
},
|
||||
IdentityPrefixes: []*IdentityRule{
|
||||
{
|
||||
Name: "foo",
|
||||
Policy: PolicyWrite,
|
||||
Intentions: PolicyWrite,
|
||||
},
|
||||
{
|
||||
Name: "prefix",
|
||||
Policy: PolicyRead,
|
||||
Intentions: PolicyWrite,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
PolicyRules: PolicyRules{
|
||||
Identities: []*IdentityRule{
|
||||
{
|
||||
Name: "foozball",
|
||||
Policy: PolicyWrite,
|
||||
Intentions: PolicyRead,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
checks: []aclCheck{
|
||||
{name: "IdentityReadAllowed", prefix: "foo", check: checkAllowIdentityRead},
|
||||
{name: "IdentityWriteAllowed", prefix: "foo", check: checkAllowIdentityWrite},
|
||||
{name: "IntentionReadAllowed", prefix: "foo", check: checkAllowIntentionRead},
|
||||
{name: "IntentionWriteAllowed", prefix: "foo", check: checkAllowIntentionWrite},
|
||||
{name: "IdentityReadAllowed", prefix: "football", check: checkAllowIdentityRead},
|
||||
{name: "IdentityWriteDenied", prefix: "football", check: checkDenyIdentityWrite},
|
||||
{name: "IntentionReadAllowed", prefix: "football", check: checkAllowIntentionRead},
|
||||
// This might be surprising but omitting intention rule gives at most intention:read
|
||||
// if we have identity:write perms. This matches services as well.
|
||||
{name: "IntentionWriteDenied", prefix: "football", check: checkDenyIntentionWrite},
|
||||
{name: "IdentityReadAllowed", prefix: "prefix", check: checkAllowIdentityRead},
|
||||
{name: "IdentityWriteDenied", prefix: "prefix", check: checkDenyIdentityWrite},
|
||||
{name: "IntentionReadAllowed", prefix: "prefix", check: checkAllowIntentionRead},
|
||||
{name: "IntentionWriteDenied", prefix: "prefix", check: checkAllowIntentionWrite},
|
||||
{name: "IdentityReadDenied", prefix: "prefix-forbidden", check: checkDenyIdentityRead},
|
||||
{name: "IdentityWriteDenied", prefix: "prefix-forbidden", check: checkDenyIdentityWrite},
|
||||
{name: "IntentionReadDenied", prefix: "prefix-forbidden", check: checkDenyIntentionRead},
|
||||
{name: "IntentionWriteDenied", prefix: "prefix-forbidden", check: checkDenyIntentionWrite},
|
||||
{name: "IdentityReadAllowed", prefix: "foozball", check: checkAllowIdentityRead},
|
||||
{name: "IdentityWriteAllowed", prefix: "foozball", check: checkAllowIdentityWrite},
|
||||
{name: "IntentionReadAllowed", prefix: "foozball", check: checkAllowIntentionRead},
|
||||
{name: "IntentionWriteDenied", prefix: "foozball", check: checkDenyIntentionWrite},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "KeyringDefaultAllowPolicyDeny",
|
||||
defaultPolicy: AllowAll(),
|
||||
|
|
|
@ -43,6 +43,7 @@ const (
|
|||
ResourceACL Resource = "acl"
|
||||
ResourceAgent Resource = "agent"
|
||||
ResourceEvent Resource = "event"
|
||||
ResourceIdentity Resource = "identity"
|
||||
ResourceIntention Resource = "intention"
|
||||
ResourceKey Resource = "key"
|
||||
ResourceKeyring Resource = "keyring"
|
||||
|
@ -77,6 +78,19 @@ type Authorizer interface {
|
|||
// EventWrite determines if a specific event may be fired.
|
||||
EventWrite(string, *AuthorizerContext) EnforcementDecision
|
||||
|
||||
// IdentityRead checks for permission to read a given workload identity.
|
||||
IdentityRead(string, *AuthorizerContext) EnforcementDecision
|
||||
|
||||
// IdentityReadAll checks for permission to read all workload identities.
|
||||
IdentityReadAll(*AuthorizerContext) EnforcementDecision
|
||||
|
||||
// IdentityWrite checks for permission to create or update a given
|
||||
// workload identity.
|
||||
IdentityWrite(string, *AuthorizerContext) EnforcementDecision
|
||||
|
||||
// IdentityWriteAny checks for write permission on any workload identity.
|
||||
IdentityWriteAny(*AuthorizerContext) EnforcementDecision
|
||||
|
||||
// IntentionDefaultAllow determines the default authorized behavior
|
||||
// when no intentions match a Connect request.
|
||||
IntentionDefaultAllow(*AuthorizerContext) EnforcementDecision
|
||||
|
@ -239,6 +253,40 @@ func (a AllowAuthorizer) EventWriteAllowed(name string, ctx *AuthorizerContext)
|
|||
return nil
|
||||
}
|
||||
|
||||
// IdentityReadAllowed checks for permission to read a given workload identity,
|
||||
func (a AllowAuthorizer) IdentityReadAllowed(name string, ctx *AuthorizerContext) error {
|
||||
if a.Authorizer.IdentityRead(name, ctx) != Allow {
|
||||
return PermissionDeniedByACL(a, ctx, ResourceIdentity, AccessRead, name)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// IdentityReadAllAllowed checks for permission to read all workload identities.
|
||||
func (a AllowAuthorizer) IdentityReadAllAllowed(ctx *AuthorizerContext) error {
|
||||
if a.Authorizer.IdentityReadAll(ctx) != Allow {
|
||||
// This is only used to gate certain UI functions right now (e.g metrics)
|
||||
return PermissionDeniedByACL(a, ctx, ResourceIdentity, AccessRead, "all identities") // read
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// IdentityWriteAllowed checks for permission to create or update a given
|
||||
// workload identity.
|
||||
func (a AllowAuthorizer) IdentityWriteAllowed(name string, ctx *AuthorizerContext) error {
|
||||
if a.Authorizer.IdentityWrite(name, ctx) != Allow {
|
||||
return PermissionDeniedByACL(a, ctx, ResourceIdentity, AccessWrite, name)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// IdentityWriteAnyAllowed checks for write permission on any workload identity
|
||||
func (a AllowAuthorizer) IdentityWriteAnyAllowed(ctx *AuthorizerContext) error {
|
||||
if a.Authorizer.IdentityWriteAny(ctx) != Allow {
|
||||
return PermissionDeniedByACL(a, ctx, ResourceIdentity, AccessWrite, "any identity")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// IntentionDefaultAllowAllowed determines the default authorized behavior
|
||||
// when no intentions match a Connect request.
|
||||
func (a AllowAuthorizer) IntentionDefaultAllowAllowed(ctx *AuthorizerContext) error {
|
||||
|
@ -503,6 +551,13 @@ func Enforce(authz Authorizer, rsc Resource, segment string, access string, ctx
|
|||
case "write":
|
||||
return authz.EventWrite(segment, ctx), nil
|
||||
}
|
||||
case ResourceIdentity:
|
||||
switch lowerAccess {
|
||||
case "read":
|
||||
return authz.IdentityRead(segment, ctx), nil
|
||||
case "write":
|
||||
return authz.IdentityWrite(segment, ctx), nil
|
||||
}
|
||||
case ResourceIntention:
|
||||
switch lowerAccess {
|
||||
case "read":
|
||||
|
|
|
@ -188,6 +188,34 @@ func TestACL_Enforce(t *testing.T) {
|
|||
ret: Deny,
|
||||
err: "Invalid access level",
|
||||
},
|
||||
{
|
||||
method: "IdentityRead",
|
||||
resource: ResourceIdentity,
|
||||
segment: "foo",
|
||||
access: "read",
|
||||
ret: Deny,
|
||||
},
|
||||
{
|
||||
method: "IdentityRead",
|
||||
resource: ResourceIdentity,
|
||||
segment: "foo",
|
||||
access: "read",
|
||||
ret: Allow,
|
||||
},
|
||||
{
|
||||
method: "IdentityWrite",
|
||||
resource: ResourceIdentity,
|
||||
segment: "foo",
|
||||
access: "write",
|
||||
ret: Deny,
|
||||
},
|
||||
{
|
||||
method: "IdentityWrite",
|
||||
resource: ResourceIdentity,
|
||||
segment: "foo",
|
||||
access: "write",
|
||||
ret: Allow,
|
||||
},
|
||||
{
|
||||
method: "IntentionRead",
|
||||
resource: ResourceIntention,
|
||||
|
|
|
@ -80,6 +80,35 @@ func (c *ChainedAuthorizer) EventWrite(name string, entCtx *AuthorizerContext) E
|
|||
})
|
||||
}
|
||||
|
||||
// IdentityRead checks for permission to read a given workload identity.
|
||||
func (c *ChainedAuthorizer) IdentityRead(name string, entCtx *AuthorizerContext) EnforcementDecision {
|
||||
return c.executeChain(func(authz Authorizer) EnforcementDecision {
|
||||
return authz.IdentityRead(name, entCtx)
|
||||
})
|
||||
}
|
||||
|
||||
// IdentityReadAll checks for permission to read all workload identities.
|
||||
func (c *ChainedAuthorizer) IdentityReadAll(entCtx *AuthorizerContext) EnforcementDecision {
|
||||
return c.executeChain(func(authz Authorizer) EnforcementDecision {
|
||||
return authz.IdentityReadAll(entCtx)
|
||||
})
|
||||
}
|
||||
|
||||
// IdentityWrite checks for permission to create or update a given
|
||||
// workload identity.
|
||||
func (c *ChainedAuthorizer) IdentityWrite(name string, entCtx *AuthorizerContext) EnforcementDecision {
|
||||
return c.executeChain(func(authz Authorizer) EnforcementDecision {
|
||||
return authz.IdentityWrite(name, entCtx)
|
||||
})
|
||||
}
|
||||
|
||||
// IdentityWriteAny checks for write permission on any workload identity.
|
||||
func (c *ChainedAuthorizer) IdentityWriteAny(entCtx *AuthorizerContext) EnforcementDecision {
|
||||
return c.executeChain(func(authz Authorizer) EnforcementDecision {
|
||||
return authz.IdentityWriteAny(entCtx)
|
||||
})
|
||||
}
|
||||
|
||||
// IntentionDefaultAllow determines the default authorized behavior
|
||||
// when no intentions match a Connect request.
|
||||
func (c *ChainedAuthorizer) IntentionDefaultAllow(entCtx *AuthorizerContext) EnforcementDecision {
|
||||
|
|
|
@ -29,6 +29,18 @@ func (authz testAuthorizer) EventRead(string, *AuthorizerContext) EnforcementDec
|
|||
func (authz testAuthorizer) EventWrite(string, *AuthorizerContext) EnforcementDecision {
|
||||
return EnforcementDecision(authz)
|
||||
}
|
||||
func (authz testAuthorizer) IdentityRead(string, *AuthorizerContext) EnforcementDecision {
|
||||
return EnforcementDecision(authz)
|
||||
}
|
||||
func (authz testAuthorizer) IdentityReadAll(*AuthorizerContext) EnforcementDecision {
|
||||
return EnforcementDecision(authz)
|
||||
}
|
||||
func (authz testAuthorizer) IdentityWrite(string, *AuthorizerContext) EnforcementDecision {
|
||||
return EnforcementDecision(authz)
|
||||
}
|
||||
func (authz testAuthorizer) IdentityWriteAny(*AuthorizerContext) EnforcementDecision {
|
||||
return EnforcementDecision(authz)
|
||||
}
|
||||
func (authz testAuthorizer) IntentionDefaultAllow(*AuthorizerContext) EnforcementDecision {
|
||||
return EnforcementDecision(authz)
|
||||
}
|
||||
|
|
|
@ -59,6 +59,8 @@ type PolicyRules struct {
|
|||
ACL string `hcl:"acl,expand"`
|
||||
Agents []*AgentRule `hcl:"agent,expand"`
|
||||
AgentPrefixes []*AgentRule `hcl:"agent_prefix,expand"`
|
||||
Identities []*IdentityRule `hcl:"identity,expand"`
|
||||
IdentityPrefixes []*IdentityRule `hcl:"identity_prefix,expand"`
|
||||
Keys []*KeyRule `hcl:"key,expand"`
|
||||
KeyPrefixes []*KeyRule `hcl:"key_prefix,expand"`
|
||||
Nodes []*NodeRule `hcl:"node,expand"`
|
||||
|
@ -90,6 +92,19 @@ type AgentRule struct {
|
|||
Policy string
|
||||
}
|
||||
|
||||
// IdentityRule represents a policy for a workload identity
|
||||
type IdentityRule struct {
|
||||
Name string `hcl:",key"`
|
||||
Policy string
|
||||
|
||||
// Intentions is the policy for intentions where this workload identity
|
||||
// is the destination. This may be empty, in which case the Policy determines
|
||||
// the intentions policy.
|
||||
Intentions string
|
||||
|
||||
EnterpriseRule `hcl:",squash"`
|
||||
}
|
||||
|
||||
// KeyRule represents a rule for a key
|
||||
type KeyRule struct {
|
||||
Prefix string `hcl:",key"`
|
||||
|
@ -168,6 +183,30 @@ func (pr *PolicyRules) Validate(conf *Config) error {
|
|||
}
|
||||
}
|
||||
|
||||
// Validate the identity policies
|
||||
for _, id := range pr.Identities {
|
||||
if !isPolicyValid(id.Policy, false) {
|
||||
return fmt.Errorf("Invalid identity policy: %#v", id)
|
||||
}
|
||||
if id.Intentions != "" && !isPolicyValid(id.Intentions, false) {
|
||||
return fmt.Errorf("Invalid identity intentions policy: %#v", id)
|
||||
}
|
||||
if err := id.EnterpriseRule.Validate(id.Policy, conf); err != nil {
|
||||
return fmt.Errorf("Invalid identity enterprise policy: %#v, got error: %v", id, err)
|
||||
}
|
||||
}
|
||||
for _, id := range pr.IdentityPrefixes {
|
||||
if !isPolicyValid(id.Policy, false) {
|
||||
return fmt.Errorf("Invalid identity_prefix policy: %#v", id)
|
||||
}
|
||||
if id.Intentions != "" && !isPolicyValid(id.Intentions, false) {
|
||||
return fmt.Errorf("Invalid identity_prefix intentions policy: %#v", id)
|
||||
}
|
||||
if err := id.EnterpriseRule.Validate(id.Policy, conf); err != nil {
|
||||
return fmt.Errorf("Invalid identity_prefix enterprise policy: %#v, got error: %v", id, err)
|
||||
}
|
||||
}
|
||||
|
||||
// Validate the key policy
|
||||
for _, kp := range pr.Keys {
|
||||
if !isPolicyValid(kp.Policy, true) {
|
||||
|
|
|
@ -14,6 +14,9 @@ type policyAuthorizer struct {
|
|||
// agentRules contain the exact-match agent policies
|
||||
agentRules *radix.Tree
|
||||
|
||||
// identityRules contains the identity exact-match policies
|
||||
identityRules *radix.Tree
|
||||
|
||||
// intentionRules contains the service intention exact-match policies
|
||||
intentionRules *radix.Tree
|
||||
|
||||
|
@ -180,6 +183,48 @@ func (p *policyAuthorizer) loadRules(policy *PolicyRules) error {
|
|||
}
|
||||
}
|
||||
|
||||
// Load the identity policy (exact matches)
|
||||
for _, id := range policy.Identities {
|
||||
if err := insertPolicyIntoRadix(id.Name, id.Policy, &id.EnterpriseRule, p.identityRules, false); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
intention := id.Intentions
|
||||
if intention == "" {
|
||||
switch id.Policy {
|
||||
case PolicyRead, PolicyWrite:
|
||||
intention = PolicyRead
|
||||
default:
|
||||
intention = PolicyDeny
|
||||
}
|
||||
}
|
||||
|
||||
if err := insertPolicyIntoRadix(id.Name, intention, &id.EnterpriseRule, p.intentionRules, false); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// Load the identity policy (prefix matches)
|
||||
for _, id := range policy.IdentityPrefixes {
|
||||
if err := insertPolicyIntoRadix(id.Name, id.Policy, &id.EnterpriseRule, p.identityRules, true); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
intention := id.Intentions
|
||||
if intention == "" {
|
||||
switch id.Policy {
|
||||
case PolicyRead, PolicyWrite:
|
||||
intention = PolicyRead
|
||||
default:
|
||||
intention = PolicyDeny
|
||||
}
|
||||
}
|
||||
|
||||
if err := insertPolicyIntoRadix(id.Name, intention, &id.EnterpriseRule, p.intentionRules, true); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// Load the key policy (exact matches)
|
||||
for _, kp := range policy.Keys {
|
||||
if err := insertPolicyIntoRadix(kp.Prefix, kp.Policy, &kp.EnterpriseRule, p.keyRules, false); err != nil {
|
||||
|
@ -349,6 +394,7 @@ func newPolicyAuthorizer(policies []*Policy, ent *Config) (*policyAuthorizer, er
|
|||
func newPolicyAuthorizerFromRules(rules *PolicyRules, ent *Config) (*policyAuthorizer, error) {
|
||||
p := &policyAuthorizer{
|
||||
agentRules: radix.New(),
|
||||
identityRules: radix.New(),
|
||||
intentionRules: radix.New(),
|
||||
keyRules: radix.New(),
|
||||
nodeRules: radix.New(),
|
||||
|
@ -528,6 +574,33 @@ func (p *policyAuthorizer) EventWrite(name string, _ *AuthorizerContext) Enforce
|
|||
return Default
|
||||
}
|
||||
|
||||
// IdentityRead checks for permission to read a given workload identity.
|
||||
func (p *policyAuthorizer) IdentityRead(name string, _ *AuthorizerContext) EnforcementDecision {
|
||||
if rule, ok := getPolicy(name, p.identityRules); ok {
|
||||
return enforce(rule.access, AccessRead)
|
||||
}
|
||||
return Default
|
||||
}
|
||||
|
||||
// IdentityReadAll checks for permission to read all workload identities.
|
||||
func (p *policyAuthorizer) IdentityReadAll(_ *AuthorizerContext) EnforcementDecision {
|
||||
return p.allAllowed(p.identityRules, AccessRead)
|
||||
}
|
||||
|
||||
// IdentityWrite checks for permission to create or update a given
|
||||
// workload identity.
|
||||
func (p *policyAuthorizer) IdentityWrite(name string, _ *AuthorizerContext) EnforcementDecision {
|
||||
if rule, ok := getPolicy(name, p.identityRules); ok {
|
||||
return enforce(rule.access, AccessWrite)
|
||||
}
|
||||
return Default
|
||||
}
|
||||
|
||||
// IdentityWriteAny checks for write permission on any workload identity.
|
||||
func (p *policyAuthorizer) IdentityWriteAny(_ *AuthorizerContext) EnforcementDecision {
|
||||
return p.anyAllowed(p.identityRules, AccessWrite)
|
||||
}
|
||||
|
||||
// IntentionDefaultAllow returns whether the default behavior when there are
|
||||
// no matching intentions is to allow or deny.
|
||||
func (p *policyAuthorizer) IntentionDefaultAllow(_ *AuthorizerContext) EnforcementDecision {
|
||||
|
|
|
@ -41,6 +41,9 @@ func TestPolicyAuthorizer(t *testing.T) {
|
|||
{name: "DefaultAgentWrite", prefix: "foo", check: checkDefaultAgentWrite},
|
||||
{name: "DefaultEventRead", prefix: "foo", check: checkDefaultEventRead},
|
||||
{name: "DefaultEventWrite", prefix: "foo", check: checkDefaultEventWrite},
|
||||
{name: "DefaultIdentityRead", prefix: "foo", check: checkDefaultIdentityRead},
|
||||
{name: "DefaultIdentityWrite", prefix: "foo", check: checkDefaultIdentityWrite},
|
||||
{name: "DefaultIdentityWriteAny", prefix: "", check: checkDefaultIdentityWriteAny},
|
||||
{name: "DefaultIntentionDefaultAllow", prefix: "foo", check: checkDefaultIntentionDefaultAllow},
|
||||
{name: "DefaultIntentionRead", prefix: "foo", check: checkDefaultIntentionRead},
|
||||
{name: "DefaultIntentionWrite", prefix: "foo", check: checkDefaultIntentionWrite},
|
||||
|
@ -185,6 +188,29 @@ func TestPolicyAuthorizer(t *testing.T) {
|
|||
Policy: PolicyRead,
|
||||
},
|
||||
},
|
||||
Identities: []*IdentityRule{
|
||||
{
|
||||
Name: "foo",
|
||||
Policy: PolicyWrite,
|
||||
Intentions: PolicyWrite,
|
||||
},
|
||||
{
|
||||
Name: "football",
|
||||
Policy: PolicyDeny,
|
||||
},
|
||||
},
|
||||
IdentityPrefixes: []*IdentityRule{
|
||||
{
|
||||
Name: "foot",
|
||||
Policy: PolicyRead,
|
||||
Intentions: PolicyRead,
|
||||
},
|
||||
{
|
||||
Name: "fo",
|
||||
Policy: PolicyRead,
|
||||
Intentions: PolicyRead,
|
||||
},
|
||||
},
|
||||
Keys: []*KeyRule{
|
||||
{
|
||||
Prefix: "foo",
|
||||
|
@ -371,20 +397,21 @@ func TestPolicyAuthorizer(t *testing.T) {
|
|||
{name: "ServiceWriteDenied", prefix: "football", check: checkDenyServiceWrite},
|
||||
{name: "ServiceWriteAnyAllowed", prefix: "", check: checkAllowServiceWriteAny},
|
||||
|
||||
{name: "NodeReadPrefixAllowed", prefix: "fo", check: checkAllowNodeRead},
|
||||
{name: "NodeWritePrefixDenied", prefix: "fo", check: checkDenyNodeWrite},
|
||||
{name: "NodeReadPrefixAllowed", prefix: "for", check: checkAllowNodeRead},
|
||||
{name: "NodeWritePrefixDenied", prefix: "for", check: checkDenyNodeWrite},
|
||||
{name: "NodeReadAllowed", prefix: "foo", check: checkAllowNodeRead},
|
||||
{name: "NodeWriteAllowed", prefix: "foo", check: checkAllowNodeWrite},
|
||||
{name: "NodeReadPrefixAllowed", prefix: "foot", check: checkAllowNodeRead},
|
||||
{name: "NodeWritePrefixDenied", prefix: "foot", check: checkDenyNodeWrite},
|
||||
{name: "NodeReadPrefixAllowed", prefix: "foot2", check: checkAllowNodeRead},
|
||||
{name: "NodeWritePrefixDenied", prefix: "foot2", check: checkDenyNodeWrite},
|
||||
{name: "NodeReadPrefixAllowed", prefix: "food", check: checkAllowNodeRead},
|
||||
{name: "NodeWritePrefixDenied", prefix: "food", check: checkDenyNodeWrite},
|
||||
{name: "NodeReadDenied", prefix: "football", check: checkDenyNodeRead},
|
||||
{name: "NodeWriteDenied", prefix: "football", check: checkDenyNodeWrite},
|
||||
{name: "IdentityReadPrefixAllowed", prefix: "fo", check: checkAllowIdentityRead},
|
||||
{name: "IdentityWritePrefixDenied", prefix: "fo", check: checkDenyIdentityWrite},
|
||||
{name: "IdentityReadPrefixAllowed", prefix: "for", check: checkAllowIdentityRead},
|
||||
{name: "IdentityWritePrefixDenied", prefix: "for", check: checkDenyIdentityWrite},
|
||||
{name: "IdentityReadAllowed", prefix: "foo", check: checkAllowIdentityRead},
|
||||
{name: "IdentityWriteAllowed", prefix: "foo", check: checkAllowIdentityWrite},
|
||||
{name: "IdentityReadPrefixAllowed", prefix: "foot", check: checkAllowIdentityRead},
|
||||
{name: "IdentityWritePrefixDenied", prefix: "foot", check: checkDenyIdentityWrite},
|
||||
{name: "IdentityReadPrefixAllowed", prefix: "foot2", check: checkAllowIdentityRead},
|
||||
{name: "IdentityWritePrefixDenied", prefix: "foot2", check: checkDenyIdentityWrite},
|
||||
{name: "IdentityReadPrefixAllowed", prefix: "food", check: checkAllowIdentityRead},
|
||||
{name: "IdentityWritePrefixDenied", prefix: "food", check: checkDenyIdentityWrite},
|
||||
{name: "IdentityReadDenied", prefix: "football", check: checkDenyIdentityRead},
|
||||
{name: "IdentityWriteDenied", prefix: "football", check: checkDenyIdentityWrite},
|
||||
{name: "IdentityWriteAnyAllowed", prefix: "", check: checkAllowIdentityWriteAny},
|
||||
|
||||
{name: "IntentionReadPrefixAllowed", prefix: "fo", check: checkAllowIntentionRead},
|
||||
{name: "IntentionWritePrefixDenied", prefix: "fo", check: checkDenyIntentionWrite},
|
||||
|
|
|
@ -9,6 +9,8 @@ type policyRulesMergeContext struct {
|
|||
agentPrefixRules map[string]*AgentRule
|
||||
eventRules map[string]*EventRule
|
||||
eventPrefixRules map[string]*EventRule
|
||||
identityRules map[string]*IdentityRule
|
||||
identityPrefixRules map[string]*IdentityRule
|
||||
keyringRule string
|
||||
keyRules map[string]*KeyRule
|
||||
keyPrefixRules map[string]*KeyRule
|
||||
|
@ -33,6 +35,8 @@ func (p *policyRulesMergeContext) init() {
|
|||
p.agentPrefixRules = make(map[string]*AgentRule)
|
||||
p.eventRules = make(map[string]*EventRule)
|
||||
p.eventPrefixRules = make(map[string]*EventRule)
|
||||
p.identityRules = make(map[string]*IdentityRule)
|
||||
p.identityPrefixRules = make(map[string]*IdentityRule)
|
||||
p.keyringRule = ""
|
||||
p.keyRules = make(map[string]*KeyRule)
|
||||
p.keyPrefixRules = make(map[string]*KeyRule)
|
||||
|
@ -98,6 +102,42 @@ func (p *policyRulesMergeContext) merge(policy *PolicyRules) {
|
|||
}
|
||||
}
|
||||
|
||||
for _, id := range policy.Identities {
|
||||
existing, found := p.identityRules[id.Name]
|
||||
|
||||
if !found {
|
||||
p.identityRules[id.Name] = id
|
||||
continue
|
||||
}
|
||||
|
||||
if takesPrecedenceOver(id.Policy, existing.Policy) {
|
||||
existing.Policy = id.Policy
|
||||
existing.EnterpriseRule = id.EnterpriseRule
|
||||
}
|
||||
|
||||
if takesPrecedenceOver(id.Intentions, existing.Intentions) {
|
||||
existing.Intentions = id.Intentions
|
||||
}
|
||||
}
|
||||
|
||||
for _, id := range policy.IdentityPrefixes {
|
||||
existing, found := p.identityPrefixRules[id.Name]
|
||||
|
||||
if !found {
|
||||
p.identityPrefixRules[id.Name] = id
|
||||
continue
|
||||
}
|
||||
|
||||
if takesPrecedenceOver(id.Policy, existing.Policy) {
|
||||
existing.Policy = id.Policy
|
||||
existing.EnterpriseRule = id.EnterpriseRule
|
||||
}
|
||||
|
||||
if takesPrecedenceOver(id.Intentions, existing.Intentions) {
|
||||
existing.Intentions = id.Intentions
|
||||
}
|
||||
}
|
||||
|
||||
if takesPrecedenceOver(policy.Keyring, p.keyringRule) {
|
||||
p.keyringRule = policy.Keyring
|
||||
}
|
||||
|
@ -269,6 +309,16 @@ func (p *policyRulesMergeContext) fill(merged *PolicyRules) {
|
|||
merged.EventPrefixes = append(merged.EventPrefixes, policy)
|
||||
}
|
||||
|
||||
merged.Identities = []*IdentityRule{}
|
||||
for _, policy := range p.identityRules {
|
||||
merged.Identities = append(merged.Identities, policy)
|
||||
}
|
||||
|
||||
merged.IdentityPrefixes = []*IdentityRule{}
|
||||
for _, policy := range p.identityPrefixRules {
|
||||
merged.IdentityPrefixes = append(merged.IdentityPrefixes, policy)
|
||||
}
|
||||
|
||||
merged.Keys = []*KeyRule{}
|
||||
for _, policy := range p.keyRules {
|
||||
merged.Keys = append(merged.Keys, policy)
|
||||
|
|
|
@ -42,6 +42,12 @@ func TestPolicySourceParse(t *testing.T) {
|
|||
event "bar" {
|
||||
policy = "deny"
|
||||
}
|
||||
identity_prefix "" {
|
||||
policy = "write"
|
||||
}
|
||||
identity "foo" {
|
||||
policy = "read"
|
||||
}
|
||||
key_prefix "" {
|
||||
policy = "read"
|
||||
}
|
||||
|
@ -117,6 +123,16 @@ func TestPolicySourceParse(t *testing.T) {
|
|||
"policy": "deny"
|
||||
}
|
||||
},
|
||||
"identity_prefix": {
|
||||
"": {
|
||||
"policy": "write"
|
||||
}
|
||||
},
|
||||
"identity": {
|
||||
"foo": {
|
||||
"policy": "read"
|
||||
}
|
||||
},
|
||||
"key_prefix": {
|
||||
"": {
|
||||
"policy": "read"
|
||||
|
@ -217,6 +233,18 @@ func TestPolicySourceParse(t *testing.T) {
|
|||
Policy: PolicyDeny,
|
||||
},
|
||||
},
|
||||
IdentityPrefixes: []*IdentityRule{
|
||||
{
|
||||
Name: "",
|
||||
Policy: PolicyWrite,
|
||||
},
|
||||
},
|
||||
Identities: []*IdentityRule{
|
||||
{
|
||||
Name: "foo",
|
||||
Policy: PolicyRead,
|
||||
},
|
||||
},
|
||||
Keyring: PolicyDeny,
|
||||
KeyPrefixes: []*KeyRule{
|
||||
{
|
||||
|
@ -303,6 +331,39 @@ func TestPolicySourceParse(t *testing.T) {
|
|||
},
|
||||
}},
|
||||
},
|
||||
{
|
||||
Name: "Identity No Intentions",
|
||||
Rules: `identity "foo" { policy = "write" }`,
|
||||
RulesJSON: `{ "identity": { "foo": { "policy": "write" }}}`,
|
||||
Expected: &Policy{PolicyRules: PolicyRules{
|
||||
Identities: []*IdentityRule{
|
||||
{
|
||||
Name: "foo",
|
||||
Policy: "write",
|
||||
},
|
||||
},
|
||||
}},
|
||||
},
|
||||
{
|
||||
Name: "Identity Intentions",
|
||||
Rules: `identity "foo" { policy = "write" intentions = "read" }`,
|
||||
RulesJSON: `{ "identity": { "foo": { "policy": "write", "intentions": "read" }}}`,
|
||||
Expected: &Policy{PolicyRules: PolicyRules{
|
||||
Identities: []*IdentityRule{
|
||||
{
|
||||
Name: "foo",
|
||||
Policy: "write",
|
||||
Intentions: "read",
|
||||
},
|
||||
},
|
||||
}},
|
||||
},
|
||||
{
|
||||
Name: "Identity Intention: invalid value",
|
||||
Rules: `identity "foo" { policy = "write" intentions = "foo" }`,
|
||||
RulesJSON: `{ "identity": { "foo": { "policy": "write", "intentions": "foo" }}}`,
|
||||
Err: "Invalid identity intentions policy",
|
||||
},
|
||||
{
|
||||
Name: "Service No Intentions",
|
||||
Rules: `service "foo" { policy = "write" }`,
|
||||
|
@ -354,6 +415,18 @@ func TestPolicySourceParse(t *testing.T) {
|
|||
RulesJSON: `{ "agent_prefix": { "foo": { "policy": "nope" }}}`,
|
||||
Err: "Invalid agent_prefix policy",
|
||||
},
|
||||
{
|
||||
Name: "Bad Policy - Identity",
|
||||
Rules: `identity "foo" { policy = "nope" }`,
|
||||
RulesJSON: `{ "identity": { "foo": { "policy": "nope" }}}`,
|
||||
Err: "Invalid identity policy",
|
||||
},
|
||||
{
|
||||
Name: "Bad Policy - Identity Prefix",
|
||||
Rules: `identity_prefix "foo" { policy = "nope" }`,
|
||||
RulesJSON: `{ "identity_prefix": { "foo": { "policy": "nope" }}}`,
|
||||
Err: "Invalid identity_prefix policy",
|
||||
},
|
||||
{
|
||||
Name: "Bad Policy - Key",
|
||||
Rules: `key "foo" { policy = "nope" }`,
|
||||
|
@ -685,6 +758,109 @@ func TestMergePolicies(t *testing.T) {
|
|||
},
|
||||
}},
|
||||
},
|
||||
{
|
||||
name: "Identities",
|
||||
input: []*Policy{
|
||||
{PolicyRules: PolicyRules{
|
||||
Identities: []*IdentityRule{
|
||||
{
|
||||
Name: "foo",
|
||||
Policy: PolicyWrite,
|
||||
Intentions: PolicyWrite,
|
||||
},
|
||||
{
|
||||
Name: "bar",
|
||||
Policy: PolicyRead,
|
||||
Intentions: PolicyRead,
|
||||
},
|
||||
{
|
||||
Name: "baz",
|
||||
Policy: PolicyWrite,
|
||||
Intentions: PolicyWrite,
|
||||
},
|
||||
},
|
||||
IdentityPrefixes: []*IdentityRule{
|
||||
{
|
||||
Name: "000",
|
||||
Policy: PolicyWrite,
|
||||
Intentions: PolicyWrite,
|
||||
},
|
||||
{
|
||||
Name: "111",
|
||||
Policy: PolicyRead,
|
||||
Intentions: PolicyRead,
|
||||
},
|
||||
{
|
||||
Name: "222",
|
||||
Policy: PolicyWrite,
|
||||
Intentions: PolicyWrite,
|
||||
},
|
||||
},
|
||||
}},
|
||||
{PolicyRules: PolicyRules{
|
||||
Identities: []*IdentityRule{
|
||||
{
|
||||
Name: "foo",
|
||||
Policy: PolicyRead,
|
||||
Intentions: PolicyRead,
|
||||
},
|
||||
{
|
||||
Name: "baz",
|
||||
Policy: PolicyDeny,
|
||||
Intentions: PolicyDeny,
|
||||
},
|
||||
},
|
||||
IdentityPrefixes: []*IdentityRule{
|
||||
{
|
||||
Name: "000",
|
||||
Policy: PolicyRead,
|
||||
Intentions: PolicyRead,
|
||||
},
|
||||
{
|
||||
Name: "222",
|
||||
Policy: PolicyDeny,
|
||||
Intentions: PolicyDeny,
|
||||
},
|
||||
},
|
||||
}},
|
||||
},
|
||||
expected: &Policy{PolicyRules: PolicyRules{
|
||||
Identities: []*IdentityRule{
|
||||
{
|
||||
Name: "foo",
|
||||
Policy: PolicyWrite,
|
||||
Intentions: PolicyWrite,
|
||||
},
|
||||
{
|
||||
Name: "bar",
|
||||
Policy: PolicyRead,
|
||||
Intentions: PolicyRead,
|
||||
},
|
||||
{
|
||||
Name: "baz",
|
||||
Policy: PolicyDeny,
|
||||
Intentions: PolicyDeny,
|
||||
},
|
||||
},
|
||||
IdentityPrefixes: []*IdentityRule{
|
||||
{
|
||||
Name: "000",
|
||||
Policy: PolicyWrite,
|
||||
Intentions: PolicyWrite,
|
||||
},
|
||||
{
|
||||
Name: "111",
|
||||
Policy: PolicyRead,
|
||||
Intentions: PolicyRead,
|
||||
},
|
||||
{
|
||||
Name: "222",
|
||||
Policy: PolicyDeny,
|
||||
Intentions: PolicyDeny,
|
||||
},
|
||||
},
|
||||
}},
|
||||
},
|
||||
{
|
||||
name: "Node",
|
||||
input: []*Policy{
|
||||
|
|
|
@ -75,6 +75,34 @@ func (s *staticAuthorizer) EventWrite(string, *AuthorizerContext) EnforcementDec
|
|||
return Deny
|
||||
}
|
||||
|
||||
func (s *staticAuthorizer) IdentityRead(string, *AuthorizerContext) EnforcementDecision {
|
||||
if s.defaultAllow {
|
||||
return Allow
|
||||
}
|
||||
return Deny
|
||||
}
|
||||
|
||||
func (s *staticAuthorizer) IdentityReadAll(*AuthorizerContext) EnforcementDecision {
|
||||
if s.defaultAllow {
|
||||
return Allow
|
||||
}
|
||||
return Deny
|
||||
}
|
||||
|
||||
func (s *staticAuthorizer) IdentityWrite(string, *AuthorizerContext) EnforcementDecision {
|
||||
if s.defaultAllow {
|
||||
return Allow
|
||||
}
|
||||
return Deny
|
||||
}
|
||||
|
||||
func (s *staticAuthorizer) IdentityWriteAny(*AuthorizerContext) EnforcementDecision {
|
||||
if s.defaultAllow {
|
||||
return Allow
|
||||
}
|
||||
return Deny
|
||||
}
|
||||
|
||||
func (s *staticAuthorizer) IntentionDefaultAllow(*AuthorizerContext) EnforcementDecision {
|
||||
if s.defaultAllow {
|
||||
return Allow
|
||||
|
|
|
@ -2190,7 +2190,7 @@ func TestACL_Authorize(t *testing.T) {
|
|||
policyReq := structs.ACLPolicySetRequest{
|
||||
Policy: structs.ACLPolicy{
|
||||
Name: "test",
|
||||
Rules: `acl = "read" operator = "write" service_prefix "" { policy = "read"} node_prefix "" { policy= "write" } key_prefix "/foo" { policy = "write" } `,
|
||||
Rules: `acl = "read" operator = "write" identity_prefix "" { policy = "read"} service_prefix "" { policy = "read"} node_prefix "" { policy= "write" } key_prefix "/foo" { policy = "write" } `,
|
||||
},
|
||||
Datacenter: "dc1",
|
||||
WriteRequest: structs.WriteRequest{Token: TestDefaultInitialManagementToken},
|
||||
|
@ -2276,6 +2276,16 @@ func TestACL_Authorize(t *testing.T) {
|
|||
Segment: "foo",
|
||||
Access: "write",
|
||||
},
|
||||
{
|
||||
Resource: "identity",
|
||||
Segment: "foo",
|
||||
Access: "read",
|
||||
},
|
||||
{
|
||||
Resource: "identity",
|
||||
Segment: "foo",
|
||||
Access: "write",
|
||||
},
|
||||
{
|
||||
Resource: "intention",
|
||||
Segment: "foo",
|
||||
|
@ -2426,6 +2436,16 @@ func TestACL_Authorize(t *testing.T) {
|
|||
Segment: "foo",
|
||||
Access: "write",
|
||||
},
|
||||
{
|
||||
Resource: "identity",
|
||||
Segment: "foo",
|
||||
Access: "read",
|
||||
},
|
||||
{
|
||||
Resource: "identity",
|
||||
Segment: "foo",
|
||||
Access: "write",
|
||||
},
|
||||
{
|
||||
Resource: "intention",
|
||||
Segment: "foo",
|
||||
|
@ -2532,6 +2552,8 @@ func TestACL_Authorize(t *testing.T) {
|
|||
false, // agent:write
|
||||
false, // event:read
|
||||
false, // event:write
|
||||
true, // identity:read
|
||||
false, // identity:write
|
||||
true, // intentions:read
|
||||
false, // intention:write
|
||||
false, // key:read
|
||||
|
|
|
@ -615,10 +615,6 @@ func (d *ACLResolverTestDelegate) ACLDatacenter() string {
|
|||
return d.datacenter
|
||||
}
|
||||
|
||||
func (d *ACLResolverTestDelegate) UseLegacyACLs() bool {
|
||||
return d.legacy
|
||||
}
|
||||
|
||||
func (d *ACLResolverTestDelegate) ResolveIdentityFromToken(token string) (bool, structs.ACLIdentity, error) {
|
||||
if !d.localTokens {
|
||||
return false, nil, nil
|
||||
|
@ -731,7 +727,6 @@ func TestACLResolver_Disabled(t *testing.T) {
|
|||
delegate := &ACLResolverTestDelegate{
|
||||
enabled: false,
|
||||
datacenter: "dc1",
|
||||
legacy: false,
|
||||
}
|
||||
|
||||
r := newTestACLResolver(t, delegate, nil)
|
||||
|
@ -746,7 +741,6 @@ func TestACLResolver_ResolveRootACL(t *testing.T) {
|
|||
delegate := &ACLResolverTestDelegate{
|
||||
enabled: true,
|
||||
datacenter: "dc1",
|
||||
legacy: false,
|
||||
}
|
||||
r := newTestACLResolver(t, delegate, nil)
|
||||
|
||||
|
@ -797,7 +791,6 @@ func TestACLResolver_DownPolicy(t *testing.T) {
|
|||
delegate := &ACLResolverTestDelegate{
|
||||
enabled: true,
|
||||
datacenter: "dc1",
|
||||
legacy: false,
|
||||
localTokens: false,
|
||||
localPolicies: true,
|
||||
localRoles: true,
|
||||
|
@ -825,7 +818,6 @@ func TestACLResolver_DownPolicy(t *testing.T) {
|
|||
delegate := &ACLResolverTestDelegate{
|
||||
enabled: true,
|
||||
datacenter: "dc1",
|
||||
legacy: false,
|
||||
localTokens: false,
|
||||
localPolicies: true,
|
||||
localRoles: true,
|
||||
|
@ -853,7 +845,6 @@ func TestACLResolver_DownPolicy(t *testing.T) {
|
|||
delegate := &ACLResolverTestDelegate{
|
||||
enabled: true,
|
||||
datacenter: "dc1",
|
||||
legacy: false,
|
||||
localTokens: true,
|
||||
localPolicies: false,
|
||||
localRoles: false,
|
||||
|
@ -889,7 +880,6 @@ func TestACLResolver_DownPolicy(t *testing.T) {
|
|||
delegate := &ACLResolverTestDelegate{
|
||||
enabled: true,
|
||||
datacenter: "dc1",
|
||||
legacy: false,
|
||||
localTokens: true,
|
||||
localPolicies: false,
|
||||
localRoles: false,
|
||||
|
@ -920,7 +910,6 @@ func TestACLResolver_DownPolicy(t *testing.T) {
|
|||
delegate := &ACLResolverTestDelegate{
|
||||
enabled: true,
|
||||
datacenter: "dc1",
|
||||
legacy: false,
|
||||
localTokens: false,
|
||||
localPolicies: true,
|
||||
localRoles: true,
|
||||
|
@ -971,7 +960,6 @@ func TestACLResolver_DownPolicy(t *testing.T) {
|
|||
delegate := &ACLResolverTestDelegate{
|
||||
enabled: true,
|
||||
datacenter: "dc1",
|
||||
legacy: false,
|
||||
localTokens: false,
|
||||
localPolicies: true,
|
||||
localRoles: true,
|
||||
|
@ -1002,7 +990,6 @@ func TestACLResolver_DownPolicy(t *testing.T) {
|
|||
delegate := &ACLResolverTestDelegate{
|
||||
enabled: true,
|
||||
datacenter: "dc1",
|
||||
legacy: false,
|
||||
localTokens: true,
|
||||
localPolicies: false,
|
||||
localRoles: false,
|
||||
|
@ -1038,7 +1025,6 @@ func TestACLResolver_DownPolicy(t *testing.T) {
|
|||
delegate := &ACLResolverTestDelegate{
|
||||
enabled: true,
|
||||
datacenter: "dc1",
|
||||
legacy: false,
|
||||
localTokens: true,
|
||||
localPolicies: false,
|
||||
localRoles: false,
|
||||
|
@ -1070,7 +1056,6 @@ func TestACLResolver_DownPolicy(t *testing.T) {
|
|||
delegate := &ACLResolverTestDelegate{
|
||||
enabled: true,
|
||||
datacenter: "dc1",
|
||||
legacy: false,
|
||||
localTokens: true,
|
||||
localPolicies: false,
|
||||
localRoles: false,
|
||||
|
@ -1117,7 +1102,6 @@ func TestACLResolver_DownPolicy(t *testing.T) {
|
|||
delegate := &ACLResolverTestDelegate{
|
||||
enabled: true,
|
||||
datacenter: "dc1",
|
||||
legacy: false,
|
||||
localTokens: true,
|
||||
localPolicies: false,
|
||||
localRoles: false,
|
||||
|
@ -1159,7 +1143,6 @@ func TestACLResolver_DownPolicy(t *testing.T) {
|
|||
delegate := &ACLResolverTestDelegate{
|
||||
enabled: true,
|
||||
datacenter: "dc1",
|
||||
legacy: false,
|
||||
localTokens: false,
|
||||
localPolicies: false,
|
||||
localRoles: false,
|
||||
|
@ -1194,7 +1177,6 @@ func TestACLResolver_DownPolicy(t *testing.T) {
|
|||
delegate := &ACLResolverTestDelegate{
|
||||
enabled: true,
|
||||
datacenter: "dc1",
|
||||
legacy: false,
|
||||
localTokens: false,
|
||||
localPolicies: false,
|
||||
localRoles: false,
|
||||
|
@ -1230,7 +1212,6 @@ func TestACLResolver_DownPolicy(t *testing.T) {
|
|||
delegate := &ACLResolverTestDelegate{
|
||||
enabled: true,
|
||||
datacenter: "dc1",
|
||||
legacy: false,
|
||||
localTokens: false,
|
||||
localPolicies: true,
|
||||
localRoles: true,
|
||||
|
@ -1276,7 +1257,6 @@ func TestACLResolver_DownPolicy(t *testing.T) {
|
|||
delegate := &ACLResolverTestDelegate{
|
||||
enabled: true,
|
||||
datacenter: "dc1",
|
||||
legacy: false,
|
||||
localTokens: false,
|
||||
localPolicies: false,
|
||||
tokenReadFn: func(_ *structs.ACLTokenGetRequest, reply *structs.ACLTokenResponse) error {
|
||||
|
@ -1340,7 +1320,6 @@ func TestACLResolver_DownPolicy(t *testing.T) {
|
|||
delegate := &ACLResolverTestDelegate{
|
||||
enabled: true,
|
||||
datacenter: "dc1",
|
||||
legacy: false,
|
||||
localTokens: false,
|
||||
localPolicies: false,
|
||||
tokenReadFn: func(_ *structs.ACLTokenGetRequest, reply *structs.ACLTokenResponse) error {
|
||||
|
@ -1398,7 +1377,6 @@ func TestACLResolver_DatacenterScoping(t *testing.T) {
|
|||
delegate := &ACLResolverTestDelegate{
|
||||
enabled: true,
|
||||
datacenter: "dc1",
|
||||
legacy: false,
|
||||
localTokens: true,
|
||||
localPolicies: true,
|
||||
localRoles: true,
|
||||
|
@ -1418,7 +1396,6 @@ func TestACLResolver_DatacenterScoping(t *testing.T) {
|
|||
delegate := &ACLResolverTestDelegate{
|
||||
enabled: true,
|
||||
datacenter: "dc2",
|
||||
legacy: false,
|
||||
localTokens: true,
|
||||
localPolicies: true,
|
||||
localRoles: true,
|
||||
|
@ -1454,7 +1431,6 @@ func TestACLResolver_Client(t *testing.T) {
|
|||
delegate := &ACLResolverTestDelegate{
|
||||
enabled: true,
|
||||
datacenter: "dc1",
|
||||
legacy: false,
|
||||
localTokens: false,
|
||||
localPolicies: false,
|
||||
tokenReadFn: func(_ *structs.ACLTokenGetRequest, reply *structs.ACLTokenResponse) error {
|
||||
|
@ -1545,7 +1521,6 @@ func TestACLResolver_Client(t *testing.T) {
|
|||
delegate := &ACLResolverTestDelegate{
|
||||
enabled: true,
|
||||
datacenter: "dc1",
|
||||
legacy: false,
|
||||
localTokens: false,
|
||||
localPolicies: false,
|
||||
tokenReadFn: func(args *structs.ACLTokenGetRequest, reply *structs.ACLTokenResponse) error {
|
||||
|
@ -1608,7 +1583,6 @@ func TestACLResolver_Client_TokensPoliciesAndRoles(t *testing.T) {
|
|||
delegate := &ACLResolverTestDelegate{
|
||||
enabled: true,
|
||||
datacenter: "dc1",
|
||||
legacy: false,
|
||||
localTokens: false,
|
||||
localPolicies: false,
|
||||
localRoles: false,
|
||||
|
@ -1625,7 +1599,6 @@ func TestACLResolver_LocalTokensPoliciesAndRoles(t *testing.T) {
|
|||
delegate := &ACLResolverTestDelegate{
|
||||
enabled: true,
|
||||
datacenter: "dc1",
|
||||
legacy: false,
|
||||
localTokens: true,
|
||||
localPolicies: true,
|
||||
localRoles: true,
|
||||
|
@ -1641,7 +1614,6 @@ func TestACLResolver_LocalPoliciesAndRoles(t *testing.T) {
|
|||
delegate := &ACLResolverTestDelegate{
|
||||
enabled: true,
|
||||
datacenter: "dc1",
|
||||
legacy: false,
|
||||
localTokens: false,
|
||||
localPolicies: true,
|
||||
localRoles: true,
|
||||
|
|
|
@ -63,6 +63,10 @@ agent_prefix "" {
|
|||
event_prefix "" {
|
||||
policy = "%[1]s"
|
||||
}
|
||||
identity_prefix "" {
|
||||
policy = "%[1]s"
|
||||
intentions = "%[1]s"
|
||||
}
|
||||
key_prefix "" {
|
||||
policy = "%[1]s"
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue