2014-08-08 22:32:43 +00:00
package consul
import (
2022-12-14 15:24:22 +00:00
"context"
2015-06-11 23:46:15 +00:00
"fmt"
2019-04-08 18:19:09 +00:00
"sort"
2018-06-28 07:06:14 +00:00
"sync"
2014-08-08 22:32:43 +00:00
"time"
2020-11-13 02:12:12 +00:00
"github.com/armon/go-metrics"
"github.com/armon/go-metrics/prometheus"
2020-01-28 23:50:41 +00:00
"github.com/hashicorp/go-hclog"
2019-03-14 14:35:34 +00:00
"golang.org/x/sync/singleflight"
2018-10-19 16:04:07 +00:00
"golang.org/x/time/rate"
2020-11-17 22:10:21 +00:00
"github.com/hashicorp/consul/acl"
2022-06-17 09:24:43 +00:00
"github.com/hashicorp/consul/acl/resolver"
2020-11-17 22:10:21 +00:00
"github.com/hashicorp/consul/agent/structs"
2022-07-01 15:18:33 +00:00
"github.com/hashicorp/consul/agent/structs/aclfilter"
2021-04-14 16:39:35 +00:00
"github.com/hashicorp/consul/agent/token"
2020-11-17 22:10:21 +00:00
"github.com/hashicorp/consul/logging"
2014-08-08 22:52:52 +00:00
)
2020-11-13 02:12:12 +00:00
var ACLCounters = [ ] prometheus . CounterDefinition {
{
2020-11-13 21:18:04 +00:00
Name : [ ] string { "acl" , "token" , "cache_hit" } ,
2020-11-19 21:29:44 +00:00
Help : "Increments if Consul is able to resolve a token's identity, or a legacy token, from the cache." ,
2020-11-13 02:12:12 +00:00
} ,
{
2020-11-13 21:18:04 +00:00
Name : [ ] string { "acl" , "token" , "cache_miss" } ,
2020-11-19 21:29:44 +00:00
Help : "Increments if Consul cannot resolve a token's identity, or a legacy token, from the cache." ,
2020-11-13 02:12:12 +00:00
} ,
}
var ACLSummaries = [ ] prometheus . SummaryDefinition {
{
2020-11-13 21:18:04 +00:00
Name : [ ] string { "acl" , "ResolveToken" } ,
2020-11-19 21:06:28 +00:00
Help : "This measures the time it takes to resolve an ACL token." ,
2020-11-13 02:12:12 +00:00
} ,
}
2016-12-14 07:21:14 +00:00
// These must be kept in sync with the constants in command/agent/acl.go.
2014-08-08 22:52:52 +00:00
const (
2023-02-07 18:26:30 +00:00
// anonymousToken is the token SecretID we re-write to if there is no token ID
2016-12-14 07:21:14 +00:00
// provided.
2014-08-11 21:54:18 +00:00
anonymousToken = "anonymous"
2015-06-18 23:18:10 +00:00
2019-04-08 17:05:51 +00:00
// aclTokenReapingRateLimit is the number of batch token reaping requests per second allowed.
aclTokenReapingRateLimit rate . Limit = 1.0
// aclTokenReapingBurst is the number of batch token reaping requests per second
// that can burst after a period of idleness.
aclTokenReapingBurst = 5
2018-10-19 16:04:07 +00:00
// aclBatchDeleteSize is the number of deletions to send in a single batch operation. 4096 should produce a batch that is <150KB
// in size but should be sufficiently large to handle 1 replication round in a single batch
aclBatchDeleteSize = 4096
// aclBatchUpsertSize is the target size in bytes we want to submit for a batch upsert request. We estimate the size at runtime
// due to the data being more variable in its size.
aclBatchUpsertSize = 256 * 1024
2019-01-22 18:14:43 +00:00
// Maximum number of re-resolution requests to be made if the token is modified between
// resolving the token and resolving its policies that would remove one of its policies.
tokenPolicyResolutionMaxRetries = 5
2019-04-15 20:43:19 +00:00
// Maximum number of re-resolution requests to be made if the token is modified between
// resolving the token and resolving its roles that would remove one of its roles.
tokenRoleResolutionMaxRetries = 5
2014-08-08 22:32:43 +00:00
)
2019-12-18 18:46:53 +00:00
// missingIdentity is used to return some identity in the event that the real identity cannot be ascertained
type missingIdentity struct {
reason string
token string
}
func ( id * missingIdentity ) ID ( ) string {
return id . reason
}
func ( id * missingIdentity ) SecretToken ( ) string {
return id . token
}
func ( id * missingIdentity ) PolicyIDs ( ) [ ] string {
return nil
}
func ( id * missingIdentity ) RoleIDs ( ) [ ] string {
return nil
}
func ( id * missingIdentity ) ServiceIdentityList ( ) [ ] * structs . ACLServiceIdentity {
return nil
}
2020-06-16 16:54:27 +00:00
func ( id * missingIdentity ) NodeIdentityList ( ) [ ] * structs . ACLNodeIdentity {
return nil
}
2019-12-18 18:46:53 +00:00
func ( id * missingIdentity ) IsExpired ( asOf time . Time ) bool {
return false
}
2020-03-10 16:15:22 +00:00
func ( id * missingIdentity ) IsLocal ( ) bool {
return false
}
2022-04-05 21:10:06 +00:00
func ( id * missingIdentity ) EnterpriseMetadata ( ) * acl . EnterpriseMeta {
2021-07-22 18:20:45 +00:00
return structs . DefaultEnterpriseMetaInDefaultPartition ( )
2019-12-18 18:46:53 +00:00
}
2018-10-19 16:04:07 +00:00
type ACLRemoteError struct {
Err error
}
2014-08-12 17:38:57 +00:00
2018-10-19 16:04:07 +00:00
func ( e ACLRemoteError ) Error ( ) string {
return fmt . Sprintf ( "Error communicating with the ACL Datacenter: %v" , e . Err )
}
2014-08-12 17:38:57 +00:00
2018-10-19 16:04:07 +00:00
func IsACLRemoteError ( err error ) bool {
_ , ok := err . ( ACLRemoteError )
return ok
2014-08-08 22:32:43 +00:00
}
2019-10-25 17:05:43 +00:00
func tokenSecretCacheID ( token string ) string {
return "token-secret:" + token
}
2021-07-30 23:20:02 +00:00
type ACLResolverBackend interface {
2021-10-04 22:54:49 +00:00
ACLDatacenter ( ) string
2018-10-19 16:04:07 +00:00
ResolveIdentityFromToken ( token string ) ( bool , structs . ACLIdentity , error )
ResolvePolicyFromID ( policyID string ) ( bool , * structs . ACLPolicy , error )
2019-04-15 20:43:19 +00:00
ResolveRoleFromID ( roleID string ) ( bool , * structs . ACLRole , error )
2022-09-09 19:05:38 +00:00
IsServerManagementToken ( token string ) bool
2021-07-30 23:20:02 +00:00
// TODO: separate methods for each RPC call (there are 4)
2022-12-14 15:24:22 +00:00
RPC ( ctx context . Context , method string , args interface { } , reply interface { } ) error
2019-10-25 15:06:16 +00:00
EnterpriseACLResolverDelegate
2018-10-19 16:04:07 +00:00
}
2014-08-08 22:32:43 +00:00
2019-04-15 20:43:19 +00:00
type policyOrRoleTokenError struct {
2019-01-22 18:14:43 +00:00
Err error
token string
}
2019-04-15 20:43:19 +00:00
func ( e policyOrRoleTokenError ) Error ( ) string {
2019-01-22 18:14:43 +00:00
return e . Err . Error ( )
}
2018-10-19 16:04:07 +00:00
// ACLResolverConfig holds all the configuration necessary to create an ACLResolver
type ACLResolverConfig struct {
2021-07-30 22:56:11 +00:00
// TODO: rename this field?
Config ACLResolverSettings
2020-01-28 23:50:41 +00:00
Logger hclog . Logger
2018-10-19 16:04:07 +00:00
// CacheConfig is a pass through configuration for ACL cache limits
CacheConfig * structs . ACLCachesConfig
2021-07-30 23:20:02 +00:00
// Backend is used to retrieve data from the state store, or perform RPCs
// to fetch data from other Datacenters.
Backend ACLResolverBackend
2015-06-18 23:18:10 +00:00
2021-08-09 20:29:21 +00:00
// 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
// 0 then ACLs will not be disabled locally. This value is always set to 0 on
// Servers.
DisableDuration time . Duration
2018-10-19 16:04:07 +00:00
2020-01-13 20:51:40 +00:00
// ACLConfig is the configuration necessary to pass through to the acl package when creating authorizers
// and when authorizing access
ACLConfig * acl . Config
2021-04-14 16:39:35 +00:00
// Tokens is the token store of locally managed tokens
Tokens * token . Store
2018-10-19 16:04:07 +00:00
}
2021-08-09 20:29:21 +00:00
const aclClientDisabledTTL = 30 * time . Second
2021-08-09 20:04:23 +00:00
2021-07-30 22:56:11 +00:00
// TODO: rename the fields to remove the ACL prefix
type ACLResolverSettings struct {
2021-08-25 18:43:11 +00:00
ACLsEnabled bool
Datacenter string
NodeName string
2022-04-05 21:10:06 +00:00
EnterpriseMeta acl . EnterpriseMeta
2021-08-06 22:59:05 +00:00
// ACLPolicyTTL is used to control the time-to-live of cached ACL policies. This has
// a major impact on performance. By default, it is set to 30 seconds.
ACLPolicyTTL time . Duration
// ACLTokenTTL is used to control the time-to-live of cached ACL tokens. This has
// a major impact on performance. By default, it is set to 30 seconds.
ACLTokenTTL time . Duration
// ACLRoleTTL is used to control the time-to-live of cached ACL roles. This has
// a major impact on performance. By default, it is set to 30 seconds.
ACLRoleTTL time . Duration
// ACLDownPolicy is used to control the ACL interaction when we cannot
// reach the PrimaryDatacenter and the token is not in the cache.
// There are the following modes:
// * allow - Allow all requests
// * deny - Deny all requests
// * extend-cache - Ignore the cache expiration, and allow cached
// ACL's to be used to service requests. This
// is the default. If the ACL is not in the cache,
// this acts like deny.
// * async-cache - Same behavior as extend-cache, but perform ACL
// Lookups asynchronously when cache TTL is expired.
ACLDownPolicy string
// ACLDefaultPolicy is used to control the ACL interaction when
// there is no defined policy. This can be "allow" which means
// ACLs are used to deny-list, or "deny" which means ACLs are
// allow-lists.
2021-07-30 22:56:11 +00:00
ACLDefaultPolicy string
}
2018-10-19 16:04:07 +00:00
// ACLResolver is the type to handle all your token and policy resolution needs.
//
// Supports:
2021-07-30 23:20:02 +00:00
// - Resolving tokens locally via the ACLResolverBackend
// - Resolving policies locally via the ACLResolverBackend
// - Resolving roles locally via the ACLResolverBackend
2020-01-27 19:54:32 +00:00
// - Resolving legacy tokens remotely via an ACL.GetPolicy RPC
2018-10-19 16:04:07 +00:00
// - Resolving tokens remotely via an ACL.TokenRead RPC
// - Resolving policies remotely via an ACL.PolicyResolve RPC
2019-04-15 20:43:19 +00:00
// - Resolving roles remotely via an ACL.RoleResolve RPC
2018-10-19 16:04:07 +00:00
//
// Remote Resolution:
//
2022-10-21 19:58:06 +00:00
// Remote resolution can be done synchronously or asynchronously depending
// on the ACLDownPolicy in the Config passed to the resolver.
2018-10-19 16:04:07 +00:00
//
2022-10-21 19:58:06 +00:00
// When the down policy is set to async-cache and we have already cached values
// then go routines will be spawned to perform the RPCs in the background
// and then will update the cache with either the positive or negative result.
2018-10-19 16:04:07 +00:00
//
2022-10-21 19:58:06 +00:00
// When the down policy is set to extend-cache or the token/policy/role is not already
// cached then the same go routines are spawned to do the RPCs in the background.
// However in this mode channels are created to receive the results of the RPC
// and are registered with the resolver. Those channels are immediately read/blocked
// upon.
2018-10-19 16:04:07 +00:00
type ACLResolver struct {
2021-07-30 22:56:11 +00:00
config ACLResolverSettings
2020-01-28 23:50:41 +00:00
logger hclog . Logger
2015-06-18 23:18:10 +00:00
2021-07-30 23:20:02 +00:00
backend ACLResolverBackend
aclConf * acl . Config
2017-09-14 19:31:01 +00:00
2021-04-14 16:39:35 +00:00
tokens * token . Store
2019-03-14 14:35:34 +00:00
cache * structs . ACLCaches
identityGroup singleflight . Group
policyGroup singleflight . Group
2019-04-15 20:43:19 +00:00
roleGroup singleflight . Group
2019-03-14 14:35:34 +00:00
legacyGroup singleflight . Group
2016-08-04 00:01:32 +00:00
2018-10-19 16:04:07 +00:00
down acl . Authorizer
2018-06-28 07:06:14 +00:00
2021-08-09 20:29:21 +00:00
disableDuration time . Duration
disabledUntil time . Time
// disabledLock synchronizes access to disabledUntil
2018-10-19 16:04:07 +00:00
disabledLock sync . RWMutex
2021-04-14 16:39:35 +00:00
2022-01-20 12:47:50 +00:00
agentRecoveryAuthz acl . Authorizer
2021-04-14 16:39:35 +00:00
}
2022-04-05 21:10:06 +00:00
func agentRecoveryAuthorizer ( nodeName string , entMeta * acl . EnterpriseMeta , aclConf * acl . Config ) ( acl . Authorizer , error ) {
2021-12-08 23:11:55 +00:00
var conf acl . Config
if aclConf != nil {
conf = * aclConf
}
setEnterpriseConf ( entMeta , & conf )
2021-08-25 18:43:11 +00:00
2022-01-20 12:47:50 +00:00
// Build a policy for the agent recovery token.
2021-12-08 23:11:55 +00:00
//
2022-01-20 12:47:50 +00:00
// The builtin agent recovery policy allows reading any node information
2021-04-14 16:39:35 +00:00
// and allows writes to the agent with the node name of the running agent
// only. This used to allow a prefix match on agent names but that seems
// entirely unnecessary so it is now using an exact match.
2021-12-08 23:11:55 +00:00
policy , err := acl . NewPolicyFromSource ( fmt . Sprintf ( `
agent "%s" {
policy = "write"
}
node_prefix "" {
policy = "read"
}
2023-02-06 15:35:52 +00:00
` , nodeName ) , & conf , entMeta . ToEnterprisePolicyMeta ( ) )
2021-12-08 23:11:55 +00:00
if err != nil {
return nil , err
2021-04-14 16:39:35 +00:00
}
2021-10-26 18:02:34 +00:00
2021-12-08 23:11:55 +00:00
return acl . NewPolicyAuthorizerWithDefaults ( acl . DenyAll ( ) , [ ] * acl . Policy { policy } , & conf )
2015-06-18 23:18:10 +00:00
}
2018-10-19 16:04:07 +00:00
func NewACLResolver ( config * ACLResolverConfig ) ( * ACLResolver , error ) {
if config == nil {
return nil , fmt . Errorf ( "ACL Resolver must be initialized with a config" )
2015-06-18 23:18:10 +00:00
}
2021-07-30 23:20:02 +00:00
if config . Backend == nil {
2021-12-01 18:40:41 +00:00
return nil , fmt . Errorf ( "ACL Resolver must be initialized with a valid backend" )
2015-06-18 23:18:10 +00:00
}
2018-10-19 16:04:07 +00:00
if config . Logger == nil {
2020-01-28 23:50:41 +00:00
config . Logger = hclog . New ( & hclog . LoggerOptions { } )
2018-10-19 16:04:07 +00:00
}
2018-06-28 07:06:14 +00:00
2018-10-19 16:04:07 +00:00
cache , err := structs . NewACLCaches ( config . CacheConfig )
if err != nil {
return nil , err
2014-08-08 22:32:43 +00:00
}
2018-10-19 16:04:07 +00:00
var down acl . Authorizer
switch config . Config . ACLDownPolicy {
case "allow" :
down = acl . AllowAll ( )
case "deny" :
down = acl . DenyAll ( )
case "async-cache" , "extend-cache" :
2021-09-23 22:11:16 +00:00
down = acl . RootAuthorizer ( config . Config . ACLDefaultPolicy )
2018-10-19 16:04:07 +00:00
default :
return nil , fmt . Errorf ( "invalid ACL down policy %q" , config . Config . ACLDownPolicy )
2014-08-08 22:32:43 +00:00
}
2018-10-19 16:04:07 +00:00
2022-01-20 12:47:50 +00:00
authz , err := agentRecoveryAuthorizer ( config . Config . NodeName , & config . Config . EnterpriseMeta , config . ACLConfig )
2021-04-14 16:39:35 +00:00
if err != nil {
2022-01-20 12:47:50 +00:00
return nil , fmt . Errorf ( "failed to initialize the agent recovery authorizer" )
2021-04-14 16:39:35 +00:00
}
2018-10-19 16:04:07 +00:00
return & ACLResolver {
2022-01-20 12:47:50 +00:00
config : config . Config ,
logger : config . Logger . Named ( logging . ACL ) ,
2021-07-30 23:20:02 +00:00
backend : config . Backend ,
2022-01-20 12:47:50 +00:00
aclConf : config . ACLConfig ,
cache : cache ,
disableDuration : config . DisableDuration ,
down : down ,
tokens : config . Tokens ,
agentRecoveryAuthz : authz ,
2018-10-19 16:04:07 +00:00
} , nil
2018-06-28 07:06:14 +00:00
}
2014-08-08 22:32:43 +00:00
2019-12-06 19:01:34 +00:00
func ( r * ACLResolver ) Close ( ) {
2020-01-13 20:51:40 +00:00
r . aclConf . Close ( )
2019-12-06 19:01:34 +00:00
}
2019-03-14 14:35:34 +00:00
func ( r * ACLResolver ) fetchAndCacheIdentityFromToken ( token string , cached * structs . IdentityCacheEntry ) ( structs . ACLIdentity , error ) {
2018-10-31 20:00:46 +00:00
req := structs . ACLTokenGetRequest {
2021-07-30 23:20:02 +00:00
Datacenter : r . backend . ACLDatacenter ( ) ,
2018-10-19 16:04:07 +00:00
TokenID : token ,
TokenIDType : structs . ACLTokenSecret ,
QueryOptions : structs . QueryOptions {
Token : token ,
AllowStale : true ,
} ,
}
var resp structs . ACLTokenResponse
2022-12-14 15:24:22 +00:00
err := r . backend . RPC ( context . Background ( ) , "ACL.TokenRead" , & req , & resp )
2018-10-19 16:04:07 +00:00
if err == nil {
if resp . Token == nil {
2022-05-04 16:38:45 +00:00
r . cache . RemoveIdentityWithSecretToken ( token )
2019-03-14 14:35:34 +00:00
return nil , acl . ErrNotFound
2020-06-09 19:13:09 +00:00
} else if resp . Token . Local && r . config . Datacenter != resp . SourceDatacenter {
2022-05-04 16:38:45 +00:00
r . cache . RemoveIdentityWithSecretToken ( token )
2020-06-09 19:13:09 +00:00
return nil , acl . PermissionDeniedError { Cause : fmt . Sprintf ( "This is a local token in datacenter %q" , resp . SourceDatacenter ) }
2018-10-19 16:04:07 +00:00
} else {
2022-05-04 16:38:45 +00:00
r . cache . PutIdentityWithSecretToken ( token , resp . Token )
2019-03-14 14:35:34 +00:00
return resp . Token , nil
2018-10-19 16:04:07 +00:00
}
}
if acl . IsErrNotFound ( err ) {
// Make sure to remove from the cache if it was deleted
2022-05-04 16:38:45 +00:00
r . cache . RemoveIdentityWithSecretToken ( token )
2019-03-14 14:35:34 +00:00
return nil , acl . ErrNotFound
2018-06-28 07:06:14 +00:00
}
2018-10-19 16:04:07 +00:00
// some other RPC error
if cached != nil && ( r . config . ACLDownPolicy == "extend-cache" || r . config . ACLDownPolicy == "async-cache" ) {
// extend the cache
2022-05-04 16:38:45 +00:00
r . cache . PutIdentityWithSecretToken ( token , cached . Identity )
2019-03-14 14:35:34 +00:00
return cached . Identity , nil
2018-10-19 16:04:07 +00:00
}
2022-05-04 16:38:45 +00:00
r . cache . RemoveIdentityWithSecretToken ( token )
2019-03-14 14:35:34 +00:00
return nil , err
2018-06-28 07:06:14 +00:00
}
2020-01-27 19:54:32 +00:00
// resolveIdentityFromToken takes a token secret as a string and returns an ACLIdentity.
// We read the value from ACLResolver's cache if available, and if the read misses
// we initiate an RPC for the value.
2018-10-19 16:04:07 +00:00
func ( r * ACLResolver ) resolveIdentityFromToken ( token string ) ( structs . ACLIdentity , error ) {
// Attempt to resolve locally first (local results are not cached)
2021-07-30 23:20:02 +00:00
if done , identity , err := r . backend . ResolveIdentityFromToken ( token ) ; done {
2018-10-19 16:04:07 +00:00
return identity , err
}
// Check the cache before making any RPC requests
2022-05-04 16:38:45 +00:00
cacheEntry := r . cache . GetIdentityWithSecretToken ( token )
2018-10-19 16:04:07 +00:00
if cacheEntry != nil && cacheEntry . Age ( ) <= r . config . ACLTokenTTL {
metrics . IncrCounter ( [ ] string { "acl" , "token" , "cache_hit" } , 1 )
2022-05-04 16:38:45 +00:00
return cacheEntry . Identity , nil
2018-06-28 07:06:14 +00:00
}
2018-10-19 16:04:07 +00:00
metrics . IncrCounter ( [ ] string { "acl" , "token" , "cache_miss" } , 1 )
// Background a RPC request and wait on it if we must
2019-03-14 14:35:34 +00:00
waitChan := r . identityGroup . DoChan ( token , func ( ) ( interface { } , error ) {
identity , err := r . fetchAndCacheIdentityFromToken ( token , cacheEntry )
return identity , err
} )
2018-10-19 16:04:07 +00:00
waitForResult := cacheEntry == nil || r . config . ACLDownPolicy != "async-cache"
if ! waitForResult {
// waitForResult being false requires the cacheEntry to not be nil
2022-05-04 16:38:45 +00:00
return cacheEntry . Identity , nil
2018-10-19 16:04:07 +00:00
}
// block on the read here, this is why we don't need chan buffering
res := <- waitChan
2019-03-14 14:35:34 +00:00
var identity structs . ACLIdentity
if res . Val != nil { // avoid a nil-not-nil bug
identity = res . Val . ( structs . ACLIdentity )
2018-06-28 07:06:14 +00:00
}
2018-10-19 16:04:07 +00:00
2019-03-14 14:35:34 +00:00
if res . Err != nil && ! acl . IsErrNotFound ( res . Err ) {
return identity , ACLRemoteError { Err : res . Err }
2014-08-08 22:32:43 +00:00
}
2019-03-14 14:35:34 +00:00
return identity , res . Err
2014-08-08 22:32:43 +00:00
}
2014-08-09 00:38:39 +00:00
2019-03-14 14:35:34 +00:00
func ( r * ACLResolver ) fetchAndCachePoliciesForIdentity ( identity structs . ACLIdentity , policyIDs [ ] string , cached map [ string ] * structs . PolicyCacheEntry ) ( map [ string ] * structs . ACLPolicy , error ) {
2018-10-31 20:00:46 +00:00
req := structs . ACLPolicyBatchGetRequest {
2021-07-30 23:20:02 +00:00
Datacenter : r . backend . ACLDatacenter ( ) ,
2018-10-19 16:04:07 +00:00
PolicyIDs : policyIDs ,
QueryOptions : structs . QueryOptions {
Token : identity . SecretToken ( ) ,
AllowStale : true ,
} ,
}
2018-10-31 20:00:46 +00:00
var resp structs . ACLPolicyBatchResponse
2022-12-14 15:24:22 +00:00
err := r . backend . RPC ( context . Background ( ) , "ACL.PolicyResolve" , & req , & resp )
2018-10-19 16:04:07 +00:00
if err == nil {
2019-03-14 14:35:34 +00:00
out := make ( map [ string ] * structs . ACLPolicy )
2018-10-19 16:04:07 +00:00
for _ , policy := range resp . Policies {
2019-03-14 14:35:34 +00:00
out [ policy . ID ] = policy
2018-10-19 16:04:07 +00:00
}
for _ , policyID := range policyIDs {
2019-03-14 14:35:34 +00:00
if policy , ok := out [ policyID ] ; ok {
r . cache . PutPolicy ( policyID , policy )
} else {
r . cache . PutPolicy ( policyID , nil )
2018-10-19 16:04:07 +00:00
}
2014-08-09 00:38:39 +00:00
}
2019-03-14 14:35:34 +00:00
return out , nil
2014-08-09 00:38:39 +00:00
}
2019-04-26 17:49:28 +00:00
if handledErr := r . maybeHandleIdentityErrorDuringFetch ( identity , err ) ; handledErr != nil {
return nil , handledErr
2018-10-19 16:04:07 +00:00
}
// other RPC error - use cache if available
extendCache := r . config . ACLDownPolicy == "extend-cache" || r . config . ACLDownPolicy == "async-cache"
2019-03-14 14:35:34 +00:00
out := make ( map [ string ] * structs . ACLPolicy )
insufficientCache := false
2018-10-19 16:04:07 +00:00
for _ , policyID := range policyIDs {
if entry , ok := cached [ policyID ] ; extendCache && ok {
2019-03-14 14:35:34 +00:00
r . cache . PutPolicy ( policyID , entry . Policy )
if entry . Policy != nil {
out [ policyID ] = entry . Policy
}
2018-10-19 16:04:07 +00:00
} else {
2019-03-14 14:35:34 +00:00
r . cache . PutPolicy ( policyID , nil )
insufficientCache = true
2018-10-19 16:04:07 +00:00
}
}
2019-03-14 14:35:34 +00:00
if insufficientCache {
return nil , ACLRemoteError { Err : err }
}
return out , nil
2018-10-19 16:04:07 +00:00
}
2019-04-15 20:43:19 +00:00
func ( r * ACLResolver ) fetchAndCacheRolesForIdentity ( identity structs . ACLIdentity , roleIDs [ ] string , cached map [ string ] * structs . RoleCacheEntry ) ( map [ string ] * structs . ACLRole , error ) {
req := structs . ACLRoleBatchGetRequest {
2021-07-30 23:20:02 +00:00
Datacenter : r . backend . ACLDatacenter ( ) ,
2019-04-15 20:43:19 +00:00
RoleIDs : roleIDs ,
QueryOptions : structs . QueryOptions {
Token : identity . SecretToken ( ) ,
AllowStale : true ,
} ,
}
var resp structs . ACLRoleBatchResponse
2022-12-14 15:24:22 +00:00
err := r . backend . RPC ( context . Background ( ) , "ACL.RoleResolve" , & req , & resp )
2019-04-15 20:43:19 +00:00
if err == nil {
out := make ( map [ string ] * structs . ACLRole )
for _ , role := range resp . Roles {
out [ role . ID ] = role
}
for _ , roleID := range roleIDs {
if role , ok := out [ roleID ] ; ok {
r . cache . PutRole ( roleID , role )
} else {
r . cache . PutRole ( roleID , nil )
}
}
return out , nil
}
2019-04-26 17:49:28 +00:00
if handledErr := r . maybeHandleIdentityErrorDuringFetch ( identity , err ) ; handledErr != nil {
return nil , handledErr
2019-04-15 20:43:19 +00:00
}
// other RPC error - use cache if available
extendCache := r . config . ACLDownPolicy == "extend-cache" || r . config . ACLDownPolicy == "async-cache"
out := make ( map [ string ] * structs . ACLRole )
insufficientCache := false
for _ , roleID := range roleIDs {
if entry , ok := cached [ roleID ] ; extendCache && ok {
r . cache . PutRole ( roleID , entry . Role )
if entry . Role != nil {
out [ roleID ] = entry . Role
}
} else {
r . cache . PutRole ( roleID , nil )
insufficientCache = true
}
}
2019-04-26 17:49:28 +00:00
2019-04-15 20:43:19 +00:00
if insufficientCache {
return nil , ACLRemoteError { Err : err }
}
2019-04-26 17:49:28 +00:00
2019-04-15 20:43:19 +00:00
return out , nil
}
2019-04-26 17:49:28 +00:00
func ( r * ACLResolver ) maybeHandleIdentityErrorDuringFetch ( identity structs . ACLIdentity , err error ) error {
if acl . IsErrNotFound ( err ) {
// make sure to indicate that this identity is no longer valid within
// the cache
2022-05-04 16:38:45 +00:00
r . cache . RemoveIdentityWithSecretToken ( identity . SecretToken ( ) )
2019-04-26 17:49:28 +00:00
// Do not touch the cache. Getting a top level ACL not found error
// only indicates that the secret token used in the request
// no longer exists
return & policyOrRoleTokenError { acl . ErrNotFound , identity . SecretToken ( ) }
}
if acl . IsErrPermissionDenied ( err ) {
// invalidate our ID cache so that identity resolution will take place
// again in the future
2022-05-04 16:38:45 +00:00
r . cache . RemoveIdentityWithSecretToken ( identity . SecretToken ( ) )
2019-04-26 17:49:28 +00:00
// Do not remove from the cache for permission denied
// what this does indicate is that our view of the token is out of date
return & policyOrRoleTokenError { acl . ErrPermissionDenied , identity . SecretToken ( ) }
}
return nil
}
2018-10-19 16:04:07 +00:00
func ( r * ACLResolver ) filterPoliciesByScope ( policies structs . ACLPolicies ) structs . ACLPolicies {
var out structs . ACLPolicies
for _ , policy := range policies {
if len ( policy . Datacenters ) == 0 {
out = append ( out , policy )
continue
}
for _ , dc := range policy . Datacenters {
if dc == r . config . Datacenter {
out = append ( out , policy )
continue
}
}
}
return out
}
func ( r * ACLResolver ) resolvePoliciesForIdentity ( identity structs . ACLIdentity ) ( structs . ACLPolicies , error ) {
2021-09-16 14:17:02 +00:00
var (
policyIDs = identity . PolicyIDs ( )
roleIDs = identity . RoleIDs ( )
2022-05-04 16:38:45 +00:00
serviceIdentities = structs . ACLServiceIdentities ( identity . ServiceIdentityList ( ) )
nodeIdentities = structs . ACLNodeIdentities ( identity . NodeIdentityList ( ) )
2021-09-16 14:17:02 +00:00
)
2019-04-08 18:19:09 +00:00
2020-06-16 16:54:27 +00:00
if len ( policyIDs ) == 0 && len ( serviceIdentities ) == 0 && len ( roleIDs ) == 0 && len ( nodeIdentities ) == 0 {
2018-10-19 16:04:07 +00:00
// In this case the default policy will be all that is in effect.
return nil , nil
}
2019-04-15 20:43:19 +00:00
// Collect all of the roles tied to this token.
roles , err := r . collectRolesForIdentity ( identity , roleIDs )
if err != nil {
return nil , err
}
// Merge the policies and service identities across Token and Role fields.
for _ , role := range roles {
for _ , link := range role . Policies {
policyIDs = append ( policyIDs , link . ID )
}
serviceIdentities = append ( serviceIdentities , role . ServiceIdentities ... )
2020-06-16 16:54:27 +00:00
nodeIdentities = append ( nodeIdentities , role . NodeIdentityList ( ) ... )
2019-04-15 20:43:19 +00:00
}
// Now deduplicate any policies or service identities that occur more than once.
policyIDs = dedupeStringSlice ( policyIDs )
2022-05-04 16:38:45 +00:00
serviceIdentities = serviceIdentities . Deduplicate ( )
nodeIdentities = nodeIdentities . Deduplicate ( )
2019-04-15 20:43:19 +00:00
// Generate synthetic policies for all service identities in effect.
2019-10-24 18:38:09 +00:00
syntheticPolicies := r . synthesizePoliciesForServiceIdentities ( serviceIdentities , identity . EnterpriseMetadata ( ) )
2021-09-16 14:17:02 +00:00
syntheticPolicies = append ( syntheticPolicies , r . synthesizePoliciesForNodeIdentities ( nodeIdentities , identity . EnterpriseMetadata ( ) ) ... )
2019-04-08 18:19:09 +00:00
2018-10-19 16:04:07 +00:00
// For the new ACLs policy replication is mandatory for correct operation on servers. Therefore
// we only attempt to resolve policies locally
2019-04-08 18:19:09 +00:00
policies , err := r . collectPoliciesForIdentity ( identity , policyIDs , len ( syntheticPolicies ) )
if err != nil {
return nil , err
}
policies = append ( policies , syntheticPolicies ... )
filtered := r . filterPoliciesByScope ( policies )
2022-12-05 16:26:10 +00:00
if len ( policies ) > 0 && len ( filtered ) == 0 {
r . logger . Warn ( "ACL token used lacks permissions in this datacenter: its associated ACL policies, service identities, and/or node identities are scoped to other datacenters" , "accessor_id" , identity . ID ( ) , "datacenter" , r . config . Datacenter )
}
2019-04-08 18:19:09 +00:00
return filtered , nil
}
2022-04-05 21:10:06 +00:00
func ( r * ACLResolver ) synthesizePoliciesForServiceIdentities ( serviceIdentities [ ] * structs . ACLServiceIdentity , entMeta * acl . EnterpriseMeta ) [ ] * structs . ACLPolicy {
2019-04-08 18:19:09 +00:00
if len ( serviceIdentities ) == 0 {
return nil
}
syntheticPolicies := make ( [ ] * structs . ACLPolicy , 0 , len ( serviceIdentities ) )
for _ , s := range serviceIdentities {
2019-10-24 18:38:09 +00:00
syntheticPolicies = append ( syntheticPolicies , s . SyntheticPolicy ( entMeta ) )
2019-04-08 18:19:09 +00:00
}
return syntheticPolicies
}
2022-04-05 21:10:06 +00:00
func ( r * ACLResolver ) synthesizePoliciesForNodeIdentities ( nodeIdentities [ ] * structs . ACLNodeIdentity , entMeta * acl . EnterpriseMeta ) [ ] * structs . ACLPolicy {
2020-06-16 16:54:27 +00:00
if len ( nodeIdentities ) == 0 {
return nil
}
syntheticPolicies := make ( [ ] * structs . ACLPolicy , 0 , len ( nodeIdentities ) )
for _ , n := range nodeIdentities {
2021-09-16 14:17:02 +00:00
syntheticPolicies = append ( syntheticPolicies , n . SyntheticPolicy ( entMeta ) )
2020-06-16 16:54:27 +00:00
}
return syntheticPolicies
}
2019-04-08 18:19:09 +00:00
func mergeStringSlice ( a , b [ ] string ) [ ] string {
out := make ( [ ] string , 0 , len ( a ) + len ( b ) )
out = append ( out , a ... )
out = append ( out , b ... )
return dedupeStringSlice ( out )
}
func dedupeStringSlice ( in [ ] string ) [ ] string {
// From: https://github.com/golang/go/wiki/SliceTricks#in-place-deduplicate-comparable
2019-04-15 20:43:19 +00:00
if len ( in ) <= 1 {
return in
}
2019-04-08 18:19:09 +00:00
sort . Strings ( in )
j := 0
for i := 1 ; i < len ( in ) ; i ++ {
if in [ j ] == in [ i ] {
continue
}
j ++
in [ j ] = in [ i ]
}
return in [ : j + 1 ]
}
func ( r * ACLResolver ) collectPoliciesForIdentity ( identity structs . ACLIdentity , policyIDs [ ] string , extraCap int ) ( [ ] * structs . ACLPolicy , error ) {
policies := make ( [ ] * structs . ACLPolicy , 0 , len ( policyIDs ) + extraCap )
2018-10-19 16:04:07 +00:00
// Get all associated policies
var missing [ ] string
var expired [ ] * structs . ACLPolicy
expCacheMap := make ( map [ string ] * structs . PolicyCacheEntry )
2020-01-27 19:54:32 +00:00
var accessorID string
if identity != nil {
accessorID = identity . ID ( )
}
2018-10-19 16:04:07 +00:00
for _ , policyID := range policyIDs {
2021-07-30 23:20:02 +00:00
if done , policy , err := r . backend . ResolvePolicyFromID ( policyID ) ; done {
2018-10-19 16:04:07 +00:00
if err != nil && ! acl . IsErrNotFound ( err ) {
2014-08-12 17:54:56 +00:00
return nil , err
}
2018-10-19 16:04:07 +00:00
if policy != nil {
policies = append ( policies , policy )
} else {
2020-01-28 23:50:41 +00:00
r . logger . Warn ( "policy not found for identity" ,
"policy" , policyID ,
2023-01-09 18:28:53 +00:00
"accessorID" , acl . AliasIfAnonymousToken ( accessorID ) ,
2020-01-29 17:16:08 +00:00
)
2018-10-19 16:04:07 +00:00
}
continue
}
// create the missing list which we can execute an RPC to get all the missing policies at once
entry := r . cache . GetPolicy ( policyID )
if entry == nil {
missing = append ( missing , policyID )
continue
}
if entry . Policy == nil {
2019-04-15 20:43:19 +00:00
// this happens when we cache a negative response for the policy's existence
2018-10-19 16:04:07 +00:00
continue
}
if entry . Age ( ) >= r . config . ACLPolicyTTL {
expired = append ( expired , entry . Policy )
expCacheMap [ policyID ] = entry
} else {
policies = append ( policies , entry . Policy )
}
}
// Hot-path if we have no missing or expired policies
if len ( missing ) + len ( expired ) == 0 {
2019-04-08 18:19:09 +00:00
return policies , nil
2018-10-19 16:04:07 +00:00
}
2019-03-14 14:35:34 +00:00
hasMissing := len ( missing ) > 0
2018-10-19 16:04:07 +00:00
fetchIDs := missing
for _ , policy := range expired {
fetchIDs = append ( fetchIDs , policy . ID )
}
// Background a RPC request and wait on it if we must
2019-03-14 14:35:34 +00:00
waitChan := r . policyGroup . DoChan ( identity . SecretToken ( ) , func ( ) ( interface { } , error ) {
policies , err := r . fetchAndCachePoliciesForIdentity ( identity , fetchIDs , expCacheMap )
return policies , err
} )
2014-08-09 00:38:39 +00:00
2019-03-14 14:35:34 +00:00
waitForResult := hasMissing || r . config . ACLDownPolicy != "async-cache"
2018-10-19 16:04:07 +00:00
if ! waitForResult {
// waitForResult being false requires that all the policies were cached already
policies = append ( policies , expired ... )
2019-04-08 18:19:09 +00:00
return policies , nil
2014-08-09 00:38:39 +00:00
}
2019-03-14 14:35:34 +00:00
res := <- waitChan
2018-10-19 16:04:07 +00:00
2019-03-14 14:35:34 +00:00
if res . Err != nil {
return nil , res . Err
}
2018-10-19 16:04:07 +00:00
2019-03-14 14:35:34 +00:00
if res . Val != nil {
foundPolicies := res . Val . ( map [ string ] * structs . ACLPolicy )
2019-01-22 18:14:43 +00:00
2019-03-14 14:35:34 +00:00
for _ , policy := range foundPolicies {
policies = append ( policies , policy )
2018-10-19 16:04:07 +00:00
}
2014-08-09 00:38:39 +00:00
}
2018-10-19 16:04:07 +00:00
2019-04-08 18:19:09 +00:00
return policies , nil
2018-10-19 16:04:07 +00:00
}
2019-04-15 20:43:19 +00:00
func ( r * ACLResolver ) resolveRolesForIdentity ( identity structs . ACLIdentity ) ( structs . ACLRoles , error ) {
return r . collectRolesForIdentity ( identity , identity . RoleIDs ( ) )
}
func ( r * ACLResolver ) collectRolesForIdentity ( identity structs . ACLIdentity , roleIDs [ ] string ) ( structs . ACLRoles , error ) {
if len ( roleIDs ) == 0 {
return nil , nil
}
// For the new ACLs policy & role replication is mandatory for correct operation
// on servers. Therefore we only attempt to resolve roles locally
roles := make ( [ ] * structs . ACLRole , 0 , len ( roleIDs ) )
var missing [ ] string
var expired [ ] * structs . ACLRole
expCacheMap := make ( map [ string ] * structs . RoleCacheEntry )
for _ , roleID := range roleIDs {
2021-07-30 23:20:02 +00:00
if done , role , err := r . backend . ResolveRoleFromID ( roleID ) ; done {
2019-04-15 20:43:19 +00:00
if err != nil && ! acl . IsErrNotFound ( err ) {
return nil , err
}
if role != nil {
roles = append ( roles , role )
} else {
2020-01-27 19:54:32 +00:00
var accessorID string
if identity != nil {
accessorID = identity . ID ( )
}
2020-01-28 23:50:41 +00:00
r . logger . Warn ( "role not found for identity" ,
"role" , roleID ,
2023-01-09 18:28:53 +00:00
"accessorID" , acl . AliasIfAnonymousToken ( accessorID ) ,
2020-01-28 23:50:41 +00:00
)
2019-04-15 20:43:19 +00:00
}
continue
}
// create the missing list which we can execute an RPC to get all the missing roles at once
entry := r . cache . GetRole ( roleID )
if entry == nil {
missing = append ( missing , roleID )
continue
}
if entry . Role == nil {
// this happens when we cache a negative response for the role's existence
continue
}
if entry . Age ( ) >= r . config . ACLRoleTTL {
expired = append ( expired , entry . Role )
expCacheMap [ roleID ] = entry
} else {
roles = append ( roles , entry . Role )
}
}
// Hot-path if we have no missing or expired roles
if len ( missing ) + len ( expired ) == 0 {
return roles , nil
}
hasMissing := len ( missing ) > 0
fetchIDs := missing
for _ , role := range expired {
fetchIDs = append ( fetchIDs , role . ID )
}
waitChan := r . roleGroup . DoChan ( identity . SecretToken ( ) , func ( ) ( interface { } , error ) {
roles , err := r . fetchAndCacheRolesForIdentity ( identity , fetchIDs , expCacheMap )
return roles , err
} )
waitForResult := hasMissing || r . config . ACLDownPolicy != "async-cache"
if ! waitForResult {
// waitForResult being false requires that all the roles were cached already
roles = append ( roles , expired ... )
return roles , nil
}
res := <- waitChan
if res . Err != nil {
return nil , res . Err
}
if res . Val != nil {
foundRoles := res . Val . ( map [ string ] * structs . ACLRole )
for _ , role := range foundRoles {
roles = append ( roles , role )
}
}
return roles , nil
}
2019-01-22 18:14:43 +00:00
func ( r * ACLResolver ) resolveTokenToIdentityAndPolicies ( token string ) ( structs . ACLIdentity , structs . ACLPolicies , error ) {
var lastErr error
var lastIdentity structs . ACLIdentity
for i := 0 ; i < tokenPolicyResolutionMaxRetries ; i ++ {
// Resolve the token to an ACLIdentity
identity , err := r . resolveIdentityFromToken ( token )
if err != nil {
return nil , nil , err
} else if identity == nil {
return nil , nil , acl . ErrNotFound
2019-04-08 17:05:51 +00:00
} else if identity . IsExpired ( time . Now ( ) ) {
return nil , nil , acl . ErrNotFound
2019-01-22 18:14:43 +00:00
}
lastIdentity = identity
policies , err := r . resolvePoliciesForIdentity ( identity )
if err == nil {
return identity , policies , nil
}
lastErr = err
2019-04-15 20:43:19 +00:00
if tokenErr , ok := err . ( * policyOrRoleTokenError ) ; ok {
2019-01-22 18:14:43 +00:00
if acl . IsErrNotFound ( err ) && tokenErr . token == identity . SecretToken ( ) {
// token was deleted while resolving policies
return nil , nil , acl . ErrNotFound
}
2019-04-15 20:43:19 +00:00
// other types of policyOrRoleTokenErrors should cause retrying the whole token
// resolution process
} else {
return identity , nil , err
}
}
return lastIdentity , nil , lastErr
}
func ( r * ACLResolver ) resolveTokenToIdentityAndRoles ( token string ) ( structs . ACLIdentity , structs . ACLRoles , error ) {
var lastErr error
var lastIdentity structs . ACLIdentity
for i := 0 ; i < tokenRoleResolutionMaxRetries ; i ++ {
// Resolve the token to an ACLIdentity
identity , err := r . resolveIdentityFromToken ( token )
if err != nil {
return nil , nil , err
} else if identity == nil {
return nil , nil , acl . ErrNotFound
} else if identity . IsExpired ( time . Now ( ) ) {
return nil , nil , acl . ErrNotFound
}
lastIdentity = identity
roles , err := r . resolveRolesForIdentity ( identity )
if err == nil {
return identity , roles , nil
}
lastErr = err
if tokenErr , ok := err . ( * policyOrRoleTokenError ) ; ok {
if acl . IsErrNotFound ( err ) && tokenErr . token == identity . SecretToken ( ) {
// token was deleted while resolving roles
return nil , nil , acl . ErrNotFound
}
// other types of policyOrRoleTokenErrors should cause retrying the whole token
2019-01-22 18:14:43 +00:00
// resolution process
} else {
return identity , nil , err
}
2014-08-09 00:38:39 +00:00
}
2018-10-19 16:04:07 +00:00
2019-01-22 18:14:43 +00:00
return lastIdentity , nil , lastErr
2018-10-19 16:04:07 +00:00
}
2021-08-09 20:29:21 +00:00
func ( r * ACLResolver ) handleACLDisabledError ( err error ) {
if r . disableDuration == 0 || err == nil || ! acl . IsErrDisabled ( err ) {
return
2018-10-19 16:04:07 +00:00
}
2021-08-09 20:29:21 +00:00
r . logger . Debug ( "ACLs disabled on servers, will retry" , "retry_interval" , r . disableDuration )
2018-10-19 16:04:07 +00:00
r . disabledLock . Lock ( )
2021-08-09 20:29:21 +00:00
r . disabledUntil = time . Now ( ) . Add ( r . disableDuration )
2018-10-19 16:04:07 +00:00
r . disabledLock . Unlock ( )
}
2021-04-14 16:39:35 +00:00
func ( r * ACLResolver ) resolveLocallyManagedToken ( token string ) ( structs . ACLIdentity , acl . Authorizer , bool ) {
// can only resolve local tokens if we were given a token store
if r . tokens == nil {
return nil , nil , false
}
2021-12-07 12:12:47 +00:00
if r . tokens . IsAgentRecoveryToken ( token ) {
2022-01-20 12:47:50 +00:00
return structs . NewAgentRecoveryTokenIdentity ( r . config . NodeName , token ) , r . agentRecoveryAuthz , true
2021-04-14 16:39:35 +00:00
}
2022-09-09 19:05:38 +00:00
if r . backend . IsServerManagementToken ( token ) {
return structs . NewACLServerIdentity ( token ) , acl . ManageAll ( ) , true
}
2021-04-14 16:39:35 +00:00
return r . resolveLocallyManagedEnterpriseToken ( token )
}
2023-02-07 20:19:09 +00:00
// ResolveToken to an acl.Authorizer and structs.ACLIdentity. The acl.Authorizer
2023-02-07 18:26:30 +00:00
// can be used to check permissions granted to the token using its secret, and the
// ACLIdentity describes the token and any defaults applied to it.
2023-02-07 20:19:09 +00:00
func ( r * ACLResolver ) ResolveToken ( tokenSecretID string ) ( resolver . Result , error ) {
2018-10-19 16:04:07 +00:00
if ! r . ACLsEnabled ( ) {
2022-06-17 09:24:43 +00:00
return resolver . Result { Authorizer : acl . ManageAll ( ) } , nil
2018-10-19 16:04:07 +00:00
}
2023-02-07 18:26:30 +00:00
if acl . RootAuthorizer ( tokenSecretID ) != nil {
2022-06-17 09:24:43 +00:00
return resolver . Result { } , acl . ErrRootDenied
2018-10-19 16:04:07 +00:00
}
// handle the anonymous token
2023-02-07 18:26:30 +00:00
if tokenSecretID == "" {
tokenSecretID = anonymousToken
2018-10-19 16:04:07 +00:00
}
2023-02-07 18:26:30 +00:00
if ident , authz , ok := r . resolveLocallyManagedToken ( tokenSecretID ) ; ok {
2022-06-17 09:24:43 +00:00
return resolver . Result { Authorizer : authz , ACLIdentity : ident } , nil
2021-04-14 16:39:35 +00:00
}
2018-10-19 16:04:07 +00:00
defer metrics . MeasureSince ( [ ] string { "acl" , "ResolveToken" } , time . Now ( ) )
2023-02-07 18:26:30 +00:00
identity , policies , err := r . resolveTokenToIdentityAndPolicies ( tokenSecretID )
2018-10-19 16:04:07 +00:00
if err != nil {
2021-08-09 20:29:21 +00:00
r . handleACLDisabledError ( err )
2018-10-19 16:04:07 +00:00
if IsACLRemoteError ( err ) {
2020-01-28 23:50:41 +00:00
r . logger . Error ( "Error resolving token" , "error" , err )
2023-02-07 18:26:30 +00:00
ident := & missingIdentity { reason : "primary-dc-down" , token : tokenSecretID }
2022-06-17 09:24:43 +00:00
return resolver . Result { Authorizer : r . down , ACLIdentity : ident } , nil
2018-10-19 16:04:07 +00:00
}
2022-06-17 09:24:43 +00:00
return resolver . Result { } , err
2018-10-19 16:04:07 +00:00
}
// Build the Authorizer
2019-10-25 15:06:16 +00:00
var chain [ ] acl . Authorizer
2021-10-22 11:41:35 +00:00
var conf acl . Config
if r . aclConf != nil {
conf = * r . aclConf
}
2021-10-26 18:02:34 +00:00
setEnterpriseConf ( identity . EnterpriseMetadata ( ) , & conf )
2019-10-25 15:06:16 +00:00
2021-10-22 11:41:35 +00:00
authz , err := policies . Compile ( r . cache , & conf )
2019-10-25 15:06:16 +00:00
if err != nil {
2022-06-17 09:24:43 +00:00
return resolver . Result { } , err
2019-10-25 15:06:16 +00:00
}
chain = append ( chain , authz )
authz , err = r . resolveEnterpriseDefaultsForIdentity ( identity )
if err != nil {
if IsACLRemoteError ( err ) {
2020-01-28 23:50:41 +00:00
r . logger . Error ( "Error resolving identity defaults" , "error" , err )
2022-06-17 09:24:43 +00:00
return resolver . Result { Authorizer : r . down , ACLIdentity : identity } , nil
2019-10-25 15:06:16 +00:00
}
2022-06-17 09:24:43 +00:00
return resolver . Result { } , err
2019-10-25 15:06:16 +00:00
} else if authz != nil {
chain = append ( chain , authz )
}
2018-10-19 16:04:07 +00:00
2019-10-25 15:06:16 +00:00
chain = append ( chain , acl . RootAuthorizer ( r . config . ACLDefaultPolicy ) )
2022-06-17 09:24:43 +00:00
return resolver . Result { Authorizer : acl . NewChainedAuthorizer ( chain ) , ACLIdentity : identity } , nil
2022-03-18 17:32:25 +00:00
}
2018-10-19 16:04:07 +00:00
func ( r * ACLResolver ) ACLsEnabled ( ) bool {
// Whether we desire ACLs to be enabled according to configuration
2020-07-03 20:52:08 +00:00
if ! r . config . ACLsEnabled {
2018-10-19 16:04:07 +00:00
return false
}
2021-08-09 20:29:21 +00:00
if r . disableDuration != 0 {
2018-10-19 16:04:07 +00:00
// Whether ACLs are disabled according to RPCs failing with a ACLs Disabled error
r . disabledLock . RLock ( )
defer r . disabledLock . RUnlock ( )
2021-08-09 20:29:21 +00:00
return time . Now ( ) . After ( r . disabledUntil )
2018-10-19 16:04:07 +00:00
}
return true
}
peering: initial sync (#12842)
- Add endpoints related to peering: read, list, generate token, initiate peering
- Update node/service/check table indexing to account for peers
- Foundational changes for pushing service updates to a peer
- Plumb peer name through Health.ServiceNodes path
see: ENT-1765, ENT-1280, ENT-1283, ENT-1283, ENT-1756, ENT-1739, ENT-1750, ENT-1679,
ENT-1709, ENT-1704, ENT-1690, ENT-1689, ENT-1702, ENT-1701, ENT-1683, ENT-1663,
ENT-1650, ENT-1678, ENT-1628, ENT-1658, ENT-1640, ENT-1637, ENT-1597, ENT-1634,
ENT-1613, ENT-1616, ENT-1617, ENT-1591, ENT-1588, ENT-1596, ENT-1572, ENT-1555
Co-authored-by: R.B. Boyer <rb@hashicorp.com>
Co-authored-by: freddygv <freddy@hashicorp.com>
Co-authored-by: Chris S. Kim <ckim@hashicorp.com>
Co-authored-by: Evan Culver <eculver@hashicorp.com>
Co-authored-by: Nitya Dhanushkodi <nitya@hashicorp.com>
2022-04-21 22:34:40 +00:00
func ( r * ACLResolver ) ResolveTokenAndDefaultMeta (
2023-02-07 18:26:30 +00:00
tokenSecretID string ,
peering: initial sync (#12842)
- Add endpoints related to peering: read, list, generate token, initiate peering
- Update node/service/check table indexing to account for peers
- Foundational changes for pushing service updates to a peer
- Plumb peer name through Health.ServiceNodes path
see: ENT-1765, ENT-1280, ENT-1283, ENT-1283, ENT-1756, ENT-1739, ENT-1750, ENT-1679,
ENT-1709, ENT-1704, ENT-1690, ENT-1689, ENT-1702, ENT-1701, ENT-1683, ENT-1663,
ENT-1650, ENT-1678, ENT-1628, ENT-1658, ENT-1640, ENT-1637, ENT-1597, ENT-1634,
ENT-1613, ENT-1616, ENT-1617, ENT-1591, ENT-1588, ENT-1596, ENT-1572, ENT-1555
Co-authored-by: R.B. Boyer <rb@hashicorp.com>
Co-authored-by: freddygv <freddy@hashicorp.com>
Co-authored-by: Chris S. Kim <ckim@hashicorp.com>
Co-authored-by: Evan Culver <eculver@hashicorp.com>
Co-authored-by: Nitya Dhanushkodi <nitya@hashicorp.com>
2022-04-21 22:34:40 +00:00
entMeta * acl . EnterpriseMeta ,
authzContext * acl . AuthorizerContext ,
2022-06-17 09:24:43 +00:00
) ( resolver . Result , error ) {
2023-02-07 20:19:09 +00:00
result , err := r . ResolveToken ( tokenSecretID )
2022-01-22 19:12:08 +00:00
if err != nil {
2022-06-17 09:24:43 +00:00
return resolver . Result { } , err
2022-01-22 19:12:08 +00:00
}
if entMeta == nil {
2022-04-05 21:10:06 +00:00
entMeta = & acl . EnterpriseMeta { }
2022-01-22 19:12:08 +00:00
}
// Default the EnterpriseMeta based on the Tokens meta or actual defaults
// in the case of unknown identity
peering: initial sync (#12842)
- Add endpoints related to peering: read, list, generate token, initiate peering
- Update node/service/check table indexing to account for peers
- Foundational changes for pushing service updates to a peer
- Plumb peer name through Health.ServiceNodes path
see: ENT-1765, ENT-1280, ENT-1283, ENT-1283, ENT-1756, ENT-1739, ENT-1750, ENT-1679,
ENT-1709, ENT-1704, ENT-1690, ENT-1689, ENT-1702, ENT-1701, ENT-1683, ENT-1663,
ENT-1650, ENT-1678, ENT-1628, ENT-1658, ENT-1640, ENT-1637, ENT-1597, ENT-1634,
ENT-1613, ENT-1616, ENT-1617, ENT-1591, ENT-1588, ENT-1596, ENT-1572, ENT-1555
Co-authored-by: R.B. Boyer <rb@hashicorp.com>
Co-authored-by: freddygv <freddy@hashicorp.com>
Co-authored-by: Chris S. Kim <ckim@hashicorp.com>
Co-authored-by: Evan Culver <eculver@hashicorp.com>
Co-authored-by: Nitya Dhanushkodi <nitya@hashicorp.com>
2022-04-21 22:34:40 +00:00
switch {
2022-11-14 18:36:27 +00:00
case authzContext . PeerOrEmpty ( ) == "" && result . ACLIdentity != nil :
2022-01-23 17:31:48 +00:00
entMeta . Merge ( result . ACLIdentity . EnterpriseMetadata ( ) )
2022-11-14 18:36:27 +00:00
peering: initial sync (#12842)
- Add endpoints related to peering: read, list, generate token, initiate peering
- Update node/service/check table indexing to account for peers
- Foundational changes for pushing service updates to a peer
- Plumb peer name through Health.ServiceNodes path
see: ENT-1765, ENT-1280, ENT-1283, ENT-1283, ENT-1756, ENT-1739, ENT-1750, ENT-1679,
ENT-1709, ENT-1704, ENT-1690, ENT-1689, ENT-1702, ENT-1701, ENT-1683, ENT-1663,
ENT-1650, ENT-1678, ENT-1628, ENT-1658, ENT-1640, ENT-1637, ENT-1597, ENT-1634,
ENT-1613, ENT-1616, ENT-1617, ENT-1591, ENT-1588, ENT-1596, ENT-1572, ENT-1555
Co-authored-by: R.B. Boyer <rb@hashicorp.com>
Co-authored-by: freddygv <freddy@hashicorp.com>
Co-authored-by: Chris S. Kim <ckim@hashicorp.com>
Co-authored-by: Evan Culver <eculver@hashicorp.com>
Co-authored-by: Nitya Dhanushkodi <nitya@hashicorp.com>
2022-04-21 22:34:40 +00:00
case result . ACLIdentity != nil :
// We _do not_ normalize the enterprise meta from the token when a peer
// name was specified because namespaces across clusters are not
// equivalent. A local namespace is _never_ correct for a remote query.
entMeta . Merge (
structs . DefaultEnterpriseMetaInPartition (
result . ACLIdentity . EnterpriseMetadata ( ) . PartitionOrDefault ( ) ,
) ,
)
default :
2022-01-22 19:12:08 +00:00
entMeta . Merge ( structs . DefaultEnterpriseMetaInDefaultPartition ( ) )
}
// Use the meta to fill in the ACL authorization context
entMeta . FillAuthzContext ( authzContext )
2022-01-23 17:31:48 +00:00
return result , err
2022-01-22 19:12:08 +00:00
}
2021-07-30 21:19:57 +00:00
func filterACLWithAuthorizer ( logger hclog . Logger , authorizer acl . Authorizer , subj interface { } ) {
2022-07-01 15:18:33 +00:00
aclfilter . New ( authorizer , logger ) . Filter ( subj )
2015-06-09 19:36:25 +00:00
}
2016-12-09 00:01:01 +00:00
2021-07-30 21:19:57 +00:00
// filterACL uses the ACLResolver to resolve the token in an acl.Authorizer,
// then uses the acl.Authorizer to filter subj. Any entities in subj that are
// not authorized for read access will be removed from subj.
2023-02-07 18:26:30 +00:00
func filterACL ( r * ACLResolver , tokenSecretID string , subj interface { } ) error {
2018-10-19 16:04:07 +00:00
// Get the ACL from the token
2023-02-07 20:19:09 +00:00
authorizer , err := r . ResolveToken ( tokenSecretID )
2018-10-19 16:04:07 +00:00
if err != nil {
return err
}
2021-07-30 21:19:57 +00:00
filterACLWithAuthorizer ( r . logger , authorizer , subj )
2021-07-30 21:08:58 +00:00
return nil
2018-10-19 16:04:07 +00:00
}
2021-10-24 22:28:46 +00:00
type partitionInfoNoop struct { }
2021-12-03 06:50:38 +00:00
func ( p * partitionInfoNoop ) ExportsForPartition ( partition string ) acl . ExportedServices {
return acl . ExportedServices { }
2021-10-24 22:28:46 +00:00
}