acl: extract a backend type for the ACLResolverBackend

This is a small step to isolate the functionality that is used for the
ACLResolver from the large Client and Server structs.
This commit is contained in:
Daniel Nephin 2021-07-30 19:20:02 -04:00
parent 19a67d8768
commit 737c0097e0
7 changed files with 47 additions and 34 deletions

View File

@ -133,11 +133,12 @@ func tokenSecretCacheID(token string) string {
return "token-secret:" + token return "token-secret:" + token
} }
type ACLResolverDelegate interface { type ACLResolverBackend interface {
ACLDatacenter() string ACLDatacenter() string
ResolveIdentityFromToken(token string) (bool, structs.ACLIdentity, error) ResolveIdentityFromToken(token string) (bool, structs.ACLIdentity, error)
ResolvePolicyFromID(policyID string) (bool, *structs.ACLPolicy, error) ResolvePolicyFromID(policyID string) (bool, *structs.ACLPolicy, error)
ResolveRoleFromID(roleID string) (bool, *structs.ACLRole, error) ResolveRoleFromID(roleID string) (bool, *structs.ACLRole, error)
// TODO: separate methods for each RPC call (there are 4)
RPC(method string, args interface{}, reply interface{}) error RPC(method string, args interface{}, reply interface{}) error
EnterpriseACLResolverDelegate EnterpriseACLResolverDelegate
} }
@ -160,8 +161,9 @@ type ACLResolverConfig struct {
// CacheConfig is a pass through configuration for ACL cache limits // CacheConfig is a pass through configuration for ACL cache limits
CacheConfig *structs.ACLCachesConfig CacheConfig *structs.ACLCachesConfig
// Delegate that implements some helper functionality that is server/client specific // Backend is used to retrieve data from the state store, or perform RPCs
Delegate ACLResolverDelegate // to fetch data from other Datacenters.
Backend ACLResolverBackend
// DisableDuration is the length of time to leave ACLs disabled when an RPC // DisableDuration is the length of time to leave ACLs disabled when an RPC
// request to a server indicates that the ACL system is disabled. If set to // request to a server indicates that the ACL system is disabled. If set to
@ -219,9 +221,9 @@ type ACLResolverSettings struct {
// ACLResolver is the type to handle all your token and policy resolution needs. // ACLResolver is the type to handle all your token and policy resolution needs.
// //
// Supports: // Supports:
// - Resolving tokens locally via the ACLResolverDelegate // - Resolving tokens locally via the ACLResolverBackend
// - Resolving policies locally via the ACLResolverDelegate // - Resolving policies locally via the ACLResolverBackend
// - Resolving roles locally via the ACLResolverDelegate // - Resolving roles locally via the ACLResolverBackend
// - Resolving legacy tokens remotely via an ACL.GetPolicy RPC // - Resolving legacy tokens remotely via an ACL.GetPolicy RPC
// - Resolving tokens remotely via an ACL.TokenRead RPC // - Resolving tokens remotely via an ACL.TokenRead RPC
// - Resolving policies remotely via an ACL.PolicyResolve RPC // - Resolving policies remotely via an ACL.PolicyResolve RPC
@ -245,7 +247,7 @@ type ACLResolver struct {
config ACLResolverSettings config ACLResolverSettings
logger hclog.Logger logger hclog.Logger
delegate ACLResolverDelegate backend ACLResolverBackend
aclConf *acl.Config aclConf *acl.Config
tokens *token.Store tokens *token.Store
@ -298,7 +300,7 @@ func NewACLResolver(config *ACLResolverConfig) (*ACLResolver, error) {
if config == nil { if config == nil {
return nil, fmt.Errorf("ACL Resolver must be initialized with a config") return nil, fmt.Errorf("ACL Resolver must be initialized with a config")
} }
if config.Delegate == nil { if config.Backend == nil {
return nil, fmt.Errorf("ACL Resolver must be initialized with a valid delegate") return nil, fmt.Errorf("ACL Resolver must be initialized with a valid delegate")
} }
@ -331,7 +333,7 @@ func NewACLResolver(config *ACLResolverConfig) (*ACLResolver, error) {
return &ACLResolver{ return &ACLResolver{
config: config.Config, config: config.Config,
logger: config.Logger.Named(logging.ACL), logger: config.Logger.Named(logging.ACL),
delegate: config.Delegate, backend: config.Backend,
aclConf: config.ACLConfig, aclConf: config.ACLConfig,
cache: cache, cache: cache,
disableDuration: config.DisableDuration, disableDuration: config.DisableDuration,
@ -349,7 +351,7 @@ func (r *ACLResolver) fetchAndCacheIdentityFromToken(token string, cached *struc
cacheID := tokenSecretCacheID(token) cacheID := tokenSecretCacheID(token)
req := structs.ACLTokenGetRequest{ req := structs.ACLTokenGetRequest{
Datacenter: r.delegate.ACLDatacenter(), Datacenter: r.backend.ACLDatacenter(),
TokenID: token, TokenID: token,
TokenIDType: structs.ACLTokenSecret, TokenIDType: structs.ACLTokenSecret,
QueryOptions: structs.QueryOptions{ QueryOptions: structs.QueryOptions{
@ -359,7 +361,7 @@ func (r *ACLResolver) fetchAndCacheIdentityFromToken(token string, cached *struc
} }
var resp structs.ACLTokenResponse var resp structs.ACLTokenResponse
err := r.delegate.RPC("ACL.TokenRead", &req, &resp) err := r.backend.RPC("ACL.TokenRead", &req, &resp)
if err == nil { if err == nil {
if resp.Token == nil { if resp.Token == nil {
r.cache.PutIdentity(cacheID, nil) r.cache.PutIdentity(cacheID, nil)
@ -396,7 +398,7 @@ func (r *ACLResolver) fetchAndCacheIdentityFromToken(token string, cached *struc
// we initiate an RPC for the value. // we initiate an RPC for the value.
func (r *ACLResolver) resolveIdentityFromToken(token string) (structs.ACLIdentity, error) { func (r *ACLResolver) resolveIdentityFromToken(token string) (structs.ACLIdentity, error) {
// Attempt to resolve locally first (local results are not cached) // Attempt to resolve locally first (local results are not cached)
if done, identity, err := r.delegate.ResolveIdentityFromToken(token); done { if done, identity, err := r.backend.ResolveIdentityFromToken(token); done {
return identity, err return identity, err
} }
@ -437,7 +439,7 @@ func (r *ACLResolver) resolveIdentityFromToken(token string) (structs.ACLIdentit
func (r *ACLResolver) fetchAndCachePoliciesForIdentity(identity structs.ACLIdentity, policyIDs []string, cached map[string]*structs.PolicyCacheEntry) (map[string]*structs.ACLPolicy, error) { func (r *ACLResolver) fetchAndCachePoliciesForIdentity(identity structs.ACLIdentity, policyIDs []string, cached map[string]*structs.PolicyCacheEntry) (map[string]*structs.ACLPolicy, error) {
req := structs.ACLPolicyBatchGetRequest{ req := structs.ACLPolicyBatchGetRequest{
Datacenter: r.delegate.ACLDatacenter(), Datacenter: r.backend.ACLDatacenter(),
PolicyIDs: policyIDs, PolicyIDs: policyIDs,
QueryOptions: structs.QueryOptions{ QueryOptions: structs.QueryOptions{
Token: identity.SecretToken(), Token: identity.SecretToken(),
@ -446,7 +448,7 @@ func (r *ACLResolver) fetchAndCachePoliciesForIdentity(identity structs.ACLIdent
} }
var resp structs.ACLPolicyBatchResponse var resp structs.ACLPolicyBatchResponse
err := r.delegate.RPC("ACL.PolicyResolve", &req, &resp) err := r.backend.RPC("ACL.PolicyResolve", &req, &resp)
if err == nil { if err == nil {
out := make(map[string]*structs.ACLPolicy) out := make(map[string]*structs.ACLPolicy)
for _, policy := range resp.Policies { for _, policy := range resp.Policies {
@ -492,7 +494,7 @@ func (r *ACLResolver) fetchAndCachePoliciesForIdentity(identity structs.ACLIdent
func (r *ACLResolver) fetchAndCacheRolesForIdentity(identity structs.ACLIdentity, roleIDs []string, cached map[string]*structs.RoleCacheEntry) (map[string]*structs.ACLRole, error) { func (r *ACLResolver) fetchAndCacheRolesForIdentity(identity structs.ACLIdentity, roleIDs []string, cached map[string]*structs.RoleCacheEntry) (map[string]*structs.ACLRole, error) {
req := structs.ACLRoleBatchGetRequest{ req := structs.ACLRoleBatchGetRequest{
Datacenter: r.delegate.ACLDatacenter(), Datacenter: r.backend.ACLDatacenter(),
RoleIDs: roleIDs, RoleIDs: roleIDs,
QueryOptions: structs.QueryOptions{ QueryOptions: structs.QueryOptions{
Token: identity.SecretToken(), Token: identity.SecretToken(),
@ -501,7 +503,7 @@ func (r *ACLResolver) fetchAndCacheRolesForIdentity(identity structs.ACLIdentity
} }
var resp structs.ACLRoleBatchResponse var resp structs.ACLRoleBatchResponse
err := r.delegate.RPC("ACL.RoleResolve", &req, &resp) err := r.backend.RPC("ACL.RoleResolve", &req, &resp)
if err == nil { if err == nil {
out := make(map[string]*structs.ACLRole) out := make(map[string]*structs.ACLRole)
for _, role := range resp.Roles { for _, role := range resp.Roles {
@ -774,7 +776,7 @@ func (r *ACLResolver) collectPoliciesForIdentity(identity structs.ACLIdentity, p
} }
for _, policyID := range policyIDs { for _, policyID := range policyIDs {
if done, policy, err := r.delegate.ResolvePolicyFromID(policyID); done { if done, policy, err := r.backend.ResolvePolicyFromID(policyID); done {
if err != nil && !acl.IsErrNotFound(err) { if err != nil && !acl.IsErrNotFound(err) {
return nil, err return nil, err
} }
@ -871,7 +873,7 @@ func (r *ACLResolver) collectRolesForIdentity(identity structs.ACLIdentity, role
expCacheMap := make(map[string]*structs.RoleCacheEntry) expCacheMap := make(map[string]*structs.RoleCacheEntry)
for _, roleID := range roleIDs { for _, roleID := range roleIDs {
if done, role, err := r.delegate.ResolveRoleFromID(roleID); done { if done, role, err := r.backend.ResolveRoleFromID(roleID); done {
if err != nil && !acl.IsErrNotFound(err) { if err != nil && !acl.IsErrNotFound(err) {
return nil, err return nil, err
} }

View File

@ -5,7 +5,7 @@ import (
"github.com/hashicorp/consul/agent/structs" "github.com/hashicorp/consul/agent/structs"
) )
var clientACLCacheConfig *structs.ACLCachesConfig = &structs.ACLCachesConfig{ var clientACLCacheConfig = &structs.ACLCachesConfig{
// The ACL cache configuration on client agents is more conservative than // The ACL cache configuration on client agents is more conservative than
// on the servers. It is assumed that individual client agents will have // on the servers. It is assumed that individual client agents will have
// fewer distinct identities accessing the client than a server would // fewer distinct identities accessing the client than a server would
@ -23,23 +23,28 @@ var clientACLCacheConfig *structs.ACLCachesConfig = &structs.ACLCachesConfig{
Roles: 128, Roles: 128,
} }
func (c *Client) ACLDatacenter() string { type clientACLResolverBackend struct {
// For resolution running on clients, servers within the current datacenter // TODO: un-embed
*Client
}
func (c *clientACLResolverBackend) ACLDatacenter() string {
// For resolution running on clients servers within the current datacenter
// must be queried first to pick up local tokens. // must be queried first to pick up local tokens.
return c.config.Datacenter return c.config.Datacenter
} }
func (c *Client) ResolveIdentityFromToken(token string) (bool, structs.ACLIdentity, error) { func (c *clientACLResolverBackend) ResolveIdentityFromToken(token string) (bool, structs.ACLIdentity, error) {
// clients do no local identity resolution at the moment // clients do no local identity resolution at the moment
return false, nil, nil return false, nil, nil
} }
func (c *Client) ResolvePolicyFromID(policyID string) (bool, *structs.ACLPolicy, error) { func (c *clientACLResolverBackend) ResolvePolicyFromID(policyID string) (bool, *structs.ACLPolicy, error) {
// clients do no local policy resolution at the moment // clients do no local policy resolution at the moment
return false, nil, nil return false, nil, nil
} }
func (c *Client) ResolveRoleFromID(roleID string) (bool, *structs.ACLRole, error) { func (c *clientACLResolverBackend) ResolveRoleFromID(roleID string) (bool, *structs.ACLRole, error) {
// clients do no local role resolution at the moment // clients do no local role resolution at the moment
return false, nil, nil return false, nil, nil
} }

View File

@ -100,9 +100,14 @@ func (s *Server) LocalTokensEnabled() bool {
return true return true
} }
func (s *Server) ACLDatacenter() string { type serverACLResolverBackend struct {
// For resolution running on servers the only option // TODO: un-embed
// is to contact the configured ACL Datacenter *Server
}
func (s *serverACLResolverBackend) ACLDatacenter() string {
// For resolution running on servers the only option is to contact the
// configured ACL Datacenter
if s.config.PrimaryDatacenter != "" { if s.config.PrimaryDatacenter != "" {
return s.config.PrimaryDatacenter return s.config.PrimaryDatacenter
} }
@ -114,6 +119,7 @@ func (s *Server) ACLDatacenter() string {
} }
// ResolveIdentityFromToken retrieves a token's full identity given its secretID. // ResolveIdentityFromToken retrieves a token's full identity given its secretID.
// TODO: why does some code call this directly instead of using ACLResolver.ResolveTokenToIdentity ?
func (s *Server) ResolveIdentityFromToken(token string) (bool, structs.ACLIdentity, error) { func (s *Server) ResolveIdentityFromToken(token string) (bool, structs.ACLIdentity, error) {
// only allow remote RPC resolution when token replication is off and // only allow remote RPC resolution when token replication is off and
// when not in the ACL datacenter // when not in the ACL datacenter
@ -131,7 +137,7 @@ func (s *Server) ResolveIdentityFromToken(token string) (bool, structs.ACLIdenti
return s.InPrimaryDatacenter() || index > 0, nil, acl.ErrNotFound return s.InPrimaryDatacenter() || index > 0, nil, acl.ErrNotFound
} }
func (s *Server) ResolvePolicyFromID(policyID string) (bool, *structs.ACLPolicy, error) { func (s *serverACLResolverBackend) ResolvePolicyFromID(policyID string) (bool, *structs.ACLPolicy, error) {
index, policy, err := s.fsm.State().ACLPolicyGetByID(nil, policyID, nil) index, policy, err := s.fsm.State().ACLPolicyGetByID(nil, policyID, nil)
if err != nil { if err != nil {
return true, nil, err return true, nil, err
@ -145,7 +151,7 @@ func (s *Server) ResolvePolicyFromID(policyID string) (bool, *structs.ACLPolicy,
return s.InPrimaryDatacenter() || index > 0, policy, acl.ErrNotFound return s.InPrimaryDatacenter() || index > 0, policy, acl.ErrNotFound
} }
func (s *Server) ResolveRoleFromID(roleID string) (bool, *structs.ACLRole, error) { func (s *serverACLResolverBackend) ResolveRoleFromID(roleID string) (bool, *structs.ACLRole, error) {
index, role, err := s.fsm.State().ACLRoleGetByID(nil, roleID, nil) index, role, err := s.fsm.State().ACLRoleGetByID(nil, roleID, nil)
if err != nil { if err != nil {
return true, nil, err return true, nil, err

View File

@ -715,7 +715,7 @@ func newTestACLResolver(t *testing.T, delegate *ACLResolverTestDelegate, cb func
Roles: 4, Roles: 4,
}, },
DisableDuration: aclClientDisabledTTL, DisableDuration: aclClientDisabledTTL,
Delegate: delegate, Backend: delegate,
} }
if cb != nil { if cb != nil {

View File

@ -119,7 +119,7 @@ func NewClient(config *Config, deps Deps) (*Client, error) {
aclConfig := ACLResolverConfig{ aclConfig := ACLResolverConfig{
Config: config.ACLResolverSettings, Config: config.ACLResolverSettings,
Delegate: c, Backend: &clientACLResolverBackend{Client: c},
Logger: c.logger, Logger: c.logger,
DisableDuration: aclClientDisabledTTL, DisableDuration: aclClientDisabledTTL,
CacheConfig: clientACLCacheConfig, CacheConfig: clientACLCacheConfig,

View File

@ -110,7 +110,7 @@ func NewReplicator(config *ReplicatorConfig) (*Replicator, error) {
return nil, fmt.Errorf("Cannot create the Replicator without a config") return nil, fmt.Errorf("Cannot create the Replicator without a config")
} }
if config.Delegate == nil { if config.Delegate == nil {
return nil, fmt.Errorf("Cannot create the Replicator without a Delegate set in the config") return nil, fmt.Errorf("Cannot create the Replicator without a Backend set in the config")
} }
if config.Logger == nil { if config.Logger == nil {
logger := hclog.New(&hclog.LoggerOptions{}) logger := hclog.New(&hclog.LoggerOptions{})

View File

@ -450,7 +450,7 @@ func NewServer(config *Config, flat Deps) (*Server, error) {
s.aclConfig = newACLConfig(partitionInfo, logger) s.aclConfig = newACLConfig(partitionInfo, logger)
aclConfig := ACLResolverConfig{ aclConfig := ACLResolverConfig{
Config: config.ACLResolverSettings, Config: config.ACLResolverSettings,
Delegate: s, Backend: &serverACLResolverBackend{Server: s},
CacheConfig: serverACLCacheConfig, CacheConfig: serverACLCacheConfig,
Logger: logger, Logger: logger,
ACLConfig: s.aclConfig, ACLConfig: s.aclConfig,