acl: small resolver changes to account for partitions (#11052)

Also refactoring the enterprise side of a test to make it easier to reason about.
This commit is contained in:
Freddy 2021-09-16 08:17:02 -06:00 committed by GitHub
parent 9f12fbd3cc
commit fcef19f94b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 435 additions and 141 deletions

View File

@ -757,10 +757,12 @@ func (r *ACLResolver) filterPoliciesByScope(policies structs.ACLPolicies) struct
} }
func (r *ACLResolver) resolvePoliciesForIdentity(identity structs.ACLIdentity) (structs.ACLPolicies, error) { func (r *ACLResolver) resolvePoliciesForIdentity(identity structs.ACLIdentity) (structs.ACLPolicies, error) {
policyIDs := identity.PolicyIDs() var (
roleIDs := identity.RoleIDs() policyIDs = identity.PolicyIDs()
serviceIdentities := identity.ServiceIdentityList() roleIDs = identity.RoleIDs()
nodeIdentities := identity.NodeIdentityList() serviceIdentities = identity.ServiceIdentityList()
nodeIdentities = identity.NodeIdentityList()
)
if len(policyIDs) == 0 && len(serviceIdentities) == 0 && len(roleIDs) == 0 && len(nodeIdentities) == 0 { if len(policyIDs) == 0 && len(serviceIdentities) == 0 && len(roleIDs) == 0 && len(nodeIdentities) == 0 {
policy := identity.EmbeddedPolicy() policy := identity.EmbeddedPolicy()
@ -794,7 +796,7 @@ func (r *ACLResolver) resolvePoliciesForIdentity(identity structs.ACLIdentity) (
// Generate synthetic policies for all service identities in effect. // Generate synthetic policies for all service identities in effect.
syntheticPolicies := r.synthesizePoliciesForServiceIdentities(serviceIdentities, identity.EnterpriseMetadata()) syntheticPolicies := r.synthesizePoliciesForServiceIdentities(serviceIdentities, identity.EnterpriseMetadata())
syntheticPolicies = append(syntheticPolicies, r.synthesizePoliciesForNodeIdentities(nodeIdentities)...) syntheticPolicies = append(syntheticPolicies, r.synthesizePoliciesForNodeIdentities(nodeIdentities, identity.EnterpriseMetadata())...)
// For the new ACLs policy replication is mandatory for correct operation on servers. Therefore // For the new ACLs policy replication is mandatory for correct operation on servers. Therefore
// we only attempt to resolve policies locally // we only attempt to resolve policies locally
@ -805,6 +807,7 @@ func (r *ACLResolver) resolvePoliciesForIdentity(identity structs.ACLIdentity) (
policies = append(policies, syntheticPolicies...) policies = append(policies, syntheticPolicies...)
filtered := r.filterPoliciesByScope(policies) filtered := r.filterPoliciesByScope(policies)
// TODO(partitions,acls): filter these by the partition/namespace of the token trying to use them?
return filtered, nil return filtered, nil
} }
@ -821,14 +824,14 @@ func (r *ACLResolver) synthesizePoliciesForServiceIdentities(serviceIdentities [
return syntheticPolicies return syntheticPolicies
} }
func (r *ACLResolver) synthesizePoliciesForNodeIdentities(nodeIdentities []*structs.ACLNodeIdentity) []*structs.ACLPolicy { func (r *ACLResolver) synthesizePoliciesForNodeIdentities(nodeIdentities []*structs.ACLNodeIdentity, entMeta *structs.EnterpriseMeta) []*structs.ACLPolicy {
if len(nodeIdentities) == 0 { if len(nodeIdentities) == 0 {
return nil return nil
} }
syntheticPolicies := make([]*structs.ACLPolicy, 0, len(nodeIdentities)) syntheticPolicies := make([]*structs.ACLPolicy, 0, len(nodeIdentities))
for _, n := range nodeIdentities { for _, n := range nodeIdentities {
syntheticPolicies = append(syntheticPolicies, n.SyntheticPolicy()) syntheticPolicies = append(syntheticPolicies, n.SyntheticPolicy(entMeta))
} }
return syntheticPolicies return syntheticPolicies
@ -1242,6 +1245,7 @@ func (r *ACLResolver) ResolveTokenToIdentityAndAuthorizer(token string) (structs
} }
if r.delegate.UseLegacyACLs() { if r.delegate.UseLegacyACLs() {
// TODO(partitions,acls): do we have to care about legacy acls?
identity, authorizer, err := r.resolveTokenLegacy(token) identity, authorizer, err := r.resolveTokenLegacy(token)
r.handleACLDisabledError(err) r.handleACLDisabledError(err)
return identity, authorizer, err return identity, authorizer, err

View File

@ -3,6 +3,8 @@
package consul package consul
import ( import (
"fmt"
"github.com/hashicorp/consul/acl" "github.com/hashicorp/consul/acl"
"github.com/hashicorp/consul/agent/structs" "github.com/hashicorp/consul/agent/structs"
) )
@ -26,3 +28,10 @@ type EnterpriseACLResolverTestDelegate struct{}
func (d *EnterpriseACLResolverTestDelegate) RPC(string, interface{}, interface{}) (bool, error) { func (d *EnterpriseACLResolverTestDelegate) RPC(string, interface{}, interface{}) (bool, error) {
return false, nil return false, nil
} }
func (d *EnterpriseACLResolverTestDelegate) UseTestLocalData(data []interface{}) {
if len(data) > 0 {
panic(fmt.Sprintf("unexpected data type: %T", data[0]))
}
}
func (d *EnterpriseACLResolverTestDelegate) UseDefaultData() {}

View File

@ -4,6 +4,7 @@ import (
"fmt" "fmt"
"os" "os"
"reflect" "reflect"
"strings"
"sync/atomic" "sync/atomic"
"testing" "testing"
"time" "time"
@ -81,19 +82,6 @@ func resolveToken(t *testing.T, r *ACLResolver, token string) acl.Authorizer {
func testIdentityForToken(token string) (bool, structs.ACLIdentity, error) { func testIdentityForToken(token string) (bool, structs.ACLIdentity, error) {
switch token { switch token {
case "missing-policy":
return true, &structs.ACLToken{
AccessorID: "435a75af-1763-4980-89f4-f0951dda53b4",
SecretID: "b1b6be70-ed2e-4c80-8495-bdb3db110b1e",
Policies: []structs.ACLTokenPolicyLink{
{
ID: "not-found",
},
{
ID: "acl-ro",
},
},
}, nil
case "missing-role": case "missing-role":
return true, &structs.ACLToken{ return true, &structs.ACLToken{
AccessorID: "435a75af-1763-4980-89f4-f0951dda53b4", AccessorID: "435a75af-1763-4980-89f4-f0951dda53b4",
@ -107,29 +95,6 @@ func testIdentityForToken(token string) (bool, structs.ACLIdentity, error) {
}, },
}, },
}, nil }, nil
case "missing-policy-on-role":
return true, &structs.ACLToken{
AccessorID: "435a75af-1763-4980-89f4-f0951dda53b4",
SecretID: "b1b6be70-ed2e-4c80-8495-bdb3db110b1e",
Roles: []structs.ACLTokenRoleLink{
{
ID: "missing-policy",
},
},
}, nil
case "legacy-management":
return true, &structs.ACLToken{
AccessorID: "d109a033-99d1-47e2-a711-d6593373a973",
SecretID: "415cd1e1-1493-4fb4-827d-d762ed9cfe7c",
Type: structs.ACLTokenTypeManagement,
}, nil
case "legacy-client":
return true, &structs.ACLToken{
AccessorID: "b7375838-b104-4a25-b457-329d939bf257",
SecretID: "03f49328-c23c-4b26-92a2-3b898332400d",
Type: structs.ACLTokenTypeClient,
Rules: `service "" { policy = "read" }`,
}, nil
case "found": case "found":
return true, &structs.ACLToken{ return true, &structs.ACLToken{
AccessorID: "5f57c1f6-6a89-4186-9445-531b316e01df", AccessorID: "5f57c1f6-6a89-4186-9445-531b316e01df",
@ -173,58 +138,6 @@ func testIdentityForToken(token string) (bool, structs.ACLIdentity, error) {
}, },
}, },
}, nil }, nil
case "found-synthetic-policy-1":
return true, &structs.ACLToken{
AccessorID: "f6c5a5fb-4da4-422b-9abf-2c942813fc71",
SecretID: "55cb7d69-2bea-42c3-a68f-2a1443d2abbc",
ServiceIdentities: []*structs.ACLServiceIdentity{
{
ServiceName: "service1",
},
},
}, nil
case "found-synthetic-policy-2":
return true, &structs.ACLToken{
AccessorID: "7c87dfad-be37-446e-8305-299585677cb5",
SecretID: "dfca9676-ac80-453a-837b-4c0cf923473c",
ServiceIdentities: []*structs.ACLServiceIdentity{
{
ServiceName: "service2",
},
},
}, nil
case "found-synthetic-policy-3":
return true, &structs.ACLToken{
AccessorID: "bebccc92-3987-489d-84c2-ffd00d93ef93",
SecretID: "de70f2e2-69d9-4e88-9815-f91c03c6bcb1",
NodeIdentities: []*structs.ACLNodeIdentity{
{
NodeName: "test-node1",
Datacenter: "dc1",
},
// as the resolver is in dc1 this identity should be ignored
{
NodeName: "test-node-dc2",
Datacenter: "dc2",
},
},
}, nil
case "found-synthetic-policy-4":
return true, &structs.ACLToken{
AccessorID: "359b9927-25fd-46b9-bd14-3470f848ec65",
SecretID: "83c4d500-847d-49f7-8c08-0483f6b4156e",
NodeIdentities: []*structs.ACLNodeIdentity{
{
NodeName: "test-node2",
Datacenter: "dc1",
},
// as the resolver is in dc1 this identity should be ignored
{
NodeName: "test-node-dc2",
Datacenter: "dc2",
},
},
}, nil
case "found-role-node-identity": case "found-role-node-identity":
return true, &structs.ACLToken{ return true, &structs.ACLToken{
AccessorID: "f3f47a09-de29-4c57-8f54-b65a9be79641", AccessorID: "f3f47a09-de29-4c57-8f54-b65a9be79641",
@ -291,18 +204,8 @@ func testIdentityForToken(token string) (bool, structs.ACLIdentity, error) {
}, },
}, },
}, nil }, nil
case anonymousToken:
return true, &structs.ACLToken{
AccessorID: "00000000-0000-0000-0000-000000000002",
SecretID: anonymousToken,
Policies: []structs.ACLTokenPolicyLink{
{
ID: "node-wr",
},
},
}, nil
default: default:
return testIdentityForTokenEnterprise(token) return true, nil, acl.ErrNotFound
} }
} }
@ -377,7 +280,7 @@ func testPolicyForID(policyID string) (bool, *structs.ACLPolicy, error) {
p.SetHash(false) p.SetHash(false)
return true, p, nil return true, p, nil
default: default:
return testPolicyForIDEnterprise(policyID) return true, nil, acl.ErrNotFound
} }
} }
@ -407,21 +310,6 @@ func testRoleForID(roleID string) (bool, *structs.ACLRole, error) {
}, },
RaftIndex: structs.RaftIndex{CreateIndex: 1, ModifyIndex: 2}, RaftIndex: structs.RaftIndex{CreateIndex: 1, ModifyIndex: 2},
}, nil }, nil
case "missing-policy":
return true, &structs.ACLRole{
ID: "missing-policy",
Name: "missing-policy",
Description: "missing-policy",
Policies: []structs.ACLRolePolicyLink{
{
ID: "not-found",
},
{
ID: "acl-ro",
},
},
RaftIndex: structs.RaftIndex{CreateIndex: 1, ModifyIndex: 2},
}, nil
case "found": case "found":
return true, &structs.ACLRole{ return true, &structs.ACLRole{
ID: "found", ID: "found",
@ -528,7 +416,7 @@ func testRoleForID(roleID string) (bool, *structs.ACLRole, error) {
}, },
}, nil }, nil
default: default:
return testRoleForIDEnterprise(roleID) return true, nil, acl.ErrNotFound
} }
} }
@ -549,6 +437,13 @@ type ACLResolverTestDelegate struct {
policyResolveFn func(*structs.ACLPolicyBatchGetRequest, *structs.ACLPolicyBatchResponse) error policyResolveFn func(*structs.ACLPolicyBatchGetRequest, *structs.ACLPolicyBatchResponse) error
roleResolveFn func(*structs.ACLRoleBatchGetRequest, *structs.ACLRoleBatchResponse) error roleResolveFn func(*structs.ACLRoleBatchGetRequest, *structs.ACLRoleBatchResponse) error
// testTokens is used by plainTokenReadFn if not nil
testTokens map[string]*structs.ACLToken
// testPolicies is used by plainPolicyResolveFn if not nil
testPolicies map[string]*structs.ACLPolicy
// testRoles is used by plainRoleResolveFn if not nil
testRoles map[string]*structs.ACLRole
localTokenResolutions int32 localTokenResolutions int32
remoteTokenResolutions int32 remoteTokenResolutions int32
localPolicyResolutions int32 localPolicyResolutions int32
@ -567,6 +462,51 @@ type ACLResolverTestDelegate struct {
EnterpriseACLResolverTestDelegate EnterpriseACLResolverTestDelegate
} }
// UseTestLocalData will force delegate-local maps to be used in lieu of the
// global factory functions.
func (d *ACLResolverTestDelegate) UseTestLocalData(data []interface{}) {
d.testTokens = make(map[string]*structs.ACLToken)
d.testPolicies = make(map[string]*structs.ACLPolicy)
d.testRoles = make(map[string]*structs.ACLRole)
var rest []interface{}
for _, item := range data {
switch x := item.(type) {
case *structs.ACLToken:
d.testTokens[x.SecretID] = x
case *structs.ACLPolicy:
d.testPolicies[x.ID] = x
case *structs.ACLRole:
d.testRoles[x.ID] = x
case string:
parts := strings.SplitN(x, ":", 2)
switch parts[0] {
case "token-not-found":
d.testTokens[parts[1]] = nil
case "policy-not-found":
d.testPolicies[parts[1]] = nil
case "role-not-found":
d.testRoles[parts[1]] = nil
default:
rest = append(rest, item)
}
default:
rest = append(rest, item)
}
}
d.EnterpriseACLResolverTestDelegate.UseTestLocalData(rest)
}
// UseDefaultData will force the global factory functions to be used instead of
// delegate-local maps.
func (d *ACLResolverTestDelegate) UseDefaultData() {
d.testTokens = nil
d.testPolicies = nil
d.testRoles = nil
d.EnterpriseACLResolverTestDelegate.UseDefaultData()
}
func (d *ACLResolverTestDelegate) Reset() { func (d *ACLResolverTestDelegate) Reset() {
d.tokenCached = false d.tokenCached = false
d.policyCached = false d.policyCached = false
@ -587,6 +527,17 @@ func (d *ACLResolverTestDelegate) defaultTokenReadFn(errAfterCached error) func(
} }
func (d *ACLResolverTestDelegate) plainTokenReadFn(args *structs.ACLTokenGetRequest, reply *structs.ACLTokenResponse) error { func (d *ACLResolverTestDelegate) plainTokenReadFn(args *structs.ACLTokenGetRequest, reply *structs.ACLTokenResponse) error {
if d.testTokens != nil {
token, ok := d.testTokens[args.TokenID]
if ok {
if token == nil {
return acl.ErrNotFound
}
reply.Token = token
}
return nil
}
_, token, err := testIdentityForToken(args.TokenID) _, token, err := testIdentityForToken(args.TokenID)
if token != nil { if token != nil {
reply.Token = token.(*structs.ACLToken) reply.Token = token.(*structs.ACLToken)
@ -611,9 +562,15 @@ func (d *ACLResolverTestDelegate) plainPolicyResolveFn(args *structs.ACLPolicyBa
// TODO: and possibly return a not-found or permission-denied here // TODO: and possibly return a not-found or permission-denied here
for _, policyID := range args.PolicyIDs { for _, policyID := range args.PolicyIDs {
_, policy, _ := testPolicyForID(policyID) if d.testPolicies != nil {
if policy != nil { if policy := d.testPolicies[policyID]; policy != nil {
reply.Policies = append(reply.Policies, policy) reply.Policies = append(reply.Policies, policy)
}
} else {
_, policy, _ := testPolicyForID(policyID)
if policy != nil {
reply.Policies = append(reply.Policies, policy)
}
} }
} }
@ -639,9 +596,15 @@ func (d *ACLResolverTestDelegate) plainRoleResolveFn(args *structs.ACLRoleBatchG
// TODO: and possibly return a not-found or permission-denied here // TODO: and possibly return a not-found or permission-denied here
for _, roleID := range args.RoleIDs { for _, roleID := range args.RoleIDs {
_, role, _ := testRoleForID(roleID) if d.testRoles != nil {
if role != nil { if role := d.testRoles[roleID]; role != nil {
reply.Roles = append(reply.Roles, role) reply.Roles = append(reply.Roles, role)
}
} else {
_, role, _ := testRoleForID(roleID)
if role != nil {
reply.Roles = append(reply.Roles, role)
}
} }
} }
@ -662,6 +625,14 @@ func (d *ACLResolverTestDelegate) ResolveIdentityFromToken(token string) (bool,
} }
atomic.AddInt32(&d.localTokenResolutions, 1) atomic.AddInt32(&d.localTokenResolutions, 1)
if d.testTokens != nil {
if token, ok := d.testTokens[token]; ok {
if token != nil {
return true, token, nil
}
}
return true, nil, acl.ErrNotFound
}
return testIdentityForToken(token) return testIdentityForToken(token)
} }
@ -671,6 +642,14 @@ func (d *ACLResolverTestDelegate) ResolvePolicyFromID(policyID string) (bool, *s
} }
atomic.AddInt32(&d.localPolicyResolutions, 1) atomic.AddInt32(&d.localPolicyResolutions, 1)
if d.testPolicies != nil {
if policy, ok := d.testPolicies[policyID]; ok {
if policy != nil {
return true, policy, nil
}
}
return true, nil, acl.ErrNotFound
}
return testPolicyForID(policyID) return testPolicyForID(policyID)
} }
@ -680,6 +659,14 @@ func (d *ACLResolverTestDelegate) ResolveRoleFromID(roleID string) (bool, *struc
} }
atomic.AddInt32(&d.localRoleResolutions, 1) atomic.AddInt32(&d.localRoleResolutions, 1)
if d.testRoles != nil {
if role, ok := d.testRoles[roleID]; ok {
if role != nil {
return true, role, nil
}
}
return true, nil, acl.ErrNotFound
}
return testRoleForID(roleID) return testRoleForID(roleID)
} }
@ -1762,6 +1749,7 @@ func testACLResolver_variousTokens(t *testing.T, delegate *ACLResolverTestDelega
} }
runTwiceAndReset("Missing Identity", func(t *testing.T) { runTwiceAndReset("Missing Identity", func(t *testing.T) {
delegate.UseTestLocalData(nil)
authz, err := r.ResolveToken("doesn't exist") authz, err := r.ResolveToken("doesn't exist")
require.Nil(t, authz) require.Nil(t, authz)
require.Error(t, err) require.Error(t, err)
@ -1769,6 +1757,25 @@ func testACLResolver_variousTokens(t *testing.T, delegate *ACLResolverTestDelega
}) })
runTwiceAndReset("Missing Policy", func(t *testing.T) { runTwiceAndReset("Missing Policy", func(t *testing.T) {
delegate.UseTestLocalData([]interface{}{
&structs.ACLToken{
AccessorID: "435a75af-1763-4980-89f4-f0951dda53b4",
SecretID: "missing-policy",
Policies: []structs.ACLTokenPolicyLink{
{ID: "not-found"},
{ID: "acl-ro"},
},
},
"policy-not-found:not-found",
&structs.ACLPolicy{
ID: "acl-ro",
Name: "acl-ro",
Description: "acl-ro",
Rules: `acl = "read"`,
Syntax: acl.SyntaxCurrent,
RaftIndex: structs.RaftIndex{CreateIndex: 1, ModifyIndex: 2},
},
})
authz := resolveToken(t, r, "missing-policy") authz := resolveToken(t, r, "missing-policy")
require.NotNil(t, authz) require.NotNil(t, authz)
require.Equal(t, acl.Allow, authz.ACLRead(nil)) require.Equal(t, acl.Allow, authz.ACLRead(nil))
@ -1776,6 +1783,33 @@ func testACLResolver_variousTokens(t *testing.T, delegate *ACLResolverTestDelega
}) })
runTwiceAndReset("Missing Role", func(t *testing.T) { runTwiceAndReset("Missing Role", func(t *testing.T) {
delegate.UseTestLocalData([]interface{}{
&structs.ACLToken{
AccessorID: "435a75af-1763-4980-89f4-f0951dda53b4",
SecretID: "missing-role",
Roles: []structs.ACLTokenRoleLink{
{ID: "not-found"},
{ID: "acl-ro"},
},
},
"role-not-found:not-found",
&structs.ACLRole{
ID: "acl-ro",
Name: "acl-ro",
Description: "acl-ro",
Policies: []structs.ACLRolePolicyLink{
{ID: "acl-ro"},
},
},
&structs.ACLPolicy{
ID: "acl-ro",
Name: "acl-ro",
Description: "acl-ro",
Rules: `acl = "read"`,
Syntax: acl.SyntaxCurrent,
RaftIndex: structs.RaftIndex{CreateIndex: 1, ModifyIndex: 2},
},
})
authz := resolveToken(t, r, "missing-role") authz := resolveToken(t, r, "missing-role")
require.NotNil(t, authz) require.NotNil(t, authz)
require.Equal(t, acl.Allow, authz.ACLRead(nil)) require.Equal(t, acl.Allow, authz.ACLRead(nil))
@ -1783,6 +1817,34 @@ func testACLResolver_variousTokens(t *testing.T, delegate *ACLResolverTestDelega
}) })
runTwiceAndReset("Missing Policy on Role", func(t *testing.T) { runTwiceAndReset("Missing Policy on Role", func(t *testing.T) {
delegate.UseTestLocalData([]interface{}{
&structs.ACLToken{
AccessorID: "435a75af-1763-4980-89f4-f0951dda53b4",
SecretID: "missing-policy-on-role",
Roles: []structs.ACLTokenRoleLink{
{ID: "missing-policy"},
},
},
&structs.ACLRole{
ID: "missing-policy",
Name: "missing-policy",
Description: "missing-policy",
Policies: []structs.ACLRolePolicyLink{
{ID: "not-found"},
{ID: "acl-ro"},
},
RaftIndex: structs.RaftIndex{CreateIndex: 1, ModifyIndex: 2},
},
"policy-not-found:not-found",
&structs.ACLPolicy{
ID: "acl-ro",
Name: "acl-ro",
Description: "acl-ro",
Rules: `acl = "read"`,
Syntax: acl.SyntaxCurrent,
RaftIndex: structs.RaftIndex{CreateIndex: 1, ModifyIndex: 2},
},
})
authz := resolveToken(t, r, "missing-policy-on-role") authz := resolveToken(t, r, "missing-policy-on-role")
require.NotNil(t, authz) require.NotNil(t, authz)
require.Equal(t, acl.Allow, authz.ACLRead(nil)) require.Equal(t, acl.Allow, authz.ACLRead(nil))
@ -1790,6 +1852,34 @@ func testACLResolver_variousTokens(t *testing.T, delegate *ACLResolverTestDelega
}) })
runTwiceAndReset("Normal with Policy", func(t *testing.T) { runTwiceAndReset("Normal with Policy", func(t *testing.T) {
delegate.UseTestLocalData([]interface{}{
&structs.ACLToken{
AccessorID: "5f57c1f6-6a89-4186-9445-531b316e01df",
SecretID: "found",
Policies: []structs.ACLTokenPolicyLink{
{ID: "node-wr"},
{ID: "dc2-key-wr"},
},
},
&structs.ACLPolicy{
ID: "node-wr",
Name: "node-wr",
Description: "node-wr",
Rules: `node_prefix "" { policy = "write"}`,
Syntax: acl.SyntaxCurrent,
Datacenters: []string{"dc1"},
RaftIndex: structs.RaftIndex{CreateIndex: 1, ModifyIndex: 2},
},
&structs.ACLPolicy{
ID: "dc2-key-wr",
Name: "dc2-key-wr",
Description: "dc2-key-wr",
Rules: `key_prefix "" { policy = "write"}`,
Syntax: acl.SyntaxCurrent,
Datacenters: []string{"dc2"},
RaftIndex: structs.RaftIndex{CreateIndex: 1, ModifyIndex: 2},
},
})
authz := resolveToken(t, r, "found") authz := resolveToken(t, r, "found")
require.NotNil(t, authz) require.NotNil(t, authz)
require.Equal(t, acl.Deny, authz.ACLRead(nil)) require.Equal(t, acl.Deny, authz.ACLRead(nil))
@ -1797,6 +1887,42 @@ func testACLResolver_variousTokens(t *testing.T, delegate *ACLResolverTestDelega
}) })
runTwiceAndReset("Normal with Role", func(t *testing.T) { runTwiceAndReset("Normal with Role", func(t *testing.T) {
delegate.UseTestLocalData([]interface{}{
&structs.ACLToken{
AccessorID: "5f57c1f6-6a89-4186-9445-531b316e01df",
SecretID: "found-role",
Roles: []structs.ACLTokenRoleLink{
{ID: "found"},
},
},
&structs.ACLRole{
ID: "found",
Name: "found",
Description: "found",
Policies: []structs.ACLRolePolicyLink{
{ID: "node-wr"},
{ID: "dc2-key-wr"},
},
},
&structs.ACLPolicy{
ID: "node-wr",
Name: "node-wr",
Description: "node-wr",
Rules: `node_prefix "" { policy = "write"}`,
Syntax: acl.SyntaxCurrent,
Datacenters: []string{"dc1"},
RaftIndex: structs.RaftIndex{CreateIndex: 1, ModifyIndex: 2},
},
&structs.ACLPolicy{
ID: "dc2-key-wr",
Name: "dc2-key-wr",
Description: "dc2-key-wr",
Rules: `key_prefix "" { policy = "write"}`,
Syntax: acl.SyntaxCurrent,
Datacenters: []string{"dc2"},
RaftIndex: structs.RaftIndex{CreateIndex: 1, ModifyIndex: 2},
},
})
authz := resolveToken(t, r, "found-role") authz := resolveToken(t, r, "found-role")
require.NotNil(t, authz) require.NotNil(t, authz)
require.Equal(t, acl.Deny, authz.ACLRead(nil)) require.Equal(t, acl.Deny, authz.ACLRead(nil))
@ -1804,6 +1930,54 @@ func testACLResolver_variousTokens(t *testing.T, delegate *ACLResolverTestDelega
}) })
runTwiceAndReset("Normal with Policy and Role", func(t *testing.T) { runTwiceAndReset("Normal with Policy and Role", func(t *testing.T) {
delegate.UseTestLocalData([]interface{}{
&structs.ACLToken{
AccessorID: "5f57c1f6-6a89-4186-9445-531b316e01df",
SecretID: "found-policy-and-role",
Policies: []structs.ACLTokenPolicyLink{
{ID: "node-wr"},
{ID: "dc2-key-wr"},
},
Roles: []structs.ACLTokenRoleLink{
{ID: "service-ro"},
},
},
&structs.ACLPolicy{
ID: "node-wr",
Name: "node-wr",
Description: "node-wr",
Rules: `node_prefix "" { policy = "write"}`,
Syntax: acl.SyntaxCurrent,
Datacenters: []string{"dc1"},
RaftIndex: structs.RaftIndex{CreateIndex: 1, ModifyIndex: 2},
},
&structs.ACLPolicy{
ID: "dc2-key-wr",
Name: "dc2-key-wr",
Description: "dc2-key-wr",
Rules: `key_prefix "" { policy = "write"}`,
Syntax: acl.SyntaxCurrent,
Datacenters: []string{"dc2"},
RaftIndex: structs.RaftIndex{CreateIndex: 1, ModifyIndex: 2},
},
&structs.ACLRole{
ID: "service-ro",
Name: "service-ro",
Description: "service-ro",
Policies: []structs.ACLRolePolicyLink{
{ID: "service-ro"},
},
RaftIndex: structs.RaftIndex{CreateIndex: 1, ModifyIndex: 2},
},
&structs.ACLPolicy{
ID: "service-ro",
Name: "service-ro",
Description: "service-ro",
Rules: `service_prefix "" { policy = "read" }`,
Syntax: acl.SyntaxCurrent,
RaftIndex: structs.RaftIndex{CreateIndex: 1, ModifyIndex: 2},
},
})
authz := resolveToken(t, r, "found-policy-and-role") authz := resolveToken(t, r, "found-policy-and-role")
require.NotNil(t, authz) require.NotNil(t, authz)
require.Equal(t, acl.Deny, authz.ACLRead(nil)) require.Equal(t, acl.Deny, authz.ACLRead(nil))
@ -1812,6 +1986,30 @@ func testACLResolver_variousTokens(t *testing.T, delegate *ACLResolverTestDelega
}) })
runTwiceAndReset("Role With Node Identity", func(t *testing.T) { runTwiceAndReset("Role With Node Identity", func(t *testing.T) {
delegate.UseTestLocalData([]interface{}{
&structs.ACLToken{
AccessorID: "f3f47a09-de29-4c57-8f54-b65a9be79641",
SecretID: "found-role-node-identity",
Roles: []structs.ACLTokenRoleLink{
{ID: "node-identity"},
},
},
&structs.ACLRole{
ID: "node-identity",
Name: "node-identity",
Description: "node-identity",
NodeIdentities: []*structs.ACLNodeIdentity{
{
NodeName: "test-node",
Datacenter: "dc1",
},
{
NodeName: "test-node-dc2",
Datacenter: "dc2",
},
},
},
})
authz := resolveToken(t, r, "found-role-node-identity") authz := resolveToken(t, r, "found-role-node-identity")
require.NotNil(t, authz) require.NotNil(t, authz)
require.Equal(t, acl.Allow, authz.NodeWrite("test-node", nil)) require.Equal(t, acl.Allow, authz.NodeWrite("test-node", nil))
@ -1821,10 +2019,57 @@ func testACLResolver_variousTokens(t *testing.T, delegate *ACLResolverTestDelega
}) })
runTwiceAndReset("Synthetic Policies Independently Cache", func(t *testing.T) { runTwiceAndReset("Synthetic Policies Independently Cache", func(t *testing.T) {
delegate.UseTestLocalData([]interface{}{
&structs.ACLToken{
AccessorID: "f6c5a5fb-4da4-422b-9abf-2c942813fc71",
SecretID: "found-synthetic-policy-1",
ServiceIdentities: []*structs.ACLServiceIdentity{
{ServiceName: "service1"},
},
},
&structs.ACLToken{
AccessorID: "7c87dfad-be37-446e-8305-299585677cb5",
SecretID: "found-synthetic-policy-2",
ServiceIdentities: []*structs.ACLServiceIdentity{
{ServiceName: "service2"},
},
},
&structs.ACLToken{
AccessorID: "bebccc92-3987-489d-84c2-ffd00d93ef93",
SecretID: "found-synthetic-policy-3",
NodeIdentities: []*structs.ACLNodeIdentity{
{
NodeName: "test-node1",
Datacenter: "dc1",
},
// as the resolver is in dc1 this identity should be ignored
{
NodeName: "test-node-dc2",
Datacenter: "dc2",
},
},
},
&structs.ACLToken{
AccessorID: "359b9927-25fd-46b9-bd14-3470f848ec65",
SecretID: "found-synthetic-policy-4",
NodeIdentities: []*structs.ACLNodeIdentity{
{
NodeName: "test-node2",
Datacenter: "dc1",
},
// as the resolver is in dc1 this identity should be ignored
{
NodeName: "test-node-dc2",
Datacenter: "dc2",
},
},
},
})
// We resolve these tokens in the same cache session // We resolve these tokens in the same cache session
// to verify that the keys for caching synthetic policies don't bleed // to verify that the keys for caching synthetic policies don't bleed
// over between each other. // over between each other.
{ t.Run("synthetic-policy-1", func(t *testing.T) { // service identity
authz, err := r.ResolveToken("found-synthetic-policy-1") authz, err := r.ResolveToken("found-synthetic-policy-1")
require.NotNil(t, authz) require.NotNil(t, authz)
require.NoError(t, err) require.NoError(t, err)
@ -1837,8 +2082,8 @@ func testACLResolver_variousTokens(t *testing.T, delegate *ACLResolverTestDelega
require.Equal(t, acl.Allow, authz.ServiceWrite("service1", nil)) require.Equal(t, acl.Allow, authz.ServiceWrite("service1", nil))
require.Equal(t, acl.Allow, authz.ServiceRead("literally-anything", nil)) require.Equal(t, acl.Allow, authz.ServiceRead("literally-anything", nil))
require.Equal(t, acl.Allow, authz.NodeRead("any-node", nil)) require.Equal(t, acl.Allow, authz.NodeRead("any-node", nil))
} })
{ t.Run("synthetic-policy-2", func(t *testing.T) { // service identity
authz, err := r.ResolveToken("found-synthetic-policy-2") authz, err := r.ResolveToken("found-synthetic-policy-2")
require.NotNil(t, authz) require.NotNil(t, authz)
require.NoError(t, err) require.NoError(t, err)
@ -1851,8 +2096,8 @@ func testACLResolver_variousTokens(t *testing.T, delegate *ACLResolverTestDelega
require.Equal(t, acl.Allow, authz.ServiceWrite("service2", nil)) require.Equal(t, acl.Allow, authz.ServiceWrite("service2", nil))
require.Equal(t, acl.Allow, authz.ServiceRead("literally-anything", nil)) require.Equal(t, acl.Allow, authz.ServiceRead("literally-anything", nil))
require.Equal(t, acl.Allow, authz.NodeRead("any-node", nil)) require.Equal(t, acl.Allow, authz.NodeRead("any-node", nil))
} })
{ t.Run("synthetic-policy-3", func(t *testing.T) { // node identity
authz, err := r.ResolveToken("found-synthetic-policy-3") authz, err := r.ResolveToken("found-synthetic-policy-3")
require.NoError(t, err) require.NoError(t, err)
require.NotNil(t, authz) require.NotNil(t, authz)
@ -1867,8 +2112,8 @@ func testACLResolver_variousTokens(t *testing.T, delegate *ACLResolverTestDelega
require.Equal(t, acl.Allow, authz.NodeWrite("test-node1", nil)) require.Equal(t, acl.Allow, authz.NodeWrite("test-node1", nil))
// ensure node identity for other DC is ignored // ensure node identity for other DC is ignored
require.Equal(t, acl.Deny, authz.NodeWrite("test-node-dc2", nil)) require.Equal(t, acl.Deny, authz.NodeWrite("test-node-dc2", nil))
} })
{ t.Run("synthetic-policy-4", func(t *testing.T) { // node identity
authz, err := r.ResolveToken("found-synthetic-policy-4") authz, err := r.ResolveToken("found-synthetic-policy-4")
require.NoError(t, err) require.NoError(t, err)
require.NotNil(t, authz) require.NotNil(t, authz)
@ -1883,10 +2128,28 @@ func testACLResolver_variousTokens(t *testing.T, delegate *ACLResolverTestDelega
require.Equal(t, acl.Allow, authz.NodeWrite("test-node2", nil)) require.Equal(t, acl.Allow, authz.NodeWrite("test-node2", nil))
// ensure node identity for other DC is ignored // ensure node identity for other DC is ignored
require.Equal(t, acl.Deny, authz.NodeWrite("test-node-dc2", nil)) require.Equal(t, acl.Deny, authz.NodeWrite("test-node-dc2", nil))
} })
}) })
runTwiceAndReset("Anonymous", func(t *testing.T) { runTwiceAndReset("Anonymous", func(t *testing.T) {
delegate.UseTestLocalData([]interface{}{
&structs.ACLToken{
AccessorID: "00000000-0000-0000-0000-000000000002",
SecretID: anonymousToken,
Policies: []structs.ACLTokenPolicyLink{
{ID: "node-wr"},
},
},
&structs.ACLPolicy{
ID: "node-wr",
Name: "node-wr",
Description: "node-wr",
Rules: `node_prefix "" { policy = "write"}`,
Syntax: acl.SyntaxCurrent,
Datacenters: []string{"dc1"},
RaftIndex: structs.RaftIndex{CreateIndex: 1, ModifyIndex: 2},
},
})
authz, err := r.ResolveToken("") authz, err := r.ResolveToken("")
require.NotNil(t, authz) require.NotNil(t, authz)
require.NoError(t, err) require.NoError(t, err)
@ -1895,6 +2158,13 @@ func testACLResolver_variousTokens(t *testing.T, delegate *ACLResolverTestDelega
}) })
runTwiceAndReset("legacy-management", func(t *testing.T) { runTwiceAndReset("legacy-management", func(t *testing.T) {
delegate.UseTestLocalData([]interface{}{
&structs.ACLToken{
AccessorID: "d109a033-99d1-47e2-a711-d6593373a973",
SecretID: "legacy-management",
Type: structs.ACLTokenTypeManagement,
},
})
authz, err := r.ResolveToken("legacy-management") authz, err := r.ResolveToken("legacy-management")
require.NotNil(t, authz) require.NotNil(t, authz)
require.NoError(t, err) require.NoError(t, err)
@ -1903,6 +2173,14 @@ func testACLResolver_variousTokens(t *testing.T, delegate *ACLResolverTestDelega
}) })
runTwiceAndReset("legacy-client", func(t *testing.T) { runTwiceAndReset("legacy-client", func(t *testing.T) {
delegate.UseTestLocalData([]interface{}{
&structs.ACLToken{
AccessorID: "b7375838-b104-4a25-b457-329d939bf257",
SecretID: "legacy-client",
Type: structs.ACLTokenTypeClient,
Rules: `service "" { policy = "read" }`,
},
})
authz, err := r.ResolveToken("legacy-client") authz, err := r.ResolveToken("legacy-client")
require.NoError(t, err) require.NoError(t, err)
require.NotNil(t, authz) require.NotNil(t, authz)

View File

@ -216,10 +216,10 @@ func (s *ACLNodeIdentity) EstimateSize() int {
return len(s.NodeName) + len(s.Datacenter) return len(s.NodeName) + len(s.Datacenter)
} }
func (s *ACLNodeIdentity) SyntheticPolicy() *ACLPolicy { func (s *ACLNodeIdentity) SyntheticPolicy(entMeta *EnterpriseMeta) *ACLPolicy {
// Given that we validate this string name before persisting, we do not // Given that we validate this string name before persisting, we do not
// have to escape it before doing the following interpolation. // have to escape it before doing the following interpolation.
rules := fmt.Sprintf(aclPolicyTemplateNodeIdentity, s.NodeName) rules := aclNodeIdentityRules(s.NodeName, entMeta)
hasher := fnv.New128a() hasher := fnv.New128a()
hashID := fmt.Sprintf("%x", hasher.Sum([]byte(rules))) hashID := fmt.Sprintf("%x", hasher.Sum([]byte(rules)))
@ -231,8 +231,7 @@ func (s *ACLNodeIdentity) SyntheticPolicy() *ACLPolicy {
policy.Rules = rules policy.Rules = rules
policy.Syntax = acl.SyntaxCurrent policy.Syntax = acl.SyntaxCurrent
policy.Datacenters = []string{s.Datacenter} policy.Datacenters = []string{s.Datacenter}
// TODO(partitions,acls): this needs to be fed the correct partition policy.EnterpriseMeta.Merge(entMeta)
policy.EnterpriseMeta = *DefaultEnterpriseMetaInDefaultPartition()
policy.SetHash(true) policy.SetHash(true)
return policy return policy
} }

View File

@ -62,6 +62,10 @@ func aclServiceIdentityRules(svc string, _ *EnterpriseMeta) string {
return fmt.Sprintf(aclPolicyTemplateServiceIdentity, svc) return fmt.Sprintf(aclPolicyTemplateServiceIdentity, svc)
} }
func aclNodeIdentityRules(node string, _ *EnterpriseMeta) string {
return fmt.Sprintf(aclPolicyTemplateNodeIdentity, node)
}
func (p *ACLPolicy) EnterprisePolicyMeta() *acl.EnterprisePolicyMeta { func (p *ACLPolicy) EnterprisePolicyMeta() *acl.EnterprisePolicyMeta {
return nil return nil
} }