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 08b222cfc3
commit 494764ee2d
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) {
policyIDs := identity.PolicyIDs()
roleIDs := identity.RoleIDs()
serviceIdentities := identity.ServiceIdentityList()
nodeIdentities := identity.NodeIdentityList()
var (
policyIDs = identity.PolicyIDs()
roleIDs = identity.RoleIDs()
serviceIdentities = identity.ServiceIdentityList()
nodeIdentities = identity.NodeIdentityList()
)
if len(policyIDs) == 0 && len(serviceIdentities) == 0 && len(roleIDs) == 0 && len(nodeIdentities) == 0 {
policy := identity.EmbeddedPolicy()
@ -794,7 +796,7 @@ func (r *ACLResolver) resolvePoliciesForIdentity(identity structs.ACLIdentity) (
// Generate synthetic policies for all service identities in effect.
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
// we only attempt to resolve policies locally
@ -805,6 +807,7 @@ func (r *ACLResolver) resolvePoliciesForIdentity(identity structs.ACLIdentity) (
policies = append(policies, syntheticPolicies...)
filtered := r.filterPoliciesByScope(policies)
// TODO(partitions,acls): filter these by the partition/namespace of the token trying to use them?
return filtered, nil
}
@ -821,14 +824,14 @@ func (r *ACLResolver) synthesizePoliciesForServiceIdentities(serviceIdentities [
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 {
return nil
}
syntheticPolicies := make([]*structs.ACLPolicy, 0, len(nodeIdentities))
for _, n := range nodeIdentities {
syntheticPolicies = append(syntheticPolicies, n.SyntheticPolicy())
syntheticPolicies = append(syntheticPolicies, n.SyntheticPolicy(entMeta))
}
return syntheticPolicies
@ -1242,6 +1245,7 @@ func (r *ACLResolver) ResolveTokenToIdentityAndAuthorizer(token string) (structs
}
if r.delegate.UseLegacyACLs() {
// TODO(partitions,acls): do we have to care about legacy acls?
identity, authorizer, err := r.resolveTokenLegacy(token)
r.handleACLDisabledError(err)
return identity, authorizer, err

View File

@ -3,6 +3,8 @@
package consul
import (
"fmt"
"github.com/hashicorp/consul/acl"
"github.com/hashicorp/consul/agent/structs"
)
@ -26,3 +28,10 @@ type EnterpriseACLResolverTestDelegate struct{}
func (d *EnterpriseACLResolverTestDelegate) RPC(string, interface{}, interface{}) (bool, error) {
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"
"os"
"reflect"
"strings"
"sync/atomic"
"testing"
"time"
@ -81,19 +82,6 @@ func resolveToken(t *testing.T, r *ACLResolver, token string) acl.Authorizer {
func testIdentityForToken(token string) (bool, structs.ACLIdentity, error) {
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":
return true, &structs.ACLToken{
AccessorID: "435a75af-1763-4980-89f4-f0951dda53b4",
@ -107,29 +95,6 @@ func testIdentityForToken(token string) (bool, structs.ACLIdentity, error) {
},
},
}, 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":
return true, &structs.ACLToken{
AccessorID: "5f57c1f6-6a89-4186-9445-531b316e01df",
@ -173,58 +138,6 @@ func testIdentityForToken(token string) (bool, structs.ACLIdentity, error) {
},
},
}, 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":
return true, &structs.ACLToken{
AccessorID: "f3f47a09-de29-4c57-8f54-b65a9be79641",
@ -291,18 +204,8 @@ func testIdentityForToken(token string) (bool, structs.ACLIdentity, error) {
},
},
}, nil
case anonymousToken:
return true, &structs.ACLToken{
AccessorID: "00000000-0000-0000-0000-000000000002",
SecretID: anonymousToken,
Policies: []structs.ACLTokenPolicyLink{
{
ID: "node-wr",
},
},
}, nil
default:
return testIdentityForTokenEnterprise(token)
return true, nil, acl.ErrNotFound
}
}
@ -377,7 +280,7 @@ func testPolicyForID(policyID string) (bool, *structs.ACLPolicy, error) {
p.SetHash(false)
return true, p, nil
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},
}, 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":
return true, &structs.ACLRole{
ID: "found",
@ -528,7 +416,7 @@ func testRoleForID(roleID string) (bool, *structs.ACLRole, error) {
},
}, nil
default:
return testRoleForIDEnterprise(roleID)
return true, nil, acl.ErrNotFound
}
}
@ -549,6 +437,13 @@ type ACLResolverTestDelegate struct {
policyResolveFn func(*structs.ACLPolicyBatchGetRequest, *structs.ACLPolicyBatchResponse) 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
remoteTokenResolutions int32
localPolicyResolutions int32
@ -567,6 +462,51 @@ type ACLResolverTestDelegate struct {
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() {
d.tokenCached = 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 {
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)
if token != nil {
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
for _, policyID := range args.PolicyIDs {
_, policy, _ := testPolicyForID(policyID)
if policy != nil {
reply.Policies = append(reply.Policies, policy)
if d.testPolicies != nil {
if policy := d.testPolicies[policyID]; policy != nil {
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
for _, roleID := range args.RoleIDs {
_, role, _ := testRoleForID(roleID)
if role != nil {
reply.Roles = append(reply.Roles, role)
if d.testRoles != nil {
if role := d.testRoles[roleID]; role != nil {
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)
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)
}
@ -671,6 +642,14 @@ func (d *ACLResolverTestDelegate) ResolvePolicyFromID(policyID string) (bool, *s
}
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)
}
@ -680,6 +659,14 @@ func (d *ACLResolverTestDelegate) ResolveRoleFromID(roleID string) (bool, *struc
}
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)
}
@ -1762,6 +1749,7 @@ func testACLResolver_variousTokens(t *testing.T, delegate *ACLResolverTestDelega
}
runTwiceAndReset("Missing Identity", func(t *testing.T) {
delegate.UseTestLocalData(nil)
authz, err := r.ResolveToken("doesn't exist")
require.Nil(t, authz)
require.Error(t, err)
@ -1769,6 +1757,25 @@ func testACLResolver_variousTokens(t *testing.T, delegate *ACLResolverTestDelega
})
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")
require.NotNil(t, authz)
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) {
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")
require.NotNil(t, authz)
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) {
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")
require.NotNil(t, authz)
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) {
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")
require.NotNil(t, authz)
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) {
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")
require.NotNil(t, authz)
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) {
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")
require.NotNil(t, authz)
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) {
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")
require.NotNil(t, authz)
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) {
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
// to verify that the keys for caching synthetic policies don't bleed
// over between each other.
{
t.Run("synthetic-policy-1", func(t *testing.T) { // service identity
authz, err := r.ResolveToken("found-synthetic-policy-1")
require.NotNil(t, authz)
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.ServiceRead("literally-anything", 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")
require.NotNil(t, authz)
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.ServiceRead("literally-anything", 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")
require.NoError(t, err)
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))
// ensure node identity for other DC is ignored
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")
require.NoError(t, err)
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))
// ensure node identity for other DC is ignored
require.Equal(t, acl.Deny, authz.NodeWrite("test-node-dc2", nil))
}
})
})
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("")
require.NotNil(t, authz)
require.NoError(t, err)
@ -1895,6 +2158,13 @@ func testACLResolver_variousTokens(t *testing.T, delegate *ACLResolverTestDelega
})
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")
require.NotNil(t, authz)
require.NoError(t, err)
@ -1903,6 +2173,14 @@ func testACLResolver_variousTokens(t *testing.T, delegate *ACLResolverTestDelega
})
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")
require.NoError(t, err)
require.NotNil(t, authz)

View File

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

View File

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