mirror of https://github.com/hashicorp/consul
ACL Authorizer overhaul (#6620)
* ACL Authorizer overhaul To account for upcoming features every Authorization function can now take an extra *acl.EnterpriseAuthorizerContext. These are unused in OSS and will always be nil. Additionally the acl package has received some thorough refactoring to enable all of the extra Consul Enterprise specific authorizations including moving sentinel enforcement into the stubbed structs. The Authorizer funcs now return an acl.EnforcementDecision instead of a boolean. This improves the overall interface as it makes multiple Authorizers easily chainable as they now indicate whether they had an authoritative decision or should use some other defaults. A ChainedAuthorizer was added to handle this Authorizer enforcement chain and will never itself return a non-authoritative decision. * Include stub for extra enterprise rules in the global management policy * Allow for an upgrade of the global-management policypull/6626/head
parent
6d645fe53c
commit
973341a592
|
@ -293,7 +293,7 @@ jobs:
|
||||||
|
|
||||||
# make dev build of nomad
|
# make dev build of nomad
|
||||||
- run:
|
- run:
|
||||||
command: make dev
|
command: make pkg/linux_amd64/nomad
|
||||||
working_directory: *NOMAD_WORKING_DIR
|
working_directory: *NOMAD_WORKING_DIR
|
||||||
|
|
||||||
# update gotestsum
|
# update gotestsum
|
||||||
|
|
1010
acl/acl.go
1010
acl/acl.go
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,6 @@
|
||||||
|
// +build !consulent
|
||||||
|
|
||||||
|
package acl
|
||||||
|
|
||||||
|
// EnterpriseACLConfig stub
|
||||||
|
type EnterpriseACLConfig struct{}
|
1570
acl/acl_test.go
1570
acl/acl_test.go
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,128 @@
|
||||||
|
package acl
|
||||||
|
|
||||||
|
type EnforcementDecision int
|
||||||
|
|
||||||
|
const (
|
||||||
|
// Deny returned from an Authorizer enforcement method indicates
|
||||||
|
// that a corresponding rule was found and that access should be denied
|
||||||
|
Deny EnforcementDecision = iota
|
||||||
|
// Allow returned from an Authorizer enforcement method indicates
|
||||||
|
// that a corresponding rule was found and that access should be allowed
|
||||||
|
Allow
|
||||||
|
// Default returned from an Authorizer enforcement method indicates
|
||||||
|
// that a corresponding rule was not found and that whether access
|
||||||
|
// should be granted or denied should be deferred to the default
|
||||||
|
// access level
|
||||||
|
Default
|
||||||
|
)
|
||||||
|
|
||||||
|
func (d EnforcementDecision) String() string {
|
||||||
|
switch d {
|
||||||
|
case Allow:
|
||||||
|
return "Allow"
|
||||||
|
case Deny:
|
||||||
|
return "Deny"
|
||||||
|
case Default:
|
||||||
|
return "Default"
|
||||||
|
default:
|
||||||
|
return "Unknown"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Authorizer is the interface for policy enforcement.
|
||||||
|
type Authorizer interface {
|
||||||
|
// ACLRead checks for permission to list all the ACLs
|
||||||
|
ACLRead(*EnterpriseAuthorizerContext) EnforcementDecision
|
||||||
|
|
||||||
|
// ACLWrite checks for permission to manipulate ACLs
|
||||||
|
ACLWrite(*EnterpriseAuthorizerContext) EnforcementDecision
|
||||||
|
|
||||||
|
// AgentRead checks for permission to read from agent endpoints for a
|
||||||
|
// given node.
|
||||||
|
AgentRead(string, *EnterpriseAuthorizerContext) EnforcementDecision
|
||||||
|
|
||||||
|
// AgentWrite checks for permission to make changes via agent endpoints
|
||||||
|
// for a given node.
|
||||||
|
AgentWrite(string, *EnterpriseAuthorizerContext) EnforcementDecision
|
||||||
|
|
||||||
|
// EventRead determines if a specific event can be queried.
|
||||||
|
EventRead(string, *EnterpriseAuthorizerContext) EnforcementDecision
|
||||||
|
|
||||||
|
// EventWrite determines if a specific event may be fired.
|
||||||
|
EventWrite(string, *EnterpriseAuthorizerContext) EnforcementDecision
|
||||||
|
|
||||||
|
// IntentionDefaultAllow determines the default authorized behavior
|
||||||
|
// when no intentions match a Connect request.
|
||||||
|
IntentionDefaultAllow(*EnterpriseAuthorizerContext) EnforcementDecision
|
||||||
|
|
||||||
|
// IntentionRead determines if a specific intention can be read.
|
||||||
|
IntentionRead(string, *EnterpriseAuthorizerContext) EnforcementDecision
|
||||||
|
|
||||||
|
// IntentionWrite determines if a specific intention can be
|
||||||
|
// created, modified, or deleted.
|
||||||
|
IntentionWrite(string, *EnterpriseAuthorizerContext) EnforcementDecision
|
||||||
|
|
||||||
|
// KeyList checks for permission to list keys under a prefix
|
||||||
|
KeyList(string, *EnterpriseAuthorizerContext) EnforcementDecision
|
||||||
|
|
||||||
|
// KeyRead checks for permission to read a given key
|
||||||
|
KeyRead(string, *EnterpriseAuthorizerContext) EnforcementDecision
|
||||||
|
|
||||||
|
// KeyWrite checks for permission to write a given key
|
||||||
|
KeyWrite(string, *EnterpriseAuthorizerContext) EnforcementDecision
|
||||||
|
|
||||||
|
// KeyWritePrefix checks for permission to write to an
|
||||||
|
// entire key prefix. This means there must be no sub-policies
|
||||||
|
// that deny a write.
|
||||||
|
KeyWritePrefix(string, *EnterpriseAuthorizerContext) EnforcementDecision
|
||||||
|
|
||||||
|
// KeyringRead determines if the encryption keyring used in
|
||||||
|
// the gossip layer can be read.
|
||||||
|
KeyringRead(*EnterpriseAuthorizerContext) EnforcementDecision
|
||||||
|
|
||||||
|
// KeyringWrite determines if the keyring can be manipulated
|
||||||
|
KeyringWrite(*EnterpriseAuthorizerContext) EnforcementDecision
|
||||||
|
|
||||||
|
// NodeRead checks for permission to read (discover) a given node.
|
||||||
|
NodeRead(string, *EnterpriseAuthorizerContext) EnforcementDecision
|
||||||
|
|
||||||
|
// NodeWrite checks for permission to create or update (register) a
|
||||||
|
// given node.
|
||||||
|
NodeWrite(string, *EnterpriseAuthorizerContext) EnforcementDecision
|
||||||
|
|
||||||
|
// OperatorRead determines if the read-only Consul operator functions
|
||||||
|
// can be used.
|
||||||
|
OperatorRead(*EnterpriseAuthorizerContext) EnforcementDecision
|
||||||
|
|
||||||
|
// OperatorWrite determines if the state-changing Consul operator
|
||||||
|
// functions can be used.
|
||||||
|
OperatorWrite(*EnterpriseAuthorizerContext) EnforcementDecision
|
||||||
|
|
||||||
|
// PreparedQueryRead determines if a specific prepared query can be read
|
||||||
|
// to show its contents (this is not used for execution).
|
||||||
|
PreparedQueryRead(string, *EnterpriseAuthorizerContext) EnforcementDecision
|
||||||
|
|
||||||
|
// PreparedQueryWrite determines if a specific prepared query can be
|
||||||
|
// created, modified, or deleted.
|
||||||
|
PreparedQueryWrite(string, *EnterpriseAuthorizerContext) EnforcementDecision
|
||||||
|
|
||||||
|
// ServiceRead checks for permission to read a given service
|
||||||
|
ServiceRead(string, *EnterpriseAuthorizerContext) EnforcementDecision
|
||||||
|
|
||||||
|
// ServiceWrite checks for permission to create or update a given
|
||||||
|
// service
|
||||||
|
ServiceWrite(string, *EnterpriseAuthorizerContext) EnforcementDecision
|
||||||
|
|
||||||
|
// SessionRead checks for permission to read sessions for a given node.
|
||||||
|
SessionRead(string, *EnterpriseAuthorizerContext) EnforcementDecision
|
||||||
|
|
||||||
|
// SessionWrite checks for permission to create sessions for a given
|
||||||
|
// node.
|
||||||
|
SessionWrite(string, *EnterpriseAuthorizerContext) EnforcementDecision
|
||||||
|
|
||||||
|
// Snapshot checks for permission to take and restore snapshots.
|
||||||
|
Snapshot(*EnterpriseAuthorizerContext) EnforcementDecision
|
||||||
|
|
||||||
|
// Embedded Interface for Consul Enterprise specific ACL enforcement
|
||||||
|
EnterpriseAuthorizer
|
||||||
|
}
|
|
@ -0,0 +1,9 @@
|
||||||
|
// +build !consulent
|
||||||
|
|
||||||
|
package acl
|
||||||
|
|
||||||
|
// EnterpriseAuthorizerContext stub
|
||||||
|
type EnterpriseAuthorizerContext struct{}
|
||||||
|
|
||||||
|
// EnterpriseAuthorizer stub interface
|
||||||
|
type EnterpriseAuthorizer interface{}
|
|
@ -0,0 +1,226 @@
|
||||||
|
package acl
|
||||||
|
|
||||||
|
// ChainedAuthorizer can combine multiple Authorizers into one.
|
||||||
|
// Each Authorizer in the chain is asked (in order) for an
|
||||||
|
// enforcement decision. The first non-Default decision that
|
||||||
|
// is rendered by an Authorizer in the chain will be used
|
||||||
|
// as the overall decision of the ChainedAuthorizer
|
||||||
|
type ChainedAuthorizer struct {
|
||||||
|
chain []Authorizer
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewChainedAuthorizer creates a ChainedAuthorizer with the provided
|
||||||
|
// chain of Authorizers. The slice provided should be in the order of
|
||||||
|
// most precedent Authorizer at the beginning and least precedent
|
||||||
|
// Authorizer at the end.
|
||||||
|
func NewChainedAuthorizer(chain []Authorizer) *ChainedAuthorizer {
|
||||||
|
return &ChainedAuthorizer{
|
||||||
|
chain: chain,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *ChainedAuthorizer) executeChain(enforce func(authz Authorizer) EnforcementDecision) EnforcementDecision {
|
||||||
|
for _, authz := range c.chain {
|
||||||
|
decision := enforce(authz)
|
||||||
|
if decision != Default {
|
||||||
|
return decision
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Deny
|
||||||
|
}
|
||||||
|
|
||||||
|
// ACLRead checks for permission to list all the ACLs
|
||||||
|
func (c *ChainedAuthorizer) ACLRead(entCtx *EnterpriseAuthorizerContext) EnforcementDecision {
|
||||||
|
return c.executeChain(func(authz Authorizer) EnforcementDecision {
|
||||||
|
return authz.ACLRead(entCtx)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// ACLWrite checks for permission to manipulate ACLs
|
||||||
|
func (c *ChainedAuthorizer) ACLWrite(entCtx *EnterpriseAuthorizerContext) EnforcementDecision {
|
||||||
|
return c.executeChain(func(authz Authorizer) EnforcementDecision {
|
||||||
|
return authz.ACLWrite(entCtx)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// AgentRead checks for permission to read from agent endpoints for a
|
||||||
|
// given node.
|
||||||
|
func (c *ChainedAuthorizer) AgentRead(node string, entCtx *EnterpriseAuthorizerContext) EnforcementDecision {
|
||||||
|
return c.executeChain(func(authz Authorizer) EnforcementDecision {
|
||||||
|
return authz.AgentRead(node, entCtx)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// AgentWrite checks for permission to make changes via agent endpoints
|
||||||
|
// for a given node.
|
||||||
|
func (c *ChainedAuthorizer) AgentWrite(node string, entCtx *EnterpriseAuthorizerContext) EnforcementDecision {
|
||||||
|
return c.executeChain(func(authz Authorizer) EnforcementDecision {
|
||||||
|
return authz.AgentWrite(node, entCtx)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// EventRead determines if a specific event can be queried.
|
||||||
|
func (c *ChainedAuthorizer) EventRead(name string, entCtx *EnterpriseAuthorizerContext) EnforcementDecision {
|
||||||
|
return c.executeChain(func(authz Authorizer) EnforcementDecision {
|
||||||
|
return authz.EventRead(name, entCtx)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// EventWrite determines if a specific event may be fired.
|
||||||
|
func (c *ChainedAuthorizer) EventWrite(name string, entCtx *EnterpriseAuthorizerContext) EnforcementDecision {
|
||||||
|
return c.executeChain(func(authz Authorizer) EnforcementDecision {
|
||||||
|
return authz.EventWrite(name, entCtx)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// IntentionDefaultAllow determines the default authorized behavior
|
||||||
|
// when no intentions match a Connect request.
|
||||||
|
func (c *ChainedAuthorizer) IntentionDefaultAllow(entCtx *EnterpriseAuthorizerContext) EnforcementDecision {
|
||||||
|
return c.executeChain(func(authz Authorizer) EnforcementDecision {
|
||||||
|
return authz.IntentionDefaultAllow(entCtx)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// IntentionRead determines if a specific intention can be read.
|
||||||
|
func (c *ChainedAuthorizer) IntentionRead(prefix string, entCtx *EnterpriseAuthorizerContext) EnforcementDecision {
|
||||||
|
return c.executeChain(func(authz Authorizer) EnforcementDecision {
|
||||||
|
return authz.IntentionRead(prefix, entCtx)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// IntentionWrite determines if a specific intention can be
|
||||||
|
// created, modified, or deleted.
|
||||||
|
func (c *ChainedAuthorizer) IntentionWrite(prefix string, entCtx *EnterpriseAuthorizerContext) EnforcementDecision {
|
||||||
|
return c.executeChain(func(authz Authorizer) EnforcementDecision {
|
||||||
|
return authz.IntentionWrite(prefix, entCtx)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// KeyList checks for permission to list keys under a prefix
|
||||||
|
func (c *ChainedAuthorizer) KeyList(keyPrefix string, entCtx *EnterpriseAuthorizerContext) EnforcementDecision {
|
||||||
|
return c.executeChain(func(authz Authorizer) EnforcementDecision {
|
||||||
|
return authz.KeyList(keyPrefix, entCtx)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// KeyRead checks for permission to read a given key
|
||||||
|
func (c *ChainedAuthorizer) KeyRead(key string, entCtx *EnterpriseAuthorizerContext) EnforcementDecision {
|
||||||
|
return c.executeChain(func(authz Authorizer) EnforcementDecision {
|
||||||
|
return authz.KeyRead(key, entCtx)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// KeyWrite checks for permission to write a given key
|
||||||
|
func (c *ChainedAuthorizer) KeyWrite(key string, entCtx *EnterpriseAuthorizerContext) EnforcementDecision {
|
||||||
|
return c.executeChain(func(authz Authorizer) EnforcementDecision {
|
||||||
|
return authz.KeyWrite(key, entCtx)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// KeyWritePrefix checks for permission to write to an
|
||||||
|
// entire key prefix. This means there must be no sub-policies
|
||||||
|
// that deny a write.
|
||||||
|
func (c *ChainedAuthorizer) KeyWritePrefix(keyPrefix string, entCtx *EnterpriseAuthorizerContext) EnforcementDecision {
|
||||||
|
return c.executeChain(func(authz Authorizer) EnforcementDecision {
|
||||||
|
return authz.KeyWritePrefix(keyPrefix, entCtx)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// KeyringRead determines if the encryption keyring used in
|
||||||
|
// the gossip layer can be read.
|
||||||
|
func (c *ChainedAuthorizer) KeyringRead(entCtx *EnterpriseAuthorizerContext) EnforcementDecision {
|
||||||
|
return c.executeChain(func(authz Authorizer) EnforcementDecision {
|
||||||
|
return authz.KeyringRead(entCtx)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// KeyringWrite determines if the keyring can be manipulated
|
||||||
|
func (c *ChainedAuthorizer) KeyringWrite(entCtx *EnterpriseAuthorizerContext) EnforcementDecision {
|
||||||
|
return c.executeChain(func(authz Authorizer) EnforcementDecision {
|
||||||
|
return authz.KeyringWrite(entCtx)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// NodeRead checks for permission to read (discover) a given node.
|
||||||
|
func (c *ChainedAuthorizer) NodeRead(node string, entCtx *EnterpriseAuthorizerContext) EnforcementDecision {
|
||||||
|
return c.executeChain(func(authz Authorizer) EnforcementDecision {
|
||||||
|
return authz.NodeRead(node, entCtx)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// NodeWrite checks for permission to create or update (register) a
|
||||||
|
// given node.
|
||||||
|
func (c *ChainedAuthorizer) NodeWrite(node string, entCtx *EnterpriseAuthorizerContext) EnforcementDecision {
|
||||||
|
return c.executeChain(func(authz Authorizer) EnforcementDecision {
|
||||||
|
return authz.NodeWrite(node, entCtx)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// OperatorRead determines if the read-only Consul operator functions
|
||||||
|
// can be used.
|
||||||
|
func (c *ChainedAuthorizer) OperatorRead(entCtx *EnterpriseAuthorizerContext) EnforcementDecision {
|
||||||
|
return c.executeChain(func(authz Authorizer) EnforcementDecision {
|
||||||
|
return authz.OperatorRead(entCtx)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// OperatorWrite determines if the state-changing Consul operator
|
||||||
|
// functions can be used.
|
||||||
|
func (c *ChainedAuthorizer) OperatorWrite(entCtx *EnterpriseAuthorizerContext) EnforcementDecision {
|
||||||
|
return c.executeChain(func(authz Authorizer) EnforcementDecision {
|
||||||
|
return authz.OperatorWrite(entCtx)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// PreparedQueryRead determines if a specific prepared query can be read
|
||||||
|
// to show its contents (this is not used for execution).
|
||||||
|
func (c *ChainedAuthorizer) PreparedQueryRead(query string, entCtx *EnterpriseAuthorizerContext) EnforcementDecision {
|
||||||
|
return c.executeChain(func(authz Authorizer) EnforcementDecision {
|
||||||
|
return authz.PreparedQueryRead(query, entCtx)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// PreparedQueryWrite determines if a specific prepared query can be
|
||||||
|
// created, modified, or deleted.
|
||||||
|
func (c *ChainedAuthorizer) PreparedQueryWrite(query string, entCtx *EnterpriseAuthorizerContext) EnforcementDecision {
|
||||||
|
return c.executeChain(func(authz Authorizer) EnforcementDecision {
|
||||||
|
return authz.PreparedQueryWrite(query, entCtx)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// ServiceRead checks for permission to read a given service
|
||||||
|
func (c *ChainedAuthorizer) ServiceRead(name string, entCtx *EnterpriseAuthorizerContext) EnforcementDecision {
|
||||||
|
return c.executeChain(func(authz Authorizer) EnforcementDecision {
|
||||||
|
return authz.ServiceRead(name, entCtx)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// ServiceWrite checks for permission to create or update a given
|
||||||
|
// service
|
||||||
|
func (c *ChainedAuthorizer) ServiceWrite(name string, entCtx *EnterpriseAuthorizerContext) EnforcementDecision {
|
||||||
|
return c.executeChain(func(authz Authorizer) EnforcementDecision {
|
||||||
|
return authz.ServiceWrite(name, entCtx)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// SessionRead checks for permission to read sessions for a given node.
|
||||||
|
func (c *ChainedAuthorizer) SessionRead(node string, entCtx *EnterpriseAuthorizerContext) EnforcementDecision {
|
||||||
|
return c.executeChain(func(authz Authorizer) EnforcementDecision {
|
||||||
|
return authz.SessionRead(node, entCtx)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// SessionWrite checks for permission to create sessions for a given
|
||||||
|
// node.
|
||||||
|
func (c *ChainedAuthorizer) SessionWrite(node string, entCtx *EnterpriseAuthorizerContext) EnforcementDecision {
|
||||||
|
return c.executeChain(func(authz Authorizer) EnforcementDecision {
|
||||||
|
return authz.SessionWrite(node, entCtx)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Snapshot checks for permission to take and restore snapshots.
|
||||||
|
func (c *ChainedAuthorizer) Snapshot(entCtx *EnterpriseAuthorizerContext) EnforcementDecision {
|
||||||
|
return c.executeChain(func(authz Authorizer) EnforcementDecision {
|
||||||
|
return authz.Snapshot(entCtx)
|
||||||
|
})
|
||||||
|
}
|
|
@ -0,0 +1,3 @@
|
||||||
|
// +build !consulent
|
||||||
|
|
||||||
|
package acl
|
|
@ -0,0 +1,247 @@
|
||||||
|
package acl
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
type testAuthorizer EnforcementDecision
|
||||||
|
|
||||||
|
func (authz testAuthorizer) ACLRead(*EnterpriseAuthorizerContext) EnforcementDecision {
|
||||||
|
return EnforcementDecision(authz)
|
||||||
|
}
|
||||||
|
func (authz testAuthorizer) ACLWrite(*EnterpriseAuthorizerContext) EnforcementDecision {
|
||||||
|
return EnforcementDecision(authz)
|
||||||
|
}
|
||||||
|
func (authz testAuthorizer) AgentRead(string, *EnterpriseAuthorizerContext) EnforcementDecision {
|
||||||
|
return EnforcementDecision(authz)
|
||||||
|
}
|
||||||
|
func (authz testAuthorizer) AgentWrite(string, *EnterpriseAuthorizerContext) EnforcementDecision {
|
||||||
|
return EnforcementDecision(authz)
|
||||||
|
}
|
||||||
|
func (authz testAuthorizer) EventRead(string, *EnterpriseAuthorizerContext) EnforcementDecision {
|
||||||
|
return EnforcementDecision(authz)
|
||||||
|
}
|
||||||
|
func (authz testAuthorizer) EventWrite(string, *EnterpriseAuthorizerContext) EnforcementDecision {
|
||||||
|
return EnforcementDecision(authz)
|
||||||
|
}
|
||||||
|
func (authz testAuthorizer) IntentionDefaultAllow(*EnterpriseAuthorizerContext) EnforcementDecision {
|
||||||
|
return EnforcementDecision(authz)
|
||||||
|
}
|
||||||
|
func (authz testAuthorizer) IntentionRead(string, *EnterpriseAuthorizerContext) EnforcementDecision {
|
||||||
|
return EnforcementDecision(authz)
|
||||||
|
}
|
||||||
|
func (authz testAuthorizer) IntentionWrite(string, *EnterpriseAuthorizerContext) EnforcementDecision {
|
||||||
|
return EnforcementDecision(authz)
|
||||||
|
}
|
||||||
|
func (authz testAuthorizer) KeyList(string, *EnterpriseAuthorizerContext) EnforcementDecision {
|
||||||
|
return EnforcementDecision(authz)
|
||||||
|
}
|
||||||
|
func (authz testAuthorizer) KeyRead(string, *EnterpriseAuthorizerContext) EnforcementDecision {
|
||||||
|
return EnforcementDecision(authz)
|
||||||
|
}
|
||||||
|
func (authz testAuthorizer) KeyWrite(string, *EnterpriseAuthorizerContext) EnforcementDecision {
|
||||||
|
return EnforcementDecision(authz)
|
||||||
|
}
|
||||||
|
func (authz testAuthorizer) KeyWritePrefix(string, *EnterpriseAuthorizerContext) EnforcementDecision {
|
||||||
|
return EnforcementDecision(authz)
|
||||||
|
}
|
||||||
|
func (authz testAuthorizer) KeyringRead(*EnterpriseAuthorizerContext) EnforcementDecision {
|
||||||
|
return EnforcementDecision(authz)
|
||||||
|
}
|
||||||
|
func (authz testAuthorizer) KeyringWrite(*EnterpriseAuthorizerContext) EnforcementDecision {
|
||||||
|
return EnforcementDecision(authz)
|
||||||
|
}
|
||||||
|
func (authz testAuthorizer) NodeRead(string, *EnterpriseAuthorizerContext) EnforcementDecision {
|
||||||
|
return EnforcementDecision(authz)
|
||||||
|
}
|
||||||
|
func (authz testAuthorizer) NodeWrite(string, *EnterpriseAuthorizerContext) EnforcementDecision {
|
||||||
|
return EnforcementDecision(authz)
|
||||||
|
}
|
||||||
|
func (authz testAuthorizer) OperatorRead(*EnterpriseAuthorizerContext) EnforcementDecision {
|
||||||
|
return EnforcementDecision(authz)
|
||||||
|
}
|
||||||
|
func (authz testAuthorizer) OperatorWrite(*EnterpriseAuthorizerContext) EnforcementDecision {
|
||||||
|
return EnforcementDecision(authz)
|
||||||
|
}
|
||||||
|
func (authz testAuthorizer) PreparedQueryRead(string, *EnterpriseAuthorizerContext) EnforcementDecision {
|
||||||
|
return EnforcementDecision(authz)
|
||||||
|
}
|
||||||
|
func (authz testAuthorizer) PreparedQueryWrite(string, *EnterpriseAuthorizerContext) EnforcementDecision {
|
||||||
|
return EnforcementDecision(authz)
|
||||||
|
}
|
||||||
|
func (authz testAuthorizer) ServiceRead(string, *EnterpriseAuthorizerContext) EnforcementDecision {
|
||||||
|
return EnforcementDecision(authz)
|
||||||
|
}
|
||||||
|
func (authz testAuthorizer) ServiceWrite(string, *EnterpriseAuthorizerContext) EnforcementDecision {
|
||||||
|
return EnforcementDecision(authz)
|
||||||
|
}
|
||||||
|
func (authz testAuthorizer) SessionRead(string, *EnterpriseAuthorizerContext) EnforcementDecision {
|
||||||
|
return EnforcementDecision(authz)
|
||||||
|
}
|
||||||
|
func (authz testAuthorizer) SessionWrite(string, *EnterpriseAuthorizerContext) EnforcementDecision {
|
||||||
|
return EnforcementDecision(authz)
|
||||||
|
}
|
||||||
|
func (authz testAuthorizer) Snapshot(*EnterpriseAuthorizerContext) EnforcementDecision {
|
||||||
|
return EnforcementDecision(authz)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestChainedAuthorizer(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
t.Run("No Authorizers", func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
authz := NewChainedAuthorizer([]Authorizer{})
|
||||||
|
checkDenyACLRead(t, authz, "foo", nil)
|
||||||
|
checkDenyACLWrite(t, authz, "foo", nil)
|
||||||
|
checkDenyAgentRead(t, authz, "foo", nil)
|
||||||
|
checkDenyAgentWrite(t, authz, "foo", nil)
|
||||||
|
checkDenyEventRead(t, authz, "foo", nil)
|
||||||
|
checkDenyEventWrite(t, authz, "foo", nil)
|
||||||
|
checkDenyIntentionDefaultAllow(t, authz, "foo", nil)
|
||||||
|
checkDenyIntentionRead(t, authz, "foo", nil)
|
||||||
|
checkDenyIntentionWrite(t, authz, "foo", nil)
|
||||||
|
checkDenyKeyRead(t, authz, "foo", nil)
|
||||||
|
checkDenyKeyList(t, authz, "foo", nil)
|
||||||
|
checkDenyKeyringRead(t, authz, "foo", nil)
|
||||||
|
checkDenyKeyringWrite(t, authz, "foo", nil)
|
||||||
|
checkDenyKeyWrite(t, authz, "foo", nil)
|
||||||
|
checkDenyKeyWritePrefix(t, authz, "foo", nil)
|
||||||
|
checkDenyNodeRead(t, authz, "foo", nil)
|
||||||
|
checkDenyNodeWrite(t, authz, "foo", nil)
|
||||||
|
checkDenyOperatorRead(t, authz, "foo", nil)
|
||||||
|
checkDenyOperatorWrite(t, authz, "foo", nil)
|
||||||
|
checkDenyPreparedQueryRead(t, authz, "foo", nil)
|
||||||
|
checkDenyPreparedQueryWrite(t, authz, "foo", nil)
|
||||||
|
checkDenyServiceRead(t, authz, "foo", nil)
|
||||||
|
checkDenyServiceWrite(t, authz, "foo", nil)
|
||||||
|
checkDenySessionRead(t, authz, "foo", nil)
|
||||||
|
checkDenySessionWrite(t, authz, "foo", nil)
|
||||||
|
checkDenySnapshot(t, authz, "foo", nil)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("Authorizer Defaults", func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
authz := NewChainedAuthorizer([]Authorizer{testAuthorizer(Default)})
|
||||||
|
checkDenyACLRead(t, authz, "foo", nil)
|
||||||
|
checkDenyACLWrite(t, authz, "foo", nil)
|
||||||
|
checkDenyAgentRead(t, authz, "foo", nil)
|
||||||
|
checkDenyAgentWrite(t, authz, "foo", nil)
|
||||||
|
checkDenyEventRead(t, authz, "foo", nil)
|
||||||
|
checkDenyEventWrite(t, authz, "foo", nil)
|
||||||
|
checkDenyIntentionDefaultAllow(t, authz, "foo", nil)
|
||||||
|
checkDenyIntentionRead(t, authz, "foo", nil)
|
||||||
|
checkDenyIntentionWrite(t, authz, "foo", nil)
|
||||||
|
checkDenyKeyRead(t, authz, "foo", nil)
|
||||||
|
checkDenyKeyList(t, authz, "foo", nil)
|
||||||
|
checkDenyKeyringRead(t, authz, "foo", nil)
|
||||||
|
checkDenyKeyringWrite(t, authz, "foo", nil)
|
||||||
|
checkDenyKeyWrite(t, authz, "foo", nil)
|
||||||
|
checkDenyKeyWritePrefix(t, authz, "foo", nil)
|
||||||
|
checkDenyNodeRead(t, authz, "foo", nil)
|
||||||
|
checkDenyNodeWrite(t, authz, "foo", nil)
|
||||||
|
checkDenyOperatorRead(t, authz, "foo", nil)
|
||||||
|
checkDenyOperatorWrite(t, authz, "foo", nil)
|
||||||
|
checkDenyPreparedQueryRead(t, authz, "foo", nil)
|
||||||
|
checkDenyPreparedQueryWrite(t, authz, "foo", nil)
|
||||||
|
checkDenyServiceRead(t, authz, "foo", nil)
|
||||||
|
checkDenyServiceWrite(t, authz, "foo", nil)
|
||||||
|
checkDenySessionRead(t, authz, "foo", nil)
|
||||||
|
checkDenySessionWrite(t, authz, "foo", nil)
|
||||||
|
checkDenySnapshot(t, authz, "foo", nil)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("Authorizer No Defaults", func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
authz := NewChainedAuthorizer([]Authorizer{testAuthorizer(Allow)})
|
||||||
|
checkAllowACLRead(t, authz, "foo", nil)
|
||||||
|
checkAllowACLWrite(t, authz, "foo", nil)
|
||||||
|
checkAllowAgentRead(t, authz, "foo", nil)
|
||||||
|
checkAllowAgentWrite(t, authz, "foo", nil)
|
||||||
|
checkAllowEventRead(t, authz, "foo", nil)
|
||||||
|
checkAllowEventWrite(t, authz, "foo", nil)
|
||||||
|
checkAllowIntentionDefaultAllow(t, authz, "foo", nil)
|
||||||
|
checkAllowIntentionRead(t, authz, "foo", nil)
|
||||||
|
checkAllowIntentionWrite(t, authz, "foo", nil)
|
||||||
|
checkAllowKeyRead(t, authz, "foo", nil)
|
||||||
|
checkAllowKeyList(t, authz, "foo", nil)
|
||||||
|
checkAllowKeyringRead(t, authz, "foo", nil)
|
||||||
|
checkAllowKeyringWrite(t, authz, "foo", nil)
|
||||||
|
checkAllowKeyWrite(t, authz, "foo", nil)
|
||||||
|
checkAllowKeyWritePrefix(t, authz, "foo", nil)
|
||||||
|
checkAllowNodeRead(t, authz, "foo", nil)
|
||||||
|
checkAllowNodeWrite(t, authz, "foo", nil)
|
||||||
|
checkAllowOperatorRead(t, authz, "foo", nil)
|
||||||
|
checkAllowOperatorWrite(t, authz, "foo", nil)
|
||||||
|
checkAllowPreparedQueryRead(t, authz, "foo", nil)
|
||||||
|
checkAllowPreparedQueryWrite(t, authz, "foo", nil)
|
||||||
|
checkAllowServiceRead(t, authz, "foo", nil)
|
||||||
|
checkAllowServiceWrite(t, authz, "foo", nil)
|
||||||
|
checkAllowSessionRead(t, authz, "foo", nil)
|
||||||
|
checkAllowSessionWrite(t, authz, "foo", nil)
|
||||||
|
checkAllowSnapshot(t, authz, "foo", nil)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("First Found", func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
authz := NewChainedAuthorizer([]Authorizer{testAuthorizer(Deny), testAuthorizer(Allow)})
|
||||||
|
checkDenyACLRead(t, authz, "foo", nil)
|
||||||
|
checkDenyACLWrite(t, authz, "foo", nil)
|
||||||
|
checkDenyAgentRead(t, authz, "foo", nil)
|
||||||
|
checkDenyAgentWrite(t, authz, "foo", nil)
|
||||||
|
checkDenyEventRead(t, authz, "foo", nil)
|
||||||
|
checkDenyEventWrite(t, authz, "foo", nil)
|
||||||
|
checkDenyIntentionDefaultAllow(t, authz, "foo", nil)
|
||||||
|
checkDenyIntentionRead(t, authz, "foo", nil)
|
||||||
|
checkDenyIntentionWrite(t, authz, "foo", nil)
|
||||||
|
checkDenyKeyRead(t, authz, "foo", nil)
|
||||||
|
checkDenyKeyList(t, authz, "foo", nil)
|
||||||
|
checkDenyKeyringRead(t, authz, "foo", nil)
|
||||||
|
checkDenyKeyringWrite(t, authz, "foo", nil)
|
||||||
|
checkDenyKeyWrite(t, authz, "foo", nil)
|
||||||
|
checkDenyKeyWritePrefix(t, authz, "foo", nil)
|
||||||
|
checkDenyNodeRead(t, authz, "foo", nil)
|
||||||
|
checkDenyNodeWrite(t, authz, "foo", nil)
|
||||||
|
checkDenyOperatorRead(t, authz, "foo", nil)
|
||||||
|
checkDenyOperatorWrite(t, authz, "foo", nil)
|
||||||
|
checkDenyPreparedQueryRead(t, authz, "foo", nil)
|
||||||
|
checkDenyPreparedQueryWrite(t, authz, "foo", nil)
|
||||||
|
checkDenyServiceRead(t, authz, "foo", nil)
|
||||||
|
checkDenyServiceWrite(t, authz, "foo", nil)
|
||||||
|
checkDenySessionRead(t, authz, "foo", nil)
|
||||||
|
checkDenySessionWrite(t, authz, "foo", nil)
|
||||||
|
checkDenySnapshot(t, authz, "foo", nil)
|
||||||
|
|
||||||
|
authz = NewChainedAuthorizer([]Authorizer{testAuthorizer(Default), testAuthorizer(Allow)})
|
||||||
|
checkAllowACLRead(t, authz, "foo", nil)
|
||||||
|
checkAllowACLWrite(t, authz, "foo", nil)
|
||||||
|
checkAllowAgentRead(t, authz, "foo", nil)
|
||||||
|
checkAllowAgentWrite(t, authz, "foo", nil)
|
||||||
|
checkAllowEventRead(t, authz, "foo", nil)
|
||||||
|
checkAllowEventWrite(t, authz, "foo", nil)
|
||||||
|
checkAllowIntentionDefaultAllow(t, authz, "foo", nil)
|
||||||
|
checkAllowIntentionRead(t, authz, "foo", nil)
|
||||||
|
checkAllowIntentionWrite(t, authz, "foo", nil)
|
||||||
|
checkAllowKeyRead(t, authz, "foo", nil)
|
||||||
|
checkAllowKeyList(t, authz, "foo", nil)
|
||||||
|
checkAllowKeyringRead(t, authz, "foo", nil)
|
||||||
|
checkAllowKeyringWrite(t, authz, "foo", nil)
|
||||||
|
checkAllowKeyWrite(t, authz, "foo", nil)
|
||||||
|
checkAllowKeyWritePrefix(t, authz, "foo", nil)
|
||||||
|
checkAllowNodeRead(t, authz, "foo", nil)
|
||||||
|
checkAllowNodeWrite(t, authz, "foo", nil)
|
||||||
|
checkAllowOperatorRead(t, authz, "foo", nil)
|
||||||
|
checkAllowOperatorWrite(t, authz, "foo", nil)
|
||||||
|
checkAllowPreparedQueryRead(t, authz, "foo", nil)
|
||||||
|
checkAllowPreparedQueryWrite(t, authz, "foo", nil)
|
||||||
|
checkAllowServiceRead(t, authz, "foo", nil)
|
||||||
|
checkAllowServiceWrite(t, authz, "foo", nil)
|
||||||
|
checkAllowSessionRead(t, authz, "foo", nil)
|
||||||
|
checkAllowSessionWrite(t, authz, "foo", nil)
|
||||||
|
checkAllowSnapshot(t, authz, "foo", nil)
|
||||||
|
})
|
||||||
|
|
||||||
|
}
|
|
@ -1,72 +0,0 @@
|
||||||
package acl
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
// These error constants define the standard ACL error types. The values
|
|
||||||
// must not be changed since the error values are sent via RPC calls
|
|
||||||
// from older clients and may not have the correct type.
|
|
||||||
const (
|
|
||||||
errNotFound = "ACL not found"
|
|
||||||
errRootDenied = "Cannot resolve root ACL"
|
|
||||||
errDisabled = "ACL support disabled"
|
|
||||||
errPermissionDenied = "Permission denied"
|
|
||||||
errInvalidParent = "Invalid Parent"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
// ErrNotFound indicates there is no matching ACL.
|
|
||||||
ErrNotFound = errors.New(errNotFound)
|
|
||||||
|
|
||||||
// ErrRootDenied is returned when attempting to resolve a root ACL.
|
|
||||||
ErrRootDenied = errors.New(errRootDenied)
|
|
||||||
|
|
||||||
// ErrDisabled is returned when ACL changes are not permitted since
|
|
||||||
// they are disabled.
|
|
||||||
ErrDisabled = errors.New(errDisabled)
|
|
||||||
|
|
||||||
// ErrPermissionDenied is returned when an ACL based rejection
|
|
||||||
// happens.
|
|
||||||
ErrPermissionDenied = PermissionDeniedError{}
|
|
||||||
|
|
||||||
// ErrInvalidParent is returned when a remotely resolve ACL
|
|
||||||
// token claims to have a non-root parent
|
|
||||||
ErrInvalidParent = errors.New(errInvalidParent)
|
|
||||||
)
|
|
||||||
|
|
||||||
// IsErrNotFound checks if the given error message is comparable to
|
|
||||||
// ErrNotFound.
|
|
||||||
func IsErrNotFound(err error) bool {
|
|
||||||
return err != nil && strings.Contains(err.Error(), errNotFound)
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsErrRootDenied checks if the given error message is comparable to
|
|
||||||
// ErrRootDenied.
|
|
||||||
func IsErrRootDenied(err error) bool {
|
|
||||||
return err != nil && strings.Contains(err.Error(), errRootDenied)
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsErrDisabled checks if the given error message is comparable to
|
|
||||||
// ErrDisabled.
|
|
||||||
func IsErrDisabled(err error) bool {
|
|
||||||
return err != nil && strings.Contains(err.Error(), errDisabled)
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsErrPermissionDenied checks if the given error message is comparable
|
|
||||||
// to ErrPermissionDenied.
|
|
||||||
func IsErrPermissionDenied(err error) bool {
|
|
||||||
return err != nil && strings.Contains(err.Error(), errPermissionDenied)
|
|
||||||
}
|
|
||||||
|
|
||||||
type PermissionDeniedError struct {
|
|
||||||
Cause string
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e PermissionDeniedError) Error() string {
|
|
||||||
if e.Cause != "" {
|
|
||||||
return errPermissionDenied + ": " + e.Cause
|
|
||||||
}
|
|
||||||
return errPermissionDenied
|
|
||||||
}
|
|
835
acl/policy.go
835
acl/policy.go
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,637 @@
|
||||||
|
package acl
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/armon/go-radix"
|
||||||
|
)
|
||||||
|
|
||||||
|
type policyAuthorizer struct {
|
||||||
|
// aclRule contains the acl management policy.
|
||||||
|
aclRule *policyAuthorizerRule
|
||||||
|
|
||||||
|
// agentRules contain the exact-match agent policies
|
||||||
|
agentRules *radix.Tree
|
||||||
|
|
||||||
|
// intentionRules contains the service intention exact-match policies
|
||||||
|
intentionRules *radix.Tree
|
||||||
|
|
||||||
|
// keyRules contains the key exact-match policies
|
||||||
|
keyRules *radix.Tree
|
||||||
|
|
||||||
|
// nodeRules contains the node exact-match policies
|
||||||
|
nodeRules *radix.Tree
|
||||||
|
|
||||||
|
// serviceRules contains the service exact-match policies
|
||||||
|
serviceRules *radix.Tree
|
||||||
|
|
||||||
|
// sessionRules contains the session exact-match policies
|
||||||
|
sessionRules *radix.Tree
|
||||||
|
|
||||||
|
// eventRules contains the user event exact-match policies
|
||||||
|
eventRules *radix.Tree
|
||||||
|
|
||||||
|
// preparedQueryRules contains the prepared query exact-match policies
|
||||||
|
preparedQueryRules *radix.Tree
|
||||||
|
|
||||||
|
// keyringRule contains the keyring policies. The keyring has
|
||||||
|
// a very simple yes/no without prefix matching, so here we
|
||||||
|
// don't need to use a radix tree.
|
||||||
|
keyringRule *policyAuthorizerRule
|
||||||
|
|
||||||
|
// operatorRule contains the operator policies.
|
||||||
|
operatorRule *policyAuthorizerRule
|
||||||
|
|
||||||
|
// embedded enterprise policy authorizer
|
||||||
|
enterprisePolicyAuthorizer
|
||||||
|
}
|
||||||
|
|
||||||
|
// policyAuthorizerRule is a struct to hold an ACL policy decision along
|
||||||
|
// with extra Consul Enterprise specific policy
|
||||||
|
type policyAuthorizerRule struct {
|
||||||
|
// decision is the enforcement decision for this rule
|
||||||
|
access AccessLevel
|
||||||
|
|
||||||
|
// Embedded Consul Enterprise specific policy
|
||||||
|
EnterpriseRule
|
||||||
|
}
|
||||||
|
|
||||||
|
// policyAuthorizerRadixLeaf is used as the main
|
||||||
|
// structure for storing in the radix.Tree's within the
|
||||||
|
// PolicyAuthorizer
|
||||||
|
type policyAuthorizerRadixLeaf struct {
|
||||||
|
exact *policyAuthorizerRule
|
||||||
|
prefix *policyAuthorizerRule
|
||||||
|
}
|
||||||
|
|
||||||
|
// getPolicy first attempts to get an exact match for the segment from the "exact" tree and then falls
|
||||||
|
// back to getting the policy for the longest prefix from the "prefix" tree
|
||||||
|
func getPolicy(segment string, tree *radix.Tree) (policy *policyAuthorizerRule, found bool) {
|
||||||
|
found = false
|
||||||
|
|
||||||
|
tree.WalkPath(segment, func(path string, leaf interface{}) bool {
|
||||||
|
policies := leaf.(*policyAuthorizerRadixLeaf)
|
||||||
|
if policies.exact != nil && path == segment {
|
||||||
|
found = true
|
||||||
|
policy = policies.exact
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
if policies.prefix != nil {
|
||||||
|
found = true
|
||||||
|
policy = policies.prefix
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// insertPolicyIntoRadix will insert or update part of the leaf node within the radix tree corresponding to the
|
||||||
|
// given segment. To update only one of the exact match or prefix match policy, set the value you want to leave alone
|
||||||
|
// to nil when calling the function.
|
||||||
|
func insertPolicyIntoRadix(segment string, policy string, ent *EnterpriseRule, tree *radix.Tree, prefix bool) error {
|
||||||
|
al, err := AccessLevelFromString(policy)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
policyRule := policyAuthorizerRule{
|
||||||
|
access: al,
|
||||||
|
}
|
||||||
|
|
||||||
|
if ent != nil {
|
||||||
|
policyRule.EnterpriseRule = *ent
|
||||||
|
}
|
||||||
|
|
||||||
|
var policyLeaf *policyAuthorizerRadixLeaf
|
||||||
|
leaf, found := tree.Get(segment)
|
||||||
|
if found {
|
||||||
|
policyLeaf = leaf.(*policyAuthorizerRadixLeaf)
|
||||||
|
} else {
|
||||||
|
policyLeaf = &policyAuthorizerRadixLeaf{}
|
||||||
|
tree.Insert(segment, policyLeaf)
|
||||||
|
}
|
||||||
|
|
||||||
|
if prefix {
|
||||||
|
policyLeaf.prefix = &policyRule
|
||||||
|
} else {
|
||||||
|
policyLeaf.exact = &policyRule
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// enforce is a convenience function to
|
||||||
|
func enforce(rule AccessLevel, requiredPermission AccessLevel) EnforcementDecision {
|
||||||
|
switch rule {
|
||||||
|
case AccessWrite:
|
||||||
|
// grants read, list and write permissions
|
||||||
|
return Allow
|
||||||
|
case AccessList:
|
||||||
|
// grants read and list permissions
|
||||||
|
if requiredPermission == AccessList || requiredPermission == AccessRead {
|
||||||
|
return Allow
|
||||||
|
} else {
|
||||||
|
return Deny
|
||||||
|
}
|
||||||
|
case AccessRead:
|
||||||
|
// grants just read permissions
|
||||||
|
if requiredPermission == AccessRead {
|
||||||
|
return Allow
|
||||||
|
} else {
|
||||||
|
return Deny
|
||||||
|
}
|
||||||
|
case AccessDeny:
|
||||||
|
// explicit denial - do not recurse
|
||||||
|
return Deny
|
||||||
|
default:
|
||||||
|
// need to recurse as there was no specific access level set
|
||||||
|
return Default
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func defaultIsAllow(decision EnforcementDecision) EnforcementDecision {
|
||||||
|
switch decision {
|
||||||
|
case Allow, Default:
|
||||||
|
return Allow
|
||||||
|
default:
|
||||||
|
return Deny
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *policyAuthorizer) loadRules(policy *PolicyRules) error {
|
||||||
|
// Load the agent policy (exact matches)
|
||||||
|
for _, ap := range policy.Agents {
|
||||||
|
if err := insertPolicyIntoRadix(ap.Node, ap.Policy, nil, p.agentRules, false); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load the agent policy (prefix matches)
|
||||||
|
for _, ap := range policy.AgentPrefixes {
|
||||||
|
if err := insertPolicyIntoRadix(ap.Node, ap.Policy, nil, p.agentRules, true); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load the key policy (exact matches)
|
||||||
|
for _, kp := range policy.Keys {
|
||||||
|
if err := insertPolicyIntoRadix(kp.Prefix, kp.Policy, &kp.EnterpriseRule, p.keyRules, false); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load the key policy (prefix matches)
|
||||||
|
for _, kp := range policy.KeyPrefixes {
|
||||||
|
if err := insertPolicyIntoRadix(kp.Prefix, kp.Policy, &kp.EnterpriseRule, p.keyRules, true); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load the node policy (exact matches)
|
||||||
|
for _, np := range policy.Nodes {
|
||||||
|
if err := insertPolicyIntoRadix(np.Name, np.Policy, &np.EnterpriseRule, p.nodeRules, false); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load the node policy (prefix matches)
|
||||||
|
for _, np := range policy.NodePrefixes {
|
||||||
|
if err := insertPolicyIntoRadix(np.Name, np.Policy, &np.EnterpriseRule, p.nodeRules, true); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load the service policy (exact matches)
|
||||||
|
for _, sp := range policy.Services {
|
||||||
|
if err := insertPolicyIntoRadix(sp.Name, sp.Policy, &sp.EnterpriseRule, p.serviceRules, false); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
intention := sp.Intentions
|
||||||
|
if intention == "" {
|
||||||
|
switch sp.Policy {
|
||||||
|
case PolicyRead, PolicyWrite:
|
||||||
|
intention = PolicyRead
|
||||||
|
default:
|
||||||
|
intention = PolicyDeny
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := insertPolicyIntoRadix(sp.Name, intention, &sp.EnterpriseRule, p.intentionRules, false); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load the service policy (prefix matches)
|
||||||
|
for _, sp := range policy.ServicePrefixes {
|
||||||
|
if err := insertPolicyIntoRadix(sp.Name, sp.Policy, &sp.EnterpriseRule, p.serviceRules, true); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
intention := sp.Intentions
|
||||||
|
if intention == "" {
|
||||||
|
switch sp.Policy {
|
||||||
|
case PolicyRead, PolicyWrite:
|
||||||
|
intention = PolicyRead
|
||||||
|
default:
|
||||||
|
intention = PolicyDeny
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := insertPolicyIntoRadix(sp.Name, intention, &sp.EnterpriseRule, p.intentionRules, true); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load the session policy (exact matches)
|
||||||
|
for _, sp := range policy.Sessions {
|
||||||
|
if err := insertPolicyIntoRadix(sp.Node, sp.Policy, nil, p.sessionRules, false); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load the session policy (prefix matches)
|
||||||
|
for _, sp := range policy.SessionPrefixes {
|
||||||
|
if err := insertPolicyIntoRadix(sp.Node, sp.Policy, nil, p.sessionRules, true); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load the event policy (exact matches)
|
||||||
|
for _, ep := range policy.Events {
|
||||||
|
if err := insertPolicyIntoRadix(ep.Event, ep.Policy, nil, p.eventRules, false); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load the event policy (prefix matches)
|
||||||
|
for _, ep := range policy.EventPrefixes {
|
||||||
|
if err := insertPolicyIntoRadix(ep.Event, ep.Policy, nil, p.eventRules, true); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load the prepared query policy (exact matches)
|
||||||
|
for _, qp := range policy.PreparedQueries {
|
||||||
|
if err := insertPolicyIntoRadix(qp.Prefix, qp.Policy, nil, p.preparedQueryRules, false); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load the prepared query policy (prefix matches)
|
||||||
|
for _, qp := range policy.PreparedQueryPrefixes {
|
||||||
|
if err := insertPolicyIntoRadix(qp.Prefix, qp.Policy, nil, p.preparedQueryRules, true); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load the acl policy
|
||||||
|
if policy.ACL != "" {
|
||||||
|
access, err := AccessLevelFromString(policy.ACL)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
p.aclRule = &policyAuthorizerRule{access: access}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load the keyring policy
|
||||||
|
if policy.Keyring != "" {
|
||||||
|
access, err := AccessLevelFromString(policy.Keyring)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
p.keyringRule = &policyAuthorizerRule{access: access}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load the operator policy
|
||||||
|
if policy.Operator != "" {
|
||||||
|
access, err := AccessLevelFromString(policy.Operator)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
p.operatorRule = &policyAuthorizerRule{access: access}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func newPolicyAuthorizer(policies []*Policy, ent *EnterpriseACLConfig) (Authorizer, error) {
|
||||||
|
policy := MergePolicies(policies)
|
||||||
|
|
||||||
|
return newPolicyAuthorizerFromRules(&policy.PolicyRules, ent)
|
||||||
|
}
|
||||||
|
|
||||||
|
func newPolicyAuthorizerFromRules(rules *PolicyRules, ent *EnterpriseACLConfig) (Authorizer, error) {
|
||||||
|
p := &policyAuthorizer{
|
||||||
|
agentRules: radix.New(),
|
||||||
|
intentionRules: radix.New(),
|
||||||
|
keyRules: radix.New(),
|
||||||
|
nodeRules: radix.New(),
|
||||||
|
serviceRules: radix.New(),
|
||||||
|
sessionRules: radix.New(),
|
||||||
|
eventRules: radix.New(),
|
||||||
|
preparedQueryRules: radix.New(),
|
||||||
|
}
|
||||||
|
|
||||||
|
p.enterprisePolicyAuthorizer.init(ent)
|
||||||
|
|
||||||
|
if err := p.loadRules(rules); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return p, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ACLRead checks if listing of ACLs is allowed
|
||||||
|
func (p *policyAuthorizer) ACLRead(*EnterpriseAuthorizerContext) EnforcementDecision {
|
||||||
|
if p.aclRule != nil {
|
||||||
|
return enforce(p.aclRule.access, AccessRead)
|
||||||
|
}
|
||||||
|
return Default
|
||||||
|
}
|
||||||
|
|
||||||
|
// ACLWrite checks if modification of ACLs is allowed
|
||||||
|
func (p *policyAuthorizer) ACLWrite(*EnterpriseAuthorizerContext) EnforcementDecision {
|
||||||
|
if p.aclRule != nil {
|
||||||
|
return enforce(p.aclRule.access, AccessWrite)
|
||||||
|
}
|
||||||
|
return Default
|
||||||
|
}
|
||||||
|
|
||||||
|
// AgentRead checks for permission to read from agent endpoints for a given
|
||||||
|
// node.
|
||||||
|
func (p *policyAuthorizer) AgentRead(node string, _ *EnterpriseAuthorizerContext) EnforcementDecision {
|
||||||
|
if rule, ok := getPolicy(node, p.agentRules); ok {
|
||||||
|
return enforce(rule.access, AccessRead)
|
||||||
|
}
|
||||||
|
return Default
|
||||||
|
}
|
||||||
|
|
||||||
|
// AgentWrite checks for permission to make changes via agent endpoints for a
|
||||||
|
// given node.
|
||||||
|
func (p *policyAuthorizer) AgentWrite(node string, _ *EnterpriseAuthorizerContext) EnforcementDecision {
|
||||||
|
if rule, ok := getPolicy(node, p.agentRules); ok {
|
||||||
|
return enforce(rule.access, AccessWrite)
|
||||||
|
}
|
||||||
|
return Default
|
||||||
|
}
|
||||||
|
|
||||||
|
// Snapshot checks if taking and restoring snapshots is allowed.
|
||||||
|
func (p *policyAuthorizer) Snapshot(_ *EnterpriseAuthorizerContext) EnforcementDecision {
|
||||||
|
if p.aclRule != nil {
|
||||||
|
return enforce(p.aclRule.access, AccessWrite)
|
||||||
|
}
|
||||||
|
return Default
|
||||||
|
}
|
||||||
|
|
||||||
|
// EventRead is used to determine if the policy allows for a
|
||||||
|
// specific user event to be read.
|
||||||
|
func (p *policyAuthorizer) EventRead(name string, _ *EnterpriseAuthorizerContext) EnforcementDecision {
|
||||||
|
if rule, ok := getPolicy(name, p.eventRules); ok {
|
||||||
|
return enforce(rule.access, AccessRead)
|
||||||
|
}
|
||||||
|
return Default
|
||||||
|
}
|
||||||
|
|
||||||
|
// EventWrite is used to determine if new events can be created
|
||||||
|
// (fired) by the policy.
|
||||||
|
func (p *policyAuthorizer) EventWrite(name string, _ *EnterpriseAuthorizerContext) EnforcementDecision {
|
||||||
|
if rule, ok := getPolicy(name, p.eventRules); ok {
|
||||||
|
return enforce(rule.access, AccessWrite)
|
||||||
|
}
|
||||||
|
return Default
|
||||||
|
}
|
||||||
|
|
||||||
|
// IntentionDefaultAllow returns whether the default behavior when there are
|
||||||
|
// no matching intentions is to allow or deny.
|
||||||
|
func (p *policyAuthorizer) IntentionDefaultAllow(_ *EnterpriseAuthorizerContext) EnforcementDecision {
|
||||||
|
// We always go up, this can't be determined by a policy.
|
||||||
|
return Default
|
||||||
|
}
|
||||||
|
|
||||||
|
// IntentionRead checks if writing (creating, updating, or deleting) of an
|
||||||
|
// intention is allowed.
|
||||||
|
func (p *policyAuthorizer) IntentionRead(prefix string, _ *EnterpriseAuthorizerContext) EnforcementDecision {
|
||||||
|
if rule, ok := getPolicy(prefix, p.intentionRules); ok {
|
||||||
|
return enforce(rule.access, AccessRead)
|
||||||
|
}
|
||||||
|
return Default
|
||||||
|
}
|
||||||
|
|
||||||
|
// IntentionWrite checks if writing (creating, updating, or deleting) of an
|
||||||
|
// intention is allowed.
|
||||||
|
func (p *policyAuthorizer) IntentionWrite(prefix string, _ *EnterpriseAuthorizerContext) EnforcementDecision {
|
||||||
|
if rule, ok := getPolicy(prefix, p.intentionRules); ok {
|
||||||
|
return enforce(rule.access, AccessWrite)
|
||||||
|
}
|
||||||
|
return Default
|
||||||
|
}
|
||||||
|
|
||||||
|
// KeyRead returns if a key is allowed to be read
|
||||||
|
func (p *policyAuthorizer) KeyRead(key string, _ *EnterpriseAuthorizerContext) EnforcementDecision {
|
||||||
|
if rule, ok := getPolicy(key, p.keyRules); ok {
|
||||||
|
return enforce(rule.access, AccessRead)
|
||||||
|
}
|
||||||
|
return Default
|
||||||
|
}
|
||||||
|
|
||||||
|
// KeyList returns if a key is allowed to be listed
|
||||||
|
func (p *policyAuthorizer) KeyList(key string, _ *EnterpriseAuthorizerContext) EnforcementDecision {
|
||||||
|
if rule, ok := getPolicy(key, p.keyRules); ok {
|
||||||
|
return enforce(rule.access, AccessList)
|
||||||
|
}
|
||||||
|
return Default
|
||||||
|
}
|
||||||
|
|
||||||
|
// KeyWrite returns if a key is allowed to be written
|
||||||
|
func (p *policyAuthorizer) KeyWrite(key string, entCtx *EnterpriseAuthorizerContext) EnforcementDecision {
|
||||||
|
if rule, ok := getPolicy(key, p.keyRules); ok {
|
||||||
|
decision := enforce(rule.access, AccessWrite)
|
||||||
|
if decision == Allow {
|
||||||
|
return defaultIsAllow(p.enterprisePolicyAuthorizer.enforce(&rule.EnterpriseRule, entCtx))
|
||||||
|
}
|
||||||
|
return decision
|
||||||
|
}
|
||||||
|
return Default
|
||||||
|
}
|
||||||
|
|
||||||
|
// KeyWritePrefix returns if a prefix is allowed to be written
|
||||||
|
//
|
||||||
|
// This is mainly used to detect whether a whole tree within
|
||||||
|
// the KV can be removed. For that reason we must be able to
|
||||||
|
// delete everything under the prefix. First we must have "write"
|
||||||
|
// on the prefix itself
|
||||||
|
func (p *policyAuthorizer) KeyWritePrefix(prefix string, _ *EnterpriseAuthorizerContext) EnforcementDecision {
|
||||||
|
// Conditions for Allow:
|
||||||
|
// * The longest prefix match rule that would apply to the given prefix
|
||||||
|
// grants AccessWrite
|
||||||
|
// AND
|
||||||
|
// * There are no rules (exact or prefix match) within/under the given prefix
|
||||||
|
// that would NOT grant AccessWrite.
|
||||||
|
//
|
||||||
|
// Conditions for Deny:
|
||||||
|
// * The longest prefix match rule that would apply to the given prefix
|
||||||
|
// does not grant AccessWrite.
|
||||||
|
// OR
|
||||||
|
// * There is 1+ rules (exact or prefix match) within/under the given prefix
|
||||||
|
// that do NOT grant AccessWrite.
|
||||||
|
//
|
||||||
|
// Conditions for Default:
|
||||||
|
// * There is no prefix match rule that would appy to the given prefix.
|
||||||
|
// AND
|
||||||
|
// * There are no rules (exact or prefix match) within/under the given prefix
|
||||||
|
// that would NOT grant AccessWrite.
|
||||||
|
|
||||||
|
baseAccess := Default
|
||||||
|
|
||||||
|
// Look for a prefix rule that would apply to the prefix we are checking
|
||||||
|
// WalkPath starts at the root and walks down to the given prefix.
|
||||||
|
// Therefore the last prefix rule we see is the one that matters
|
||||||
|
p.keyRules.WalkPath(prefix, func(path string, leaf interface{}) bool {
|
||||||
|
rule := leaf.(*policyAuthorizerRadixLeaf)
|
||||||
|
|
||||||
|
if rule.prefix != nil {
|
||||||
|
if rule.prefix.access != AccessWrite {
|
||||||
|
baseAccess = Deny
|
||||||
|
} else {
|
||||||
|
baseAccess = Allow
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
})
|
||||||
|
|
||||||
|
// baseAccess will be Deny only when a prefix rule was found and it didn't
|
||||||
|
// grant AccessWrite. Otherwise the access level will be Default or Allow
|
||||||
|
// neither of which should be returned right now.
|
||||||
|
if baseAccess == Deny {
|
||||||
|
return baseAccess
|
||||||
|
}
|
||||||
|
|
||||||
|
// Look if any of our children do not allow write access. This loop takes
|
||||||
|
// into account both prefix and exact match rules.
|
||||||
|
withinPrefixAccess := Default
|
||||||
|
p.keyRules.WalkPrefix(prefix, func(path string, leaf interface{}) bool {
|
||||||
|
rule := leaf.(*policyAuthorizerRadixLeaf)
|
||||||
|
|
||||||
|
if rule.prefix != nil && rule.prefix.access != AccessWrite {
|
||||||
|
withinPrefixAccess = Deny
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if rule.exact != nil && rule.exact.access != AccessWrite {
|
||||||
|
withinPrefixAccess = Deny
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
})
|
||||||
|
|
||||||
|
// Deny the write if any sub-rules may be violated. If none are violated then
|
||||||
|
// we can defer to the baseAccess.
|
||||||
|
if withinPrefixAccess == Deny {
|
||||||
|
return Deny
|
||||||
|
}
|
||||||
|
|
||||||
|
// either Default or Allow at this point. Allow if there was a prefix rule
|
||||||
|
// that was applicable and it granted write access. Default if there was
|
||||||
|
// no applicable rule.
|
||||||
|
return baseAccess
|
||||||
|
}
|
||||||
|
|
||||||
|
// KeyringRead is used to determine if the keyring can be
|
||||||
|
// read by the current ACL token.
|
||||||
|
func (p *policyAuthorizer) KeyringRead(*EnterpriseAuthorizerContext) EnforcementDecision {
|
||||||
|
if p.keyringRule != nil {
|
||||||
|
return enforce(p.keyringRule.access, AccessRead)
|
||||||
|
}
|
||||||
|
return Default
|
||||||
|
}
|
||||||
|
|
||||||
|
// KeyringWrite determines if the keyring can be manipulated.
|
||||||
|
func (p *policyAuthorizer) KeyringWrite(*EnterpriseAuthorizerContext) EnforcementDecision {
|
||||||
|
if p.keyringRule != nil {
|
||||||
|
return enforce(p.keyringRule.access, AccessWrite)
|
||||||
|
}
|
||||||
|
return Default
|
||||||
|
}
|
||||||
|
|
||||||
|
// OperatorRead determines if the read-only operator functions are allowed.
|
||||||
|
func (p *policyAuthorizer) OperatorRead(*EnterpriseAuthorizerContext) EnforcementDecision {
|
||||||
|
if p.operatorRule != nil {
|
||||||
|
return enforce(p.operatorRule.access, AccessRead)
|
||||||
|
}
|
||||||
|
return Default
|
||||||
|
}
|
||||||
|
|
||||||
|
// OperatorWrite determines if the state-changing operator functions are
|
||||||
|
// allowed.
|
||||||
|
func (p *policyAuthorizer) OperatorWrite(*EnterpriseAuthorizerContext) EnforcementDecision {
|
||||||
|
if p.operatorRule != nil {
|
||||||
|
return enforce(p.operatorRule.access, AccessWrite)
|
||||||
|
}
|
||||||
|
return Default
|
||||||
|
}
|
||||||
|
|
||||||
|
// NodeRead checks if reading (discovery) of a node is allowed
|
||||||
|
func (p *policyAuthorizer) NodeRead(name string, _ *EnterpriseAuthorizerContext) EnforcementDecision {
|
||||||
|
if rule, ok := getPolicy(name, p.nodeRules); ok {
|
||||||
|
return enforce(rule.access, AccessRead)
|
||||||
|
}
|
||||||
|
return Default
|
||||||
|
}
|
||||||
|
|
||||||
|
// NodeWrite checks if writing (registering) a node is allowed
|
||||||
|
func (p *policyAuthorizer) NodeWrite(name string, _ *EnterpriseAuthorizerContext) EnforcementDecision {
|
||||||
|
if rule, ok := getPolicy(name, p.nodeRules); ok {
|
||||||
|
return enforce(rule.access, AccessWrite)
|
||||||
|
}
|
||||||
|
return Default
|
||||||
|
}
|
||||||
|
|
||||||
|
// PreparedQueryRead checks if reading (listing) of a prepared query is
|
||||||
|
// allowed - this isn't execution, just listing its contents.
|
||||||
|
func (p *policyAuthorizer) PreparedQueryRead(prefix string, _ *EnterpriseAuthorizerContext) EnforcementDecision {
|
||||||
|
if rule, ok := getPolicy(prefix, p.preparedQueryRules); ok {
|
||||||
|
return enforce(rule.access, AccessRead)
|
||||||
|
}
|
||||||
|
return Default
|
||||||
|
}
|
||||||
|
|
||||||
|
// PreparedQueryWrite checks if writing (creating, updating, or deleting) of a
|
||||||
|
// prepared query is allowed.
|
||||||
|
func (p *policyAuthorizer) PreparedQueryWrite(prefix string, _ *EnterpriseAuthorizerContext) EnforcementDecision {
|
||||||
|
if rule, ok := getPolicy(prefix, p.preparedQueryRules); ok {
|
||||||
|
return enforce(rule.access, AccessWrite)
|
||||||
|
}
|
||||||
|
return Default
|
||||||
|
}
|
||||||
|
|
||||||
|
// ServiceRead checks if reading (discovery) of a service is allowed
|
||||||
|
func (p *policyAuthorizer) ServiceRead(name string, _ *EnterpriseAuthorizerContext) EnforcementDecision {
|
||||||
|
if rule, ok := getPolicy(name, p.serviceRules); ok {
|
||||||
|
return enforce(rule.access, AccessRead)
|
||||||
|
}
|
||||||
|
return Default
|
||||||
|
}
|
||||||
|
|
||||||
|
// ServiceWrite checks if writing (registering) a service is allowed
|
||||||
|
func (p *policyAuthorizer) ServiceWrite(name string, _ *EnterpriseAuthorizerContext) EnforcementDecision {
|
||||||
|
if rule, ok := getPolicy(name, p.serviceRules); ok {
|
||||||
|
return enforce(rule.access, AccessWrite)
|
||||||
|
}
|
||||||
|
return Default
|
||||||
|
}
|
||||||
|
|
||||||
|
// SessionRead checks for permission to read sessions for a given node.
|
||||||
|
func (p *policyAuthorizer) SessionRead(node string, _ *EnterpriseAuthorizerContext) EnforcementDecision {
|
||||||
|
if rule, ok := getPolicy(node, p.sessionRules); ok {
|
||||||
|
return enforce(rule.access, AccessRead)
|
||||||
|
}
|
||||||
|
return Default
|
||||||
|
}
|
||||||
|
|
||||||
|
// SessionWrite checks for permission to create sessions for a given node.
|
||||||
|
func (p *policyAuthorizer) SessionWrite(node string, _ *EnterpriseAuthorizerContext) EnforcementDecision {
|
||||||
|
// Check for an exact rule or catch-all
|
||||||
|
if rule, ok := getPolicy(node, p.sessionRules); ok {
|
||||||
|
return enforce(rule.access, AccessWrite)
|
||||||
|
}
|
||||||
|
return Default
|
||||||
|
}
|
|
@ -0,0 +1,30 @@
|
||||||
|
// +build !consulent
|
||||||
|
|
||||||
|
package acl
|
||||||
|
|
||||||
|
// enterprisePolicyAuthorizer stub
|
||||||
|
type enterprisePolicyAuthorizer struct{}
|
||||||
|
|
||||||
|
func (authz *enterprisePolicyAuthorizer) init(*EnterpriseACLConfig) {
|
||||||
|
// nothing to do
|
||||||
|
}
|
||||||
|
|
||||||
|
func (authz *enterprisePolicyAuthorizer) enforce(_ *EnterpriseRule, _ *EnterpriseAuthorizerContext) EnforcementDecision {
|
||||||
|
return Default
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewPolicyAuthorizer merges the policies and returns an Authorizer that will enforce them
|
||||||
|
func NewPolicyAuthorizer(policies []*Policy, entConfig *EnterpriseACLConfig) (Authorizer, error) {
|
||||||
|
return newPolicyAuthorizer(policies, entConfig)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewPolicyAuthorizerWithDefaults will actually created a ChainedAuthorizer with
|
||||||
|
// the policies compiled into one Authorizer and the backup policy of the defaultAuthz
|
||||||
|
func NewPolicyAuthorizerWithDefaults(defaultAuthz Authorizer, policies []*Policy, entConfig *EnterpriseACLConfig) (Authorizer, error) {
|
||||||
|
authz, err := newPolicyAuthorizer(policies, entConfig)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return NewChainedAuthorizer([]Authorizer{authz, defaultAuthz}), nil
|
||||||
|
}
|
|
@ -0,0 +1,371 @@
|
||||||
|
package acl
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Note that many of the policy authorizer tests still live in acl_test.go. These utilize a default policy or layer
|
||||||
|
// up multiple authorizers just like before the latest overhaul of the ACL package. To reduce the code diff and to
|
||||||
|
// ensure compatibility from version to version those tests have been only minimally altered. The tests in this
|
||||||
|
// file are specific to the newer functionality.
|
||||||
|
func TestPolicyAuthorizer(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
type aclCheck struct {
|
||||||
|
name string
|
||||||
|
prefix string
|
||||||
|
check func(t *testing.T, authz Authorizer, prefix string, entCtx *EnterpriseAuthorizerContext)
|
||||||
|
}
|
||||||
|
|
||||||
|
type aclTest struct {
|
||||||
|
policy *Policy
|
||||||
|
checks []aclCheck
|
||||||
|
}
|
||||||
|
|
||||||
|
cases := map[string]aclTest{
|
||||||
|
// This test ensures that if the policy doesn't define a rule then the policy authorizer will
|
||||||
|
// return no concrete enforcement decision. This allows deferring to some defaults in another
|
||||||
|
// authorizer including usage of a default overall policy of "deny"
|
||||||
|
"Defaults": aclTest{
|
||||||
|
policy: &Policy{},
|
||||||
|
checks: []aclCheck{
|
||||||
|
{name: "DefaultACLRead", prefix: "foo", check: checkDefaultACLRead},
|
||||||
|
{name: "DefaultACLWrite", prefix: "foo", check: checkDefaultACLWrite},
|
||||||
|
{name: "DefaultAgentRead", prefix: "foo", check: checkDefaultAgentRead},
|
||||||
|
{name: "DefaultAgentWrite", prefix: "foo", check: checkDefaultAgentWrite},
|
||||||
|
{name: "DefaultEventRead", prefix: "foo", check: checkDefaultEventRead},
|
||||||
|
{name: "DefaultEventWrite", prefix: "foo", check: checkDefaultEventWrite},
|
||||||
|
{name: "DefaultIntentionDefaultAllow", prefix: "foo", check: checkDefaultIntentionDefaultAllow},
|
||||||
|
{name: "DefaultIntentionRead", prefix: "foo", check: checkDefaultIntentionRead},
|
||||||
|
{name: "DefaultIntentionWrite", prefix: "foo", check: checkDefaultIntentionWrite},
|
||||||
|
{name: "DefaultKeyRead", prefix: "foo", check: checkDefaultKeyRead},
|
||||||
|
{name: "DefaultKeyList", prefix: "foo", check: checkDefaultKeyList},
|
||||||
|
{name: "DefaultKeyringRead", prefix: "foo", check: checkDefaultKeyringRead},
|
||||||
|
{name: "DefaultKeyringWrite", prefix: "foo", check: checkDefaultKeyringWrite},
|
||||||
|
{name: "DefaultKeyWrite", prefix: "foo", check: checkDefaultKeyWrite},
|
||||||
|
{name: "DefaultKeyWritePrefix", prefix: "foo", check: checkDefaultKeyWritePrefix},
|
||||||
|
{name: "DefaultNodeRead", prefix: "foo", check: checkDefaultNodeRead},
|
||||||
|
{name: "DefaultNodeWrite", prefix: "foo", check: checkDefaultNodeWrite},
|
||||||
|
{name: "DefaultOperatorRead", prefix: "foo", check: checkDefaultOperatorRead},
|
||||||
|
{name: "DefaultOperatorWrite", prefix: "foo", check: checkDefaultOperatorWrite},
|
||||||
|
{name: "DefaultPreparedQueryRead", prefix: "foo", check: checkDefaultPreparedQueryRead},
|
||||||
|
{name: "DefaultPreparedQueryWrite", prefix: "foo", check: checkDefaultPreparedQueryWrite},
|
||||||
|
{name: "DefaultServiceRead", prefix: "foo", check: checkDefaultServiceRead},
|
||||||
|
{name: "DefaultServiceWrite", prefix: "foo", check: checkDefaultServiceWrite},
|
||||||
|
{name: "DefaultSessionRead", prefix: "foo", check: checkDefaultSessionRead},
|
||||||
|
{name: "DefaultSessionWrite", prefix: "foo", check: checkDefaultSessionWrite},
|
||||||
|
{name: "DefaultSnapshot", prefix: "foo", check: checkDefaultSnapshot},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"Prefer Exact Matches": aclTest{
|
||||||
|
policy: &Policy{PolicyRules: PolicyRules{
|
||||||
|
Agents: []*AgentRule{
|
||||||
|
&AgentRule{
|
||||||
|
Node: "foo",
|
||||||
|
Policy: PolicyWrite,
|
||||||
|
},
|
||||||
|
&AgentRule{
|
||||||
|
Node: "football",
|
||||||
|
Policy: PolicyDeny,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
AgentPrefixes: []*AgentRule{
|
||||||
|
&AgentRule{
|
||||||
|
Node: "foot",
|
||||||
|
Policy: PolicyRead,
|
||||||
|
},
|
||||||
|
&AgentRule{
|
||||||
|
Node: "fo",
|
||||||
|
Policy: PolicyRead,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Keys: []*KeyRule{
|
||||||
|
&KeyRule{
|
||||||
|
Prefix: "foo",
|
||||||
|
Policy: PolicyWrite,
|
||||||
|
},
|
||||||
|
&KeyRule{
|
||||||
|
Prefix: "football",
|
||||||
|
Policy: PolicyDeny,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
KeyPrefixes: []*KeyRule{
|
||||||
|
&KeyRule{
|
||||||
|
Prefix: "foot",
|
||||||
|
Policy: PolicyRead,
|
||||||
|
},
|
||||||
|
&KeyRule{
|
||||||
|
Prefix: "fo",
|
||||||
|
Policy: PolicyRead,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Nodes: []*NodeRule{
|
||||||
|
&NodeRule{
|
||||||
|
Name: "foo",
|
||||||
|
Policy: PolicyWrite,
|
||||||
|
},
|
||||||
|
&NodeRule{
|
||||||
|
Name: "football",
|
||||||
|
Policy: PolicyDeny,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
NodePrefixes: []*NodeRule{
|
||||||
|
&NodeRule{
|
||||||
|
Name: "foot",
|
||||||
|
Policy: PolicyRead,
|
||||||
|
},
|
||||||
|
&NodeRule{
|
||||||
|
Name: "fo",
|
||||||
|
Policy: PolicyRead,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Services: []*ServiceRule{
|
||||||
|
&ServiceRule{
|
||||||
|
Name: "foo",
|
||||||
|
Policy: PolicyWrite,
|
||||||
|
Intentions: PolicyWrite,
|
||||||
|
},
|
||||||
|
&ServiceRule{
|
||||||
|
Name: "football",
|
||||||
|
Policy: PolicyDeny,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
ServicePrefixes: []*ServiceRule{
|
||||||
|
&ServiceRule{
|
||||||
|
Name: "foot",
|
||||||
|
Policy: PolicyRead,
|
||||||
|
Intentions: PolicyRead,
|
||||||
|
},
|
||||||
|
&ServiceRule{
|
||||||
|
Name: "fo",
|
||||||
|
Policy: PolicyRead,
|
||||||
|
Intentions: PolicyRead,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Sessions: []*SessionRule{
|
||||||
|
&SessionRule{
|
||||||
|
Node: "foo",
|
||||||
|
Policy: PolicyWrite,
|
||||||
|
},
|
||||||
|
&SessionRule{
|
||||||
|
Node: "football",
|
||||||
|
Policy: PolicyDeny,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
SessionPrefixes: []*SessionRule{
|
||||||
|
&SessionRule{
|
||||||
|
Node: "foot",
|
||||||
|
Policy: PolicyRead,
|
||||||
|
},
|
||||||
|
&SessionRule{
|
||||||
|
Node: "fo",
|
||||||
|
Policy: PolicyRead,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Events: []*EventRule{
|
||||||
|
&EventRule{
|
||||||
|
Event: "foo",
|
||||||
|
Policy: PolicyWrite,
|
||||||
|
},
|
||||||
|
&EventRule{
|
||||||
|
Event: "football",
|
||||||
|
Policy: PolicyDeny,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
EventPrefixes: []*EventRule{
|
||||||
|
&EventRule{
|
||||||
|
Event: "foot",
|
||||||
|
Policy: PolicyRead,
|
||||||
|
},
|
||||||
|
&EventRule{
|
||||||
|
Event: "fo",
|
||||||
|
Policy: PolicyRead,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
PreparedQueries: []*PreparedQueryRule{
|
||||||
|
&PreparedQueryRule{
|
||||||
|
Prefix: "foo",
|
||||||
|
Policy: PolicyWrite,
|
||||||
|
},
|
||||||
|
&PreparedQueryRule{
|
||||||
|
Prefix: "football",
|
||||||
|
Policy: PolicyDeny,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
PreparedQueryPrefixes: []*PreparedQueryRule{
|
||||||
|
&PreparedQueryRule{
|
||||||
|
Prefix: "foot",
|
||||||
|
Policy: PolicyRead,
|
||||||
|
},
|
||||||
|
&PreparedQueryRule{
|
||||||
|
Prefix: "fo",
|
||||||
|
Policy: PolicyRead,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}},
|
||||||
|
checks: []aclCheck{
|
||||||
|
{name: "AgentReadPrefixAllowed", prefix: "fo", check: checkAllowAgentRead},
|
||||||
|
{name: "AgentWritePrefixDenied", prefix: "fo", check: checkDenyAgentWrite},
|
||||||
|
{name: "AgentReadPrefixAllowed", prefix: "for", check: checkAllowAgentRead},
|
||||||
|
{name: "AgentWritePrefixDenied", prefix: "for", check: checkDenyAgentWrite},
|
||||||
|
{name: "AgentReadAllowed", prefix: "foo", check: checkAllowAgentRead},
|
||||||
|
{name: "AgentWriteAllowed", prefix: "foo", check: checkAllowAgentWrite},
|
||||||
|
{name: "AgentReadPrefixAllowed", prefix: "foot", check: checkAllowAgentRead},
|
||||||
|
{name: "AgentWritePrefixDenied", prefix: "foot", check: checkDenyAgentWrite},
|
||||||
|
{name: "AgentReadPrefixAllowed", prefix: "foot2", check: checkAllowAgentRead},
|
||||||
|
{name: "AgentWritePrefixDenied", prefix: "foot2", check: checkDenyAgentWrite},
|
||||||
|
{name: "AgentReadPrefixAllowed", prefix: "food", check: checkAllowAgentRead},
|
||||||
|
{name: "AgentWritePrefixDenied", prefix: "food", check: checkDenyAgentWrite},
|
||||||
|
{name: "AgentReadDenied", prefix: "football", check: checkDenyAgentRead},
|
||||||
|
{name: "AgentWriteDenied", prefix: "football", check: checkDenyAgentWrite},
|
||||||
|
|
||||||
|
{name: "KeyReadPrefixAllowed", prefix: "fo", check: checkAllowKeyRead},
|
||||||
|
{name: "KeyWritePrefixDenied", prefix: "fo", check: checkDenyKeyWrite},
|
||||||
|
{name: "KeyReadPrefixAllowed", prefix: "for", check: checkAllowKeyRead},
|
||||||
|
{name: "KeyWritePrefixDenied", prefix: "for", check: checkDenyKeyWrite},
|
||||||
|
{name: "KeyReadAllowed", prefix: "foo", check: checkAllowKeyRead},
|
||||||
|
{name: "KeyWriteAllowed", prefix: "foo", check: checkAllowKeyWrite},
|
||||||
|
{name: "KeyReadPrefixAllowed", prefix: "foot", check: checkAllowKeyRead},
|
||||||
|
{name: "KeyWritePrefixDenied", prefix: "foot", check: checkDenyKeyWrite},
|
||||||
|
{name: "KeyReadPrefixAllowed", prefix: "foot2", check: checkAllowKeyRead},
|
||||||
|
{name: "KeyWritePrefixDenied", prefix: "foot2", check: checkDenyKeyWrite},
|
||||||
|
{name: "KeyReadPrefixAllowed", prefix: "food", check: checkAllowKeyRead},
|
||||||
|
{name: "KeyWritePrefixDenied", prefix: "food", check: checkDenyKeyWrite},
|
||||||
|
{name: "KeyReadDenied", prefix: "football", check: checkDenyKeyRead},
|
||||||
|
{name: "KeyWriteDenied", prefix: "football", check: checkDenyKeyWrite},
|
||||||
|
|
||||||
|
{name: "NodeReadPrefixAllowed", prefix: "fo", check: checkAllowNodeRead},
|
||||||
|
{name: "NodeWritePrefixDenied", prefix: "fo", check: checkDenyNodeWrite},
|
||||||
|
{name: "NodeReadPrefixAllowed", prefix: "for", check: checkAllowNodeRead},
|
||||||
|
{name: "NodeWritePrefixDenied", prefix: "for", check: checkDenyNodeWrite},
|
||||||
|
{name: "NodeReadAllowed", prefix: "foo", check: checkAllowNodeRead},
|
||||||
|
{name: "NodeWriteAllowed", prefix: "foo", check: checkAllowNodeWrite},
|
||||||
|
{name: "NodeReadPrefixAllowed", prefix: "foot", check: checkAllowNodeRead},
|
||||||
|
{name: "NodeWritePrefixDenied", prefix: "foot", check: checkDenyNodeWrite},
|
||||||
|
{name: "NodeReadPrefixAllowed", prefix: "foot2", check: checkAllowNodeRead},
|
||||||
|
{name: "NodeWritePrefixDenied", prefix: "foot2", check: checkDenyNodeWrite},
|
||||||
|
{name: "NodeReadPrefixAllowed", prefix: "food", check: checkAllowNodeRead},
|
||||||
|
{name: "NodeWritePrefixDenied", prefix: "food", check: checkDenyNodeWrite},
|
||||||
|
{name: "NodeReadDenied", prefix: "football", check: checkDenyNodeRead},
|
||||||
|
{name: "NodeWriteDenied", prefix: "football", check: checkDenyNodeWrite},
|
||||||
|
|
||||||
|
{name: "ServiceReadPrefixAllowed", prefix: "fo", check: checkAllowServiceRead},
|
||||||
|
{name: "ServiceWritePrefixDenied", prefix: "fo", check: checkDenyServiceWrite},
|
||||||
|
{name: "ServiceReadPrefixAllowed", prefix: "for", check: checkAllowServiceRead},
|
||||||
|
{name: "ServiceWritePrefixDenied", prefix: "for", check: checkDenyServiceWrite},
|
||||||
|
{name: "ServiceReadAllowed", prefix: "foo", check: checkAllowServiceRead},
|
||||||
|
{name: "ServiceWriteAllowed", prefix: "foo", check: checkAllowServiceWrite},
|
||||||
|
{name: "ServiceReadPrefixAllowed", prefix: "foot", check: checkAllowServiceRead},
|
||||||
|
{name: "ServiceWritePrefixDenied", prefix: "foot", check: checkDenyServiceWrite},
|
||||||
|
{name: "ServiceReadPrefixAllowed", prefix: "foot2", check: checkAllowServiceRead},
|
||||||
|
{name: "ServiceWritePrefixDenied", prefix: "foot2", check: checkDenyServiceWrite},
|
||||||
|
{name: "ServiceReadPrefixAllowed", prefix: "food", check: checkAllowServiceRead},
|
||||||
|
{name: "ServiceWritePrefixDenied", prefix: "food", check: checkDenyServiceWrite},
|
||||||
|
{name: "ServiceReadDenied", prefix: "football", check: checkDenyServiceRead},
|
||||||
|
{name: "ServiceWriteDenied", prefix: "football", check: checkDenyServiceWrite},
|
||||||
|
|
||||||
|
{name: "NodeReadPrefixAllowed", prefix: "fo", check: checkAllowNodeRead},
|
||||||
|
{name: "NodeWritePrefixDenied", prefix: "fo", check: checkDenyNodeWrite},
|
||||||
|
{name: "NodeReadPrefixAllowed", prefix: "for", check: checkAllowNodeRead},
|
||||||
|
{name: "NodeWritePrefixDenied", prefix: "for", check: checkDenyNodeWrite},
|
||||||
|
{name: "NodeReadAllowed", prefix: "foo", check: checkAllowNodeRead},
|
||||||
|
{name: "NodeWriteAllowed", prefix: "foo", check: checkAllowNodeWrite},
|
||||||
|
{name: "NodeReadPrefixAllowed", prefix: "foot", check: checkAllowNodeRead},
|
||||||
|
{name: "NodeWritePrefixDenied", prefix: "foot", check: checkDenyNodeWrite},
|
||||||
|
{name: "NodeReadPrefixAllowed", prefix: "foot2", check: checkAllowNodeRead},
|
||||||
|
{name: "NodeWritePrefixDenied", prefix: "foot2", check: checkDenyNodeWrite},
|
||||||
|
{name: "NodeReadPrefixAllowed", prefix: "food", check: checkAllowNodeRead},
|
||||||
|
{name: "NodeWritePrefixDenied", prefix: "food", check: checkDenyNodeWrite},
|
||||||
|
{name: "NodeReadDenied", prefix: "football", check: checkDenyNodeRead},
|
||||||
|
{name: "NodeWriteDenied", prefix: "football", check: checkDenyNodeWrite},
|
||||||
|
|
||||||
|
{name: "IntentionReadPrefixAllowed", prefix: "fo", check: checkAllowIntentionRead},
|
||||||
|
{name: "IntentionWritePrefixDenied", prefix: "fo", check: checkDenyIntentionWrite},
|
||||||
|
{name: "IntentionReadPrefixAllowed", prefix: "for", check: checkAllowIntentionRead},
|
||||||
|
{name: "IntentionWritePrefixDenied", prefix: "for", check: checkDenyIntentionWrite},
|
||||||
|
{name: "IntentionReadAllowed", prefix: "foo", check: checkAllowIntentionRead},
|
||||||
|
{name: "IntentionWriteAllowed", prefix: "foo", check: checkAllowIntentionWrite},
|
||||||
|
{name: "IntentionReadPrefixAllowed", prefix: "foot", check: checkAllowIntentionRead},
|
||||||
|
{name: "IntentionWritePrefixDenied", prefix: "foot", check: checkDenyIntentionWrite},
|
||||||
|
{name: "IntentionReadPrefixAllowed", prefix: "foot2", check: checkAllowIntentionRead},
|
||||||
|
{name: "IntentionWritePrefixDenied", prefix: "foot2", check: checkDenyIntentionWrite},
|
||||||
|
{name: "IntentionReadPrefixAllowed", prefix: "food", check: checkAllowIntentionRead},
|
||||||
|
{name: "IntentionWritePrefixDenied", prefix: "food", check: checkDenyIntentionWrite},
|
||||||
|
{name: "IntentionReadDenied", prefix: "football", check: checkDenyIntentionRead},
|
||||||
|
{name: "IntentionWriteDenied", prefix: "football", check: checkDenyIntentionWrite},
|
||||||
|
|
||||||
|
{name: "SessionReadPrefixAllowed", prefix: "fo", check: checkAllowSessionRead},
|
||||||
|
{name: "SessionWritePrefixDenied", prefix: "fo", check: checkDenySessionWrite},
|
||||||
|
{name: "SessionReadPrefixAllowed", prefix: "for", check: checkAllowSessionRead},
|
||||||
|
{name: "SessionWritePrefixDenied", prefix: "for", check: checkDenySessionWrite},
|
||||||
|
{name: "SessionReadAllowed", prefix: "foo", check: checkAllowSessionRead},
|
||||||
|
{name: "SessionWriteAllowed", prefix: "foo", check: checkAllowSessionWrite},
|
||||||
|
{name: "SessionReadPrefixAllowed", prefix: "foot", check: checkAllowSessionRead},
|
||||||
|
{name: "SessionWritePrefixDenied", prefix: "foot", check: checkDenySessionWrite},
|
||||||
|
{name: "SessionReadPrefixAllowed", prefix: "foot2", check: checkAllowSessionRead},
|
||||||
|
{name: "SessionWritePrefixDenied", prefix: "foot2", check: checkDenySessionWrite},
|
||||||
|
{name: "SessionReadPrefixAllowed", prefix: "food", check: checkAllowSessionRead},
|
||||||
|
{name: "SessionWritePrefixDenied", prefix: "food", check: checkDenySessionWrite},
|
||||||
|
{name: "SessionReadDenied", prefix: "football", check: checkDenySessionRead},
|
||||||
|
{name: "SessionWriteDenied", prefix: "football", check: checkDenySessionWrite},
|
||||||
|
|
||||||
|
{name: "EventReadPrefixAllowed", prefix: "fo", check: checkAllowEventRead},
|
||||||
|
{name: "EventWritePrefixDenied", prefix: "fo", check: checkDenyEventWrite},
|
||||||
|
{name: "EventReadPrefixAllowed", prefix: "for", check: checkAllowEventRead},
|
||||||
|
{name: "EventWritePrefixDenied", prefix: "for", check: checkDenyEventWrite},
|
||||||
|
{name: "EventReadAllowed", prefix: "foo", check: checkAllowEventRead},
|
||||||
|
{name: "EventWriteAllowed", prefix: "foo", check: checkAllowEventWrite},
|
||||||
|
{name: "EventReadPrefixAllowed", prefix: "foot", check: checkAllowEventRead},
|
||||||
|
{name: "EventWritePrefixDenied", prefix: "foot", check: checkDenyEventWrite},
|
||||||
|
{name: "EventReadPrefixAllowed", prefix: "foot2", check: checkAllowEventRead},
|
||||||
|
{name: "EventWritePrefixDenied", prefix: "foot2", check: checkDenyEventWrite},
|
||||||
|
{name: "EventReadPrefixAllowed", prefix: "food", check: checkAllowEventRead},
|
||||||
|
{name: "EventWritePrefixDenied", prefix: "food", check: checkDenyEventWrite},
|
||||||
|
{name: "EventReadDenied", prefix: "football", check: checkDenyEventRead},
|
||||||
|
{name: "EventWriteDenied", prefix: "football", check: checkDenyEventWrite},
|
||||||
|
|
||||||
|
{name: "PreparedQueryReadPrefixAllowed", prefix: "fo", check: checkAllowPreparedQueryRead},
|
||||||
|
{name: "PreparedQueryWritePrefixDenied", prefix: "fo", check: checkDenyPreparedQueryWrite},
|
||||||
|
{name: "PreparedQueryReadPrefixAllowed", prefix: "for", check: checkAllowPreparedQueryRead},
|
||||||
|
{name: "PreparedQueryWritePrefixDenied", prefix: "for", check: checkDenyPreparedQueryWrite},
|
||||||
|
{name: "PreparedQueryReadAllowed", prefix: "foo", check: checkAllowPreparedQueryRead},
|
||||||
|
{name: "PreparedQueryWriteAllowed", prefix: "foo", check: checkAllowPreparedQueryWrite},
|
||||||
|
{name: "PreparedQueryReadPrefixAllowed", prefix: "foot", check: checkAllowPreparedQueryRead},
|
||||||
|
{name: "PreparedQueryWritePrefixDenied", prefix: "foot", check: checkDenyPreparedQueryWrite},
|
||||||
|
{name: "PreparedQueryReadPrefixAllowed", prefix: "foot2", check: checkAllowPreparedQueryRead},
|
||||||
|
{name: "PreparedQueryWritePrefixDenied", prefix: "foot2", check: checkDenyPreparedQueryWrite},
|
||||||
|
{name: "PreparedQueryReadPrefixAllowed", prefix: "food", check: checkAllowPreparedQueryRead},
|
||||||
|
{name: "PreparedQueryWritePrefixDenied", prefix: "food", check: checkDenyPreparedQueryWrite},
|
||||||
|
{name: "PreparedQueryReadDenied", prefix: "football", check: checkDenyPreparedQueryRead},
|
||||||
|
{name: "PreparedQueryWriteDenied", prefix: "football", check: checkDenyPreparedQueryWrite},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for name, tcase := range cases {
|
||||||
|
name := name
|
||||||
|
tcase := tcase
|
||||||
|
t.Run(name, func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
authz, err := NewPolicyAuthorizer([]*Policy{tcase.policy}, nil)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
for _, check := range tcase.checks {
|
||||||
|
checkName := check.name
|
||||||
|
if check.prefix != "" {
|
||||||
|
checkName = fmt.Sprintf("%s.Prefix(%s)", checkName, check.prefix)
|
||||||
|
}
|
||||||
|
t.Run(checkName, func(t *testing.T) {
|
||||||
|
check := check
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
check.check(t, authz, check.prefix, nil)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,364 @@
|
||||||
|
package acl
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/binary"
|
||||||
|
"fmt"
|
||||||
|
"hash"
|
||||||
|
|
||||||
|
"golang.org/x/crypto/blake2b"
|
||||||
|
)
|
||||||
|
|
||||||
|
type policyRulesMergeContext struct {
|
||||||
|
aclRule string
|
||||||
|
agentRules map[string]*AgentRule
|
||||||
|
agentPrefixRules map[string]*AgentRule
|
||||||
|
eventRules map[string]*EventRule
|
||||||
|
eventPrefixRules map[string]*EventRule
|
||||||
|
keyringRule string
|
||||||
|
keyRules map[string]*KeyRule
|
||||||
|
keyPrefixRules map[string]*KeyRule
|
||||||
|
nodeRules map[string]*NodeRule
|
||||||
|
nodePrefixRules map[string]*NodeRule
|
||||||
|
operatorRule string
|
||||||
|
preparedQueryRules map[string]*PreparedQueryRule
|
||||||
|
preparedQueryPrefixRules map[string]*PreparedQueryRule
|
||||||
|
serviceRules map[string]*ServiceRule
|
||||||
|
servicePrefixRules map[string]*ServiceRule
|
||||||
|
sessionRules map[string]*SessionRule
|
||||||
|
sessionPrefixRules map[string]*SessionRule
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *policyRulesMergeContext) init() {
|
||||||
|
p.aclRule = ""
|
||||||
|
p.agentRules = make(map[string]*AgentRule)
|
||||||
|
p.agentPrefixRules = make(map[string]*AgentRule)
|
||||||
|
p.eventRules = make(map[string]*EventRule)
|
||||||
|
p.eventPrefixRules = make(map[string]*EventRule)
|
||||||
|
p.keyringRule = ""
|
||||||
|
p.keyRules = make(map[string]*KeyRule)
|
||||||
|
p.keyPrefixRules = make(map[string]*KeyRule)
|
||||||
|
p.nodeRules = make(map[string]*NodeRule)
|
||||||
|
p.nodePrefixRules = make(map[string]*NodeRule)
|
||||||
|
p.operatorRule = ""
|
||||||
|
p.preparedQueryRules = make(map[string]*PreparedQueryRule)
|
||||||
|
p.preparedQueryPrefixRules = make(map[string]*PreparedQueryRule)
|
||||||
|
p.serviceRules = make(map[string]*ServiceRule)
|
||||||
|
p.servicePrefixRules = make(map[string]*ServiceRule)
|
||||||
|
p.sessionRules = make(map[string]*SessionRule)
|
||||||
|
p.sessionPrefixRules = make(map[string]*SessionRule)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *policyRulesMergeContext) merge(policy *PolicyRules) {
|
||||||
|
if takesPrecedenceOver(policy.ACL, p.aclRule) {
|
||||||
|
p.aclRule = policy.ACL
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, ap := range policy.Agents {
|
||||||
|
update := true
|
||||||
|
if permission, found := p.agentRules[ap.Node]; found {
|
||||||
|
update = takesPrecedenceOver(ap.Policy, permission.Policy)
|
||||||
|
}
|
||||||
|
|
||||||
|
if update {
|
||||||
|
p.agentRules[ap.Node] = ap
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, ap := range policy.AgentPrefixes {
|
||||||
|
update := true
|
||||||
|
if permission, found := p.agentPrefixRules[ap.Node]; found {
|
||||||
|
update = takesPrecedenceOver(ap.Policy, permission.Policy)
|
||||||
|
}
|
||||||
|
|
||||||
|
if update {
|
||||||
|
p.agentPrefixRules[ap.Node] = ap
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, ep := range policy.Events {
|
||||||
|
update := true
|
||||||
|
if permission, found := p.eventRules[ep.Event]; found {
|
||||||
|
update = takesPrecedenceOver(ep.Policy, permission.Policy)
|
||||||
|
}
|
||||||
|
|
||||||
|
if update {
|
||||||
|
p.eventRules[ep.Event] = ep
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, ep := range policy.EventPrefixes {
|
||||||
|
update := true
|
||||||
|
if permission, found := p.eventPrefixRules[ep.Event]; found {
|
||||||
|
update = takesPrecedenceOver(ep.Policy, permission.Policy)
|
||||||
|
}
|
||||||
|
|
||||||
|
if update {
|
||||||
|
p.eventPrefixRules[ep.Event] = ep
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if takesPrecedenceOver(policy.Keyring, p.keyringRule) {
|
||||||
|
p.keyringRule = policy.Keyring
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, kp := range policy.Keys {
|
||||||
|
update := true
|
||||||
|
if permission, found := p.keyRules[kp.Prefix]; found {
|
||||||
|
update = takesPrecedenceOver(kp.Policy, permission.Policy)
|
||||||
|
}
|
||||||
|
|
||||||
|
if update {
|
||||||
|
p.keyRules[kp.Prefix] = kp
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, kp := range policy.KeyPrefixes {
|
||||||
|
update := true
|
||||||
|
if permission, found := p.keyPrefixRules[kp.Prefix]; found {
|
||||||
|
update = takesPrecedenceOver(kp.Policy, permission.Policy)
|
||||||
|
}
|
||||||
|
|
||||||
|
if update {
|
||||||
|
p.keyPrefixRules[kp.Prefix] = kp
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, np := range policy.Nodes {
|
||||||
|
update := true
|
||||||
|
if permission, found := p.nodeRules[np.Name]; found {
|
||||||
|
update = takesPrecedenceOver(np.Policy, permission.Policy)
|
||||||
|
}
|
||||||
|
|
||||||
|
if update {
|
||||||
|
p.nodeRules[np.Name] = np
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, np := range policy.NodePrefixes {
|
||||||
|
update := true
|
||||||
|
if permission, found := p.nodePrefixRules[np.Name]; found {
|
||||||
|
update = takesPrecedenceOver(np.Policy, permission.Policy)
|
||||||
|
}
|
||||||
|
|
||||||
|
if update {
|
||||||
|
p.nodePrefixRules[np.Name] = np
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if takesPrecedenceOver(policy.Operator, p.operatorRule) {
|
||||||
|
p.operatorRule = policy.Operator
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, qp := range policy.PreparedQueries {
|
||||||
|
update := true
|
||||||
|
if permission, found := p.preparedQueryRules[qp.Prefix]; found {
|
||||||
|
update = takesPrecedenceOver(qp.Policy, permission.Policy)
|
||||||
|
}
|
||||||
|
|
||||||
|
if update {
|
||||||
|
p.preparedQueryRules[qp.Prefix] = qp
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, qp := range policy.PreparedQueryPrefixes {
|
||||||
|
update := true
|
||||||
|
if permission, found := p.preparedQueryPrefixRules[qp.Prefix]; found {
|
||||||
|
update = takesPrecedenceOver(qp.Policy, permission.Policy)
|
||||||
|
}
|
||||||
|
|
||||||
|
if update {
|
||||||
|
p.preparedQueryPrefixRules[qp.Prefix] = qp
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, sp := range policy.Services {
|
||||||
|
existing, found := p.serviceRules[sp.Name]
|
||||||
|
|
||||||
|
if !found {
|
||||||
|
p.serviceRules[sp.Name] = sp
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if takesPrecedenceOver(sp.Policy, existing.Policy) {
|
||||||
|
existing.Policy = sp.Policy
|
||||||
|
existing.EnterpriseRule = sp.EnterpriseRule
|
||||||
|
}
|
||||||
|
|
||||||
|
if takesPrecedenceOver(sp.Intentions, existing.Intentions) {
|
||||||
|
existing.Intentions = sp.Intentions
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, sp := range policy.ServicePrefixes {
|
||||||
|
existing, found := p.servicePrefixRules[sp.Name]
|
||||||
|
|
||||||
|
if !found {
|
||||||
|
p.servicePrefixRules[sp.Name] = sp
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if takesPrecedenceOver(sp.Policy, existing.Policy) {
|
||||||
|
existing.Policy = sp.Policy
|
||||||
|
existing.EnterpriseRule = sp.EnterpriseRule
|
||||||
|
}
|
||||||
|
|
||||||
|
if takesPrecedenceOver(sp.Intentions, existing.Intentions) {
|
||||||
|
existing.Intentions = sp.Intentions
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, sp := range policy.Sessions {
|
||||||
|
update := true
|
||||||
|
if permission, found := p.sessionRules[sp.Node]; found {
|
||||||
|
update = takesPrecedenceOver(sp.Policy, permission.Policy)
|
||||||
|
}
|
||||||
|
|
||||||
|
if update {
|
||||||
|
p.sessionRules[sp.Node] = sp
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, sp := range policy.SessionPrefixes {
|
||||||
|
update := true
|
||||||
|
if permission, found := p.sessionPrefixRules[sp.Node]; found {
|
||||||
|
update = takesPrecedenceOver(sp.Policy, permission.Policy)
|
||||||
|
}
|
||||||
|
|
||||||
|
if update {
|
||||||
|
p.sessionPrefixRules[sp.Node] = sp
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *policyRulesMergeContext) update(merged *PolicyRules) {
|
||||||
|
merged.ACL = p.aclRule
|
||||||
|
merged.Keyring = p.keyringRule
|
||||||
|
merged.Operator = p.operatorRule
|
||||||
|
|
||||||
|
// All the for loop appends are ugly but Go doesn't have a way to get
|
||||||
|
// a slice of all values within a map so this is necessary
|
||||||
|
|
||||||
|
merged.Agents = []*AgentRule{}
|
||||||
|
for _, policy := range p.agentRules {
|
||||||
|
merged.Agents = append(merged.Agents, policy)
|
||||||
|
}
|
||||||
|
|
||||||
|
merged.AgentPrefixes = []*AgentRule{}
|
||||||
|
for _, policy := range p.agentPrefixRules {
|
||||||
|
merged.AgentPrefixes = append(merged.AgentPrefixes, policy)
|
||||||
|
}
|
||||||
|
|
||||||
|
merged.Events = []*EventRule{}
|
||||||
|
for _, policy := range p.eventRules {
|
||||||
|
merged.Events = append(merged.Events, policy)
|
||||||
|
}
|
||||||
|
|
||||||
|
merged.EventPrefixes = []*EventRule{}
|
||||||
|
for _, policy := range p.eventPrefixRules {
|
||||||
|
merged.EventPrefixes = append(merged.EventPrefixes, policy)
|
||||||
|
}
|
||||||
|
|
||||||
|
merged.Keys = []*KeyRule{}
|
||||||
|
for _, policy := range p.keyRules {
|
||||||
|
merged.Keys = append(merged.Keys, policy)
|
||||||
|
}
|
||||||
|
|
||||||
|
merged.KeyPrefixes = []*KeyRule{}
|
||||||
|
for _, policy := range p.keyPrefixRules {
|
||||||
|
merged.KeyPrefixes = append(merged.KeyPrefixes, policy)
|
||||||
|
}
|
||||||
|
|
||||||
|
merged.Nodes = []*NodeRule{}
|
||||||
|
for _, policy := range p.nodeRules {
|
||||||
|
merged.Nodes = append(merged.Nodes, policy)
|
||||||
|
}
|
||||||
|
|
||||||
|
merged.NodePrefixes = []*NodeRule{}
|
||||||
|
for _, policy := range p.nodePrefixRules {
|
||||||
|
merged.NodePrefixes = append(merged.NodePrefixes, policy)
|
||||||
|
}
|
||||||
|
|
||||||
|
merged.PreparedQueries = []*PreparedQueryRule{}
|
||||||
|
for _, policy := range p.preparedQueryRules {
|
||||||
|
merged.PreparedQueries = append(merged.PreparedQueries, policy)
|
||||||
|
}
|
||||||
|
|
||||||
|
merged.PreparedQueryPrefixes = []*PreparedQueryRule{}
|
||||||
|
for _, policy := range p.preparedQueryPrefixRules {
|
||||||
|
merged.PreparedQueryPrefixes = append(merged.PreparedQueryPrefixes, policy)
|
||||||
|
}
|
||||||
|
|
||||||
|
merged.Services = []*ServiceRule{}
|
||||||
|
for _, policy := range p.serviceRules {
|
||||||
|
merged.Services = append(merged.Services, policy)
|
||||||
|
}
|
||||||
|
|
||||||
|
merged.ServicePrefixes = []*ServiceRule{}
|
||||||
|
for _, policy := range p.servicePrefixRules {
|
||||||
|
merged.ServicePrefixes = append(merged.ServicePrefixes, policy)
|
||||||
|
}
|
||||||
|
|
||||||
|
merged.Sessions = []*SessionRule{}
|
||||||
|
for _, policy := range p.sessionRules {
|
||||||
|
merged.Sessions = append(merged.Sessions, policy)
|
||||||
|
}
|
||||||
|
|
||||||
|
merged.SessionPrefixes = []*SessionRule{}
|
||||||
|
for _, policy := range p.sessionPrefixRules {
|
||||||
|
merged.SessionPrefixes = append(merged.SessionPrefixes, policy)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type PolicyMerger struct {
|
||||||
|
idHasher hash.Hash
|
||||||
|
policyRulesMergeContext
|
||||||
|
enterprisePolicyRulesMergeContext
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewPolicyMerger() *PolicyMerger {
|
||||||
|
merger := &PolicyMerger{}
|
||||||
|
merger.init()
|
||||||
|
return merger
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *PolicyMerger) init() {
|
||||||
|
var err error
|
||||||
|
m.idHasher, err = blake2b.New256(nil)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
m.policyRulesMergeContext.init()
|
||||||
|
m.enterprisePolicyRulesMergeContext.init()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *PolicyMerger) Merge(policy *Policy) {
|
||||||
|
// This is part of calculating the merged policies ID
|
||||||
|
m.idHasher.Write([]byte(policy.ID))
|
||||||
|
binary.Write(m.idHasher, binary.BigEndian, policy.Revision)
|
||||||
|
|
||||||
|
m.policyRulesMergeContext.merge(&policy.PolicyRules)
|
||||||
|
m.enterprisePolicyRulesMergeContext.merge(&policy.EnterprisePolicyRules)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Policy outputs the merged policy
|
||||||
|
func (m *PolicyMerger) Policy() *Policy {
|
||||||
|
merged := &Policy{
|
||||||
|
ID: fmt.Sprintf("%x", m.idHasher.Sum(nil)),
|
||||||
|
}
|
||||||
|
|
||||||
|
m.policyRulesMergeContext.update(&merged.PolicyRules)
|
||||||
|
m.enterprisePolicyRulesMergeContext.update(&merged.EnterprisePolicyRules)
|
||||||
|
|
||||||
|
return merged
|
||||||
|
}
|
||||||
|
|
||||||
|
func MergePolicies(policies []*Policy) *Policy {
|
||||||
|
var merger PolicyMerger
|
||||||
|
merger.init()
|
||||||
|
for _, p := range policies {
|
||||||
|
merger.Merge(p)
|
||||||
|
}
|
||||||
|
|
||||||
|
return merger.Policy()
|
||||||
|
}
|
|
@ -0,0 +1,17 @@
|
||||||
|
// +build !consulent
|
||||||
|
|
||||||
|
package acl
|
||||||
|
|
||||||
|
type enterprisePolicyRulesMergeContext struct{}
|
||||||
|
|
||||||
|
func (ctx *enterprisePolicyRulesMergeContext) init() {
|
||||||
|
// do nothing
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ctx *enterprisePolicyRulesMergeContext) merge(*EnterprisePolicyRules) {
|
||||||
|
// do nothing
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ctx *enterprisePolicyRulesMergeContext) update(*EnterprisePolicyRules) {
|
||||||
|
// do nothing
|
||||||
|
}
|
|
@ -0,0 +1,19 @@
|
||||||
|
// +build !consulent
|
||||||
|
|
||||||
|
package acl
|
||||||
|
|
||||||
|
// EnterpriseRule stub
|
||||||
|
type EnterpriseRule struct{}
|
||||||
|
|
||||||
|
func (r *EnterpriseRule) Validate(string, *EnterpriseACLConfig) error {
|
||||||
|
// nothing to validate
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// EnterprisePolicyRules stub
|
||||||
|
type EnterprisePolicyRules struct{}
|
||||||
|
|
||||||
|
func (r *EnterprisePolicyRules) Validate(*EnterpriseACLConfig) error {
|
||||||
|
// nothing to validate
|
||||||
|
return nil
|
||||||
|
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,244 @@
|
||||||
|
package acl
|
||||||
|
|
||||||
|
var (
|
||||||
|
// allowAll is a singleton policy which allows all
|
||||||
|
// non-management actions
|
||||||
|
allowAll Authorizer = &StaticAuthorizer{
|
||||||
|
allowManage: false,
|
||||||
|
defaultAllow: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
// denyAll is a singleton policy which denies all actions
|
||||||
|
denyAll Authorizer = &StaticAuthorizer{
|
||||||
|
allowManage: false,
|
||||||
|
defaultAllow: false,
|
||||||
|
}
|
||||||
|
|
||||||
|
// manageAll is a singleton policy which allows all
|
||||||
|
// actions, including management
|
||||||
|
// TODO (acls) - Do we need to keep this around? Our config parsing doesn't allow
|
||||||
|
// specifying a default "manage" policy so I believe nothing will every use this.
|
||||||
|
manageAll Authorizer = &StaticAuthorizer{
|
||||||
|
allowManage: true,
|
||||||
|
defaultAllow: true,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
// StaticAuthorizer is used to implement a base ACL policy. It either
|
||||||
|
// allows or denies all requests. This can be used as a parent
|
||||||
|
// ACL to act in a blacklist or whitelist mode.
|
||||||
|
type StaticAuthorizer struct {
|
||||||
|
allowManage bool
|
||||||
|
defaultAllow bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StaticAuthorizer) ACLRead(*EnterpriseAuthorizerContext) EnforcementDecision {
|
||||||
|
if s.allowManage {
|
||||||
|
return Allow
|
||||||
|
}
|
||||||
|
return Deny
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StaticAuthorizer) ACLWrite(*EnterpriseAuthorizerContext) EnforcementDecision {
|
||||||
|
if s.allowManage {
|
||||||
|
return Allow
|
||||||
|
}
|
||||||
|
return Deny
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StaticAuthorizer) AgentRead(string, *EnterpriseAuthorizerContext) EnforcementDecision {
|
||||||
|
if s.defaultAllow {
|
||||||
|
return Allow
|
||||||
|
}
|
||||||
|
return Deny
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StaticAuthorizer) AgentWrite(string, *EnterpriseAuthorizerContext) EnforcementDecision {
|
||||||
|
if s.defaultAllow {
|
||||||
|
return Allow
|
||||||
|
}
|
||||||
|
return Deny
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StaticAuthorizer) EventRead(string, *EnterpriseAuthorizerContext) EnforcementDecision {
|
||||||
|
if s.defaultAllow {
|
||||||
|
return Allow
|
||||||
|
}
|
||||||
|
return Deny
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StaticAuthorizer) EventWrite(string, *EnterpriseAuthorizerContext) EnforcementDecision {
|
||||||
|
if s.defaultAllow {
|
||||||
|
return Allow
|
||||||
|
}
|
||||||
|
return Deny
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StaticAuthorizer) IntentionDefaultAllow(*EnterpriseAuthorizerContext) EnforcementDecision {
|
||||||
|
if s.defaultAllow {
|
||||||
|
return Allow
|
||||||
|
}
|
||||||
|
return Deny
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StaticAuthorizer) IntentionRead(string, *EnterpriseAuthorizerContext) EnforcementDecision {
|
||||||
|
if s.defaultAllow {
|
||||||
|
return Allow
|
||||||
|
}
|
||||||
|
return Deny
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StaticAuthorizer) IntentionWrite(string, *EnterpriseAuthorizerContext) EnforcementDecision {
|
||||||
|
if s.defaultAllow {
|
||||||
|
return Allow
|
||||||
|
}
|
||||||
|
return Deny
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StaticAuthorizer) KeyRead(string, *EnterpriseAuthorizerContext) EnforcementDecision {
|
||||||
|
if s.defaultAllow {
|
||||||
|
return Allow
|
||||||
|
}
|
||||||
|
return Deny
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StaticAuthorizer) KeyList(string, *EnterpriseAuthorizerContext) EnforcementDecision {
|
||||||
|
if s.defaultAllow {
|
||||||
|
return Allow
|
||||||
|
}
|
||||||
|
return Deny
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StaticAuthorizer) KeyWrite(string, *EnterpriseAuthorizerContext) EnforcementDecision {
|
||||||
|
if s.defaultAllow {
|
||||||
|
return Allow
|
||||||
|
}
|
||||||
|
return Deny
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StaticAuthorizer) KeyWritePrefix(string, *EnterpriseAuthorizerContext) EnforcementDecision {
|
||||||
|
if s.defaultAllow {
|
||||||
|
return Allow
|
||||||
|
}
|
||||||
|
return Deny
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StaticAuthorizer) KeyringRead(*EnterpriseAuthorizerContext) EnforcementDecision {
|
||||||
|
if s.defaultAllow {
|
||||||
|
return Allow
|
||||||
|
}
|
||||||
|
return Deny
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StaticAuthorizer) KeyringWrite(*EnterpriseAuthorizerContext) EnforcementDecision {
|
||||||
|
if s.defaultAllow {
|
||||||
|
return Allow
|
||||||
|
}
|
||||||
|
return Deny
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StaticAuthorizer) NodeRead(string, *EnterpriseAuthorizerContext) EnforcementDecision {
|
||||||
|
if s.defaultAllow {
|
||||||
|
return Allow
|
||||||
|
}
|
||||||
|
return Deny
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StaticAuthorizer) NodeWrite(string, *EnterpriseAuthorizerContext) EnforcementDecision {
|
||||||
|
if s.defaultAllow {
|
||||||
|
return Allow
|
||||||
|
}
|
||||||
|
return Deny
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StaticAuthorizer) OperatorRead(*EnterpriseAuthorizerContext) EnforcementDecision {
|
||||||
|
if s.defaultAllow {
|
||||||
|
return Allow
|
||||||
|
}
|
||||||
|
return Deny
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StaticAuthorizer) OperatorWrite(*EnterpriseAuthorizerContext) EnforcementDecision {
|
||||||
|
if s.defaultAllow {
|
||||||
|
return Allow
|
||||||
|
}
|
||||||
|
return Deny
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StaticAuthorizer) PreparedQueryRead(string, *EnterpriseAuthorizerContext) EnforcementDecision {
|
||||||
|
if s.defaultAllow {
|
||||||
|
return Allow
|
||||||
|
}
|
||||||
|
return Deny
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StaticAuthorizer) PreparedQueryWrite(string, *EnterpriseAuthorizerContext) EnforcementDecision {
|
||||||
|
if s.defaultAllow {
|
||||||
|
return Allow
|
||||||
|
}
|
||||||
|
return Deny
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StaticAuthorizer) ServiceRead(string, *EnterpriseAuthorizerContext) EnforcementDecision {
|
||||||
|
if s.defaultAllow {
|
||||||
|
return Allow
|
||||||
|
}
|
||||||
|
return Deny
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StaticAuthorizer) ServiceWrite(string, *EnterpriseAuthorizerContext) EnforcementDecision {
|
||||||
|
if s.defaultAllow {
|
||||||
|
return Allow
|
||||||
|
}
|
||||||
|
return Deny
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StaticAuthorizer) SessionRead(string, *EnterpriseAuthorizerContext) EnforcementDecision {
|
||||||
|
if s.defaultAllow {
|
||||||
|
return Allow
|
||||||
|
}
|
||||||
|
return Deny
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StaticAuthorizer) SessionWrite(string, *EnterpriseAuthorizerContext) EnforcementDecision {
|
||||||
|
if s.defaultAllow {
|
||||||
|
return Allow
|
||||||
|
}
|
||||||
|
return Deny
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StaticAuthorizer) Snapshot(_ *EnterpriseAuthorizerContext) EnforcementDecision {
|
||||||
|
if s.allowManage {
|
||||||
|
return Allow
|
||||||
|
}
|
||||||
|
return Deny
|
||||||
|
}
|
||||||
|
|
||||||
|
// AllowAll returns an Authorizer that allows all operations
|
||||||
|
func AllowAll() Authorizer {
|
||||||
|
return allowAll
|
||||||
|
}
|
||||||
|
|
||||||
|
// DenyAll returns an Authorizer that denies all operations
|
||||||
|
func DenyAll() Authorizer {
|
||||||
|
return denyAll
|
||||||
|
}
|
||||||
|
|
||||||
|
// ManageAll returns an Authorizer that can manage all resources
|
||||||
|
func ManageAll() Authorizer {
|
||||||
|
return manageAll
|
||||||
|
}
|
||||||
|
|
||||||
|
// RootAuthorizer returns a possible Authorizer if the ID matches a root policy
|
||||||
|
func RootAuthorizer(id string) Authorizer {
|
||||||
|
switch id {
|
||||||
|
case "allow":
|
||||||
|
return allowAll
|
||||||
|
case "deny":
|
||||||
|
return denyAll
|
||||||
|
case "manage":
|
||||||
|
return manageAll
|
||||||
|
default:
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,103 @@
|
||||||
|
package acl
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestStaticAuthorizer(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
t.Run("AllowAll", func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
authz := AllowAll()
|
||||||
|
checkDenyACLRead(t, authz, "foo", nil)
|
||||||
|
checkDenyACLWrite(t, authz, "foo", nil)
|
||||||
|
checkAllowAgentRead(t, authz, "foo", nil)
|
||||||
|
checkAllowAgentWrite(t, authz, "foo", nil)
|
||||||
|
checkAllowEventRead(t, authz, "foo", nil)
|
||||||
|
checkAllowEventWrite(t, authz, "foo", nil)
|
||||||
|
checkAllowIntentionDefaultAllow(t, authz, "foo", nil)
|
||||||
|
checkAllowIntentionRead(t, authz, "foo", nil)
|
||||||
|
checkAllowIntentionWrite(t, authz, "foo", nil)
|
||||||
|
checkAllowKeyRead(t, authz, "foo", nil)
|
||||||
|
checkAllowKeyList(t, authz, "foo", nil)
|
||||||
|
checkAllowKeyringRead(t, authz, "foo", nil)
|
||||||
|
checkAllowKeyringWrite(t, authz, "foo", nil)
|
||||||
|
checkAllowKeyWrite(t, authz, "foo", nil)
|
||||||
|
checkAllowKeyWritePrefix(t, authz, "foo", nil)
|
||||||
|
checkAllowNodeRead(t, authz, "foo", nil)
|
||||||
|
checkAllowNodeWrite(t, authz, "foo", nil)
|
||||||
|
checkAllowOperatorRead(t, authz, "foo", nil)
|
||||||
|
checkAllowOperatorWrite(t, authz, "foo", nil)
|
||||||
|
checkAllowPreparedQueryRead(t, authz, "foo", nil)
|
||||||
|
checkAllowPreparedQueryWrite(t, authz, "foo", nil)
|
||||||
|
checkAllowServiceRead(t, authz, "foo", nil)
|
||||||
|
checkAllowServiceWrite(t, authz, "foo", nil)
|
||||||
|
checkAllowSessionRead(t, authz, "foo", nil)
|
||||||
|
checkAllowSessionWrite(t, authz, "foo", nil)
|
||||||
|
checkDenySnapshot(t, authz, "foo", nil)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("DenyAll", func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
authz := DenyAll()
|
||||||
|
checkDenyACLRead(t, authz, "foo", nil)
|
||||||
|
checkDenyACLWrite(t, authz, "foo", nil)
|
||||||
|
checkDenyAgentRead(t, authz, "foo", nil)
|
||||||
|
checkDenyAgentWrite(t, authz, "foo", nil)
|
||||||
|
checkDenyEventRead(t, authz, "foo", nil)
|
||||||
|
checkDenyEventWrite(t, authz, "foo", nil)
|
||||||
|
checkDenyIntentionDefaultAllow(t, authz, "foo", nil)
|
||||||
|
checkDenyIntentionRead(t, authz, "foo", nil)
|
||||||
|
checkDenyIntentionWrite(t, authz, "foo", nil)
|
||||||
|
checkDenyKeyRead(t, authz, "foo", nil)
|
||||||
|
checkDenyKeyList(t, authz, "foo", nil)
|
||||||
|
checkDenyKeyringRead(t, authz, "foo", nil)
|
||||||
|
checkDenyKeyringWrite(t, authz, "foo", nil)
|
||||||
|
checkDenyKeyWrite(t, authz, "foo", nil)
|
||||||
|
checkDenyKeyWritePrefix(t, authz, "foo", nil)
|
||||||
|
checkDenyNodeRead(t, authz, "foo", nil)
|
||||||
|
checkDenyNodeWrite(t, authz, "foo", nil)
|
||||||
|
checkDenyOperatorRead(t, authz, "foo", nil)
|
||||||
|
checkDenyOperatorWrite(t, authz, "foo", nil)
|
||||||
|
checkDenyPreparedQueryRead(t, authz, "foo", nil)
|
||||||
|
checkDenyPreparedQueryWrite(t, authz, "foo", nil)
|
||||||
|
checkDenyServiceRead(t, authz, "foo", nil)
|
||||||
|
checkDenyServiceWrite(t, authz, "foo", nil)
|
||||||
|
checkDenySessionRead(t, authz, "foo", nil)
|
||||||
|
checkDenySessionWrite(t, authz, "foo", nil)
|
||||||
|
checkDenySnapshot(t, authz, "foo", nil)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("ManageAll", func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
authz := ManageAll()
|
||||||
|
checkAllowACLRead(t, authz, "foo", nil)
|
||||||
|
checkAllowACLWrite(t, authz, "foo", nil)
|
||||||
|
checkAllowAgentRead(t, authz, "foo", nil)
|
||||||
|
checkAllowAgentWrite(t, authz, "foo", nil)
|
||||||
|
checkAllowEventRead(t, authz, "foo", nil)
|
||||||
|
checkAllowEventWrite(t, authz, "foo", nil)
|
||||||
|
checkAllowIntentionDefaultAllow(t, authz, "foo", nil)
|
||||||
|
checkAllowIntentionRead(t, authz, "foo", nil)
|
||||||
|
checkAllowIntentionWrite(t, authz, "foo", nil)
|
||||||
|
checkAllowKeyRead(t, authz, "foo", nil)
|
||||||
|
checkAllowKeyList(t, authz, "foo", nil)
|
||||||
|
checkAllowKeyringRead(t, authz, "foo", nil)
|
||||||
|
checkAllowKeyringWrite(t, authz, "foo", nil)
|
||||||
|
checkAllowKeyWrite(t, authz, "foo", nil)
|
||||||
|
checkAllowKeyWritePrefix(t, authz, "foo", nil)
|
||||||
|
checkAllowNodeRead(t, authz, "foo", nil)
|
||||||
|
checkAllowNodeWrite(t, authz, "foo", nil)
|
||||||
|
checkAllowOperatorRead(t, authz, "foo", nil)
|
||||||
|
checkAllowOperatorWrite(t, authz, "foo", nil)
|
||||||
|
checkAllowPreparedQueryRead(t, authz, "foo", nil)
|
||||||
|
checkAllowPreparedQueryWrite(t, authz, "foo", nil)
|
||||||
|
checkAllowServiceRead(t, authz, "foo", nil)
|
||||||
|
checkAllowServiceWrite(t, authz, "foo", nil)
|
||||||
|
checkAllowSessionRead(t, authz, "foo", nil)
|
||||||
|
checkAllowSessionWrite(t, authz, "foo", nil)
|
||||||
|
checkAllowSnapshot(t, authz, "foo", nil)
|
||||||
|
})
|
||||||
|
}
|
64
agent/acl.go
64
agent/acl.go
|
@ -41,20 +41,22 @@ func (a *Agent) initializeACLs() error {
|
||||||
// only. This used to allow a prefix match on agent names but that seems
|
// only. This used to allow a prefix match on agent names but that seems
|
||||||
// entirely unnecessary so it is now using an exact match.
|
// entirely unnecessary so it is now using an exact match.
|
||||||
policy := &acl.Policy{
|
policy := &acl.Policy{
|
||||||
Agents: []*acl.AgentPolicy{
|
PolicyRules: acl.PolicyRules{
|
||||||
&acl.AgentPolicy{
|
Agents: []*acl.AgentRule{
|
||||||
Node: a.config.NodeName,
|
&acl.AgentRule{
|
||||||
Policy: acl.PolicyWrite,
|
Node: a.config.NodeName,
|
||||||
|
Policy: acl.PolicyWrite,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
NodePrefixes: []*acl.NodeRule{
|
||||||
NodePrefixes: []*acl.NodePolicy{
|
&acl.NodeRule{
|
||||||
&acl.NodePolicy{
|
Name: "",
|
||||||
Name: "",
|
Policy: acl.PolicyRead,
|
||||||
Policy: acl.PolicyRead,
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
master, err := acl.NewPolicyAuthorizer(acl.DenyAll(), []*acl.Policy{policy}, nil)
|
master, err := acl.NewPolicyAuthorizerWithDefaults(acl.DenyAll(), []*acl.Policy{policy}, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -75,14 +77,16 @@ func (a *Agent) vetServiceRegister(token string, service *structs.NodeService) e
|
||||||
}
|
}
|
||||||
|
|
||||||
// Vet the service itself.
|
// Vet the service itself.
|
||||||
if !rule.ServiceWrite(service.Service, nil) {
|
// TODO (namespaces) - pass through a real ent authz ctx
|
||||||
|
if rule.ServiceWrite(service.Service, nil) != acl.Allow {
|
||||||
return acl.ErrPermissionDenied
|
return acl.ErrPermissionDenied
|
||||||
}
|
}
|
||||||
|
|
||||||
// Vet any service that might be getting overwritten.
|
// Vet any service that might be getting overwritten.
|
||||||
services := a.State.Services()
|
services := a.State.Services()
|
||||||
if existing, ok := services[service.ID]; ok {
|
if existing, ok := services[service.ID]; ok {
|
||||||
if !rule.ServiceWrite(existing.Service, nil) {
|
// TODO (namespaces) - pass through a real ent authz ctx
|
||||||
|
if rule.ServiceWrite(existing.Service, nil) != acl.Allow {
|
||||||
return acl.ErrPermissionDenied
|
return acl.ErrPermissionDenied
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -90,7 +94,8 @@ func (a *Agent) vetServiceRegister(token string, service *structs.NodeService) e
|
||||||
// If the service is a proxy, ensure that it has write on the destination too
|
// If the service is a proxy, ensure that it has write on the destination too
|
||||||
// since it can be discovered as an instance of that service.
|
// since it can be discovered as an instance of that service.
|
||||||
if service.Kind == structs.ServiceKindConnectProxy {
|
if service.Kind == structs.ServiceKindConnectProxy {
|
||||||
if !rule.ServiceWrite(service.Proxy.DestinationServiceName, nil) {
|
// TODO (namespaces) - pass through a real ent authz ctx
|
||||||
|
if rule.ServiceWrite(service.Proxy.DestinationServiceName, nil) != acl.Allow {
|
||||||
return acl.ErrPermissionDenied
|
return acl.ErrPermissionDenied
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -113,7 +118,8 @@ func (a *Agent) vetServiceUpdate(token string, serviceID string) error {
|
||||||
// Vet any changes based on the existing services's info.
|
// Vet any changes based on the existing services's info.
|
||||||
services := a.State.Services()
|
services := a.State.Services()
|
||||||
if existing, ok := services[serviceID]; ok {
|
if existing, ok := services[serviceID]; ok {
|
||||||
if !rule.ServiceWrite(existing.Service, nil) {
|
// TODO (namespaces) - pass through a real ent authz ctx
|
||||||
|
if rule.ServiceWrite(existing.Service, nil) != acl.Allow {
|
||||||
return acl.ErrPermissionDenied
|
return acl.ErrPermissionDenied
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -137,11 +143,13 @@ func (a *Agent) vetCheckRegister(token string, check *structs.HealthCheck) error
|
||||||
|
|
||||||
// Vet the check itself.
|
// Vet the check itself.
|
||||||
if len(check.ServiceName) > 0 {
|
if len(check.ServiceName) > 0 {
|
||||||
if !rule.ServiceWrite(check.ServiceName, nil) {
|
// TODO (namespaces) - pass through a real ent authz ctx
|
||||||
|
if rule.ServiceWrite(check.ServiceName, nil) != acl.Allow {
|
||||||
return acl.ErrPermissionDenied
|
return acl.ErrPermissionDenied
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if !rule.NodeWrite(a.config.NodeName, nil) {
|
// TODO (namespaces) - pass through a real ent authz ctx
|
||||||
|
if rule.NodeWrite(a.config.NodeName, nil) != acl.Allow {
|
||||||
return acl.ErrPermissionDenied
|
return acl.ErrPermissionDenied
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -150,11 +158,13 @@ func (a *Agent) vetCheckRegister(token string, check *structs.HealthCheck) error
|
||||||
checks := a.State.Checks()
|
checks := a.State.Checks()
|
||||||
if existing, ok := checks[check.CheckID]; ok {
|
if existing, ok := checks[check.CheckID]; ok {
|
||||||
if len(existing.ServiceName) > 0 {
|
if len(existing.ServiceName) > 0 {
|
||||||
if !rule.ServiceWrite(existing.ServiceName, nil) {
|
// TODO (namespaces) - pass through a real ent authz ctx
|
||||||
|
if rule.ServiceWrite(existing.ServiceName, nil) != acl.Allow {
|
||||||
return acl.ErrPermissionDenied
|
return acl.ErrPermissionDenied
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if !rule.NodeWrite(a.config.NodeName, nil) {
|
// TODO (namespaces) - pass through a real ent authz ctx
|
||||||
|
if rule.NodeWrite(a.config.NodeName, nil) != acl.Allow {
|
||||||
return acl.ErrPermissionDenied
|
return acl.ErrPermissionDenied
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -178,11 +188,13 @@ func (a *Agent) vetCheckUpdate(token string, checkID types.CheckID) error {
|
||||||
checks := a.State.Checks()
|
checks := a.State.Checks()
|
||||||
if existing, ok := checks[checkID]; ok {
|
if existing, ok := checks[checkID]; ok {
|
||||||
if len(existing.ServiceName) > 0 {
|
if len(existing.ServiceName) > 0 {
|
||||||
if !rule.ServiceWrite(existing.ServiceName, nil) {
|
// TODO (namespaces) - pass through a real ent authz ctx
|
||||||
|
if rule.ServiceWrite(existing.ServiceName, nil) != acl.Allow {
|
||||||
return acl.ErrPermissionDenied
|
return acl.ErrPermissionDenied
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if !rule.NodeWrite(a.config.NodeName, nil) {
|
// TODO (namespaces) - pass through a real ent authz ctx
|
||||||
|
if rule.NodeWrite(a.config.NodeName, nil) != acl.Allow {
|
||||||
return acl.ErrPermissionDenied
|
return acl.ErrPermissionDenied
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -208,7 +220,8 @@ func (a *Agent) filterMembers(token string, members *[]serf.Member) error {
|
||||||
m := *members
|
m := *members
|
||||||
for i := 0; i < len(m); i++ {
|
for i := 0; i < len(m); i++ {
|
||||||
node := m[i].Name
|
node := m[i].Name
|
||||||
if rule.NodeRead(node) {
|
// TODO (namespaces) - pass through a real ent authz ctx
|
||||||
|
if rule.NodeRead(node, nil) == acl.Allow {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
a.logger.Printf("[DEBUG] agent: dropping node %q from result due to ACLs", node)
|
a.logger.Printf("[DEBUG] agent: dropping node %q from result due to ACLs", node)
|
||||||
|
@ -232,7 +245,8 @@ func (a *Agent) filterServices(token string, services *map[string]*structs.NodeS
|
||||||
|
|
||||||
// Filter out services based on the service policy.
|
// Filter out services based on the service policy.
|
||||||
for id, service := range *services {
|
for id, service := range *services {
|
||||||
if rule.ServiceRead(service.Service) {
|
// TODO (namespaces) - pass through a real ent authz ctx
|
||||||
|
if rule.ServiceRead(service.Service, nil) == acl.Allow {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
a.logger.Printf("[DEBUG] agent: dropping service %q from result due to ACLs", id)
|
a.logger.Printf("[DEBUG] agent: dropping service %q from result due to ACLs", id)
|
||||||
|
@ -255,11 +269,13 @@ func (a *Agent) filterChecks(token string, checks *map[types.CheckID]*structs.He
|
||||||
// Filter out checks based on the node or service policy.
|
// Filter out checks based on the node or service policy.
|
||||||
for id, check := range *checks {
|
for id, check := range *checks {
|
||||||
if len(check.ServiceName) > 0 {
|
if len(check.ServiceName) > 0 {
|
||||||
if rule.ServiceRead(check.ServiceName) {
|
// TODO (namespaces) - pass through a real ent authz ctx
|
||||||
|
if rule.ServiceRead(check.ServiceName, nil) == acl.Allow {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if rule.NodeRead(a.config.NodeName) {
|
// TODO (namespaces) - pass through a real ent authz ctx
|
||||||
|
if rule.NodeRead(a.config.NodeName, nil) == acl.Allow {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -110,7 +110,8 @@ func (s *HTTPServer) ACLRulesTranslate(resp http.ResponseWriter, req *http.Reque
|
||||||
}
|
}
|
||||||
// Should this require lesser permissions? Really the only reason to require authorization at all is
|
// Should this require lesser permissions? Really the only reason to require authorization at all is
|
||||||
// to prevent external entities from DoS Consul with repeated rule translation requests
|
// to prevent external entities from DoS Consul with repeated rule translation requests
|
||||||
if rule != nil && !rule.ACLRead() {
|
// TODO (namespaces) - pass through a real ent authz ctx
|
||||||
|
if rule != nil && rule.ACLRead(nil) != acl.Allow {
|
||||||
return nil, acl.ErrPermissionDenied
|
return nil, acl.ErrPermissionDenied
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -195,10 +195,10 @@ func TestACL_AgentMasterToken(t *testing.T) {
|
||||||
require.NotNil(t, authz)
|
require.NotNil(t, authz)
|
||||||
require.Nil(t, err)
|
require.Nil(t, err)
|
||||||
|
|
||||||
require.True(t, authz.AgentRead(a.config.NodeName))
|
require.Equal(t, acl.Allow, authz.AgentRead(a.config.NodeName, nil))
|
||||||
require.True(t, authz.AgentWrite(a.config.NodeName))
|
require.Equal(t, acl.Allow, authz.AgentWrite(a.config.NodeName, nil))
|
||||||
require.True(t, authz.NodeRead("foobarbaz"))
|
require.Equal(t, acl.Allow, authz.NodeRead("foobarbaz", nil))
|
||||||
require.False(t, authz.NodeWrite("foobarbaz", nil))
|
require.Equal(t, acl.Deny, authz.NodeWrite("foobarbaz", nil))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestACL_RootAuthorizersDenied(t *testing.T) {
|
func TestACL_RootAuthorizersDenied(t *testing.T) {
|
||||||
|
@ -225,7 +225,7 @@ func TestACL_RootAuthorizersDenied(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func authzFromPolicy(policy *acl.Policy) (acl.Authorizer, error) {
|
func authzFromPolicy(policy *acl.Policy) (acl.Authorizer, error) {
|
||||||
return acl.NewPolicyAuthorizer(acl.DenyAll(), []*acl.Policy{policy}, nil)
|
return acl.NewPolicyAuthorizerWithDefaults(acl.DenyAll(), []*acl.Policy{policy}, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
// catalogPolicy supplies some standard policies to help with testing the
|
// catalogPolicy supplies some standard policies to help with testing the
|
||||||
|
@ -235,32 +235,42 @@ func catalogPolicy(token string) (acl.Authorizer, error) {
|
||||||
|
|
||||||
case "node-ro":
|
case "node-ro":
|
||||||
return authzFromPolicy(&acl.Policy{
|
return authzFromPolicy(&acl.Policy{
|
||||||
NodePrefixes: []*acl.NodePolicy{
|
PolicyRules: acl.PolicyRules{
|
||||||
&acl.NodePolicy{Name: "Node", Policy: "read"},
|
NodePrefixes: []*acl.NodeRule{
|
||||||
|
&acl.NodeRule{Name: "Node", Policy: "read"},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
case "node-rw":
|
case "node-rw":
|
||||||
return authzFromPolicy(&acl.Policy{
|
return authzFromPolicy(&acl.Policy{
|
||||||
NodePrefixes: []*acl.NodePolicy{
|
PolicyRules: acl.PolicyRules{
|
||||||
&acl.NodePolicy{Name: "Node", Policy: "write"},
|
NodePrefixes: []*acl.NodeRule{
|
||||||
|
&acl.NodeRule{Name: "Node", Policy: "write"},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
case "service-ro":
|
case "service-ro":
|
||||||
return authzFromPolicy(&acl.Policy{
|
return authzFromPolicy(&acl.Policy{
|
||||||
ServicePrefixes: []*acl.ServicePolicy{
|
PolicyRules: acl.PolicyRules{
|
||||||
&acl.ServicePolicy{Name: "service", Policy: "read"},
|
ServicePrefixes: []*acl.ServiceRule{
|
||||||
|
&acl.ServiceRule{Name: "service", Policy: "read"},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
case "service-rw":
|
case "service-rw":
|
||||||
return authzFromPolicy(&acl.Policy{
|
return authzFromPolicy(&acl.Policy{
|
||||||
ServicePrefixes: []*acl.ServicePolicy{
|
PolicyRules: acl.PolicyRules{
|
||||||
&acl.ServicePolicy{Name: "service", Policy: "write"},
|
ServicePrefixes: []*acl.ServiceRule{
|
||||||
|
&acl.ServiceRule{Name: "service", Policy: "write"},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
case "other-rw":
|
case "other-rw":
|
||||||
return authzFromPolicy(&acl.Policy{
|
return authzFromPolicy(&acl.Policy{
|
||||||
ServicePrefixes: []*acl.ServicePolicy{
|
PolicyRules: acl.PolicyRules{
|
||||||
&acl.ServicePolicy{Name: "other", Policy: "write"},
|
ServicePrefixes: []*acl.ServiceRule{
|
||||||
|
&acl.ServiceRule{Name: "other", Policy: "write"},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
default:
|
default:
|
||||||
|
|
|
@ -48,7 +48,7 @@ func (s *HTTPServer) AgentSelf(resp http.ResponseWriter, req *http.Request) (int
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if rule != nil && !rule.AgentRead(s.agent.config.NodeName) {
|
if rule != nil && rule.AgentRead(s.agent.config.NodeName, nil) != acl.Allow {
|
||||||
return nil, acl.ErrPermissionDenied
|
return nil, acl.ErrPermissionDenied
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -101,7 +101,7 @@ func (s *HTTPServer) AgentMetrics(resp http.ResponseWriter, req *http.Request) (
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if rule != nil && !rule.AgentRead(s.agent.config.NodeName) {
|
if rule != nil && rule.AgentRead(s.agent.config.NodeName, nil) != acl.Allow {
|
||||||
return nil, acl.ErrPermissionDenied
|
return nil, acl.ErrPermissionDenied
|
||||||
}
|
}
|
||||||
if enablePrometheusOutput(req) {
|
if enablePrometheusOutput(req) {
|
||||||
|
@ -130,7 +130,7 @@ func (s *HTTPServer) AgentReload(resp http.ResponseWriter, req *http.Request) (i
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if rule != nil && !rule.AgentWrite(s.agent.config.NodeName) {
|
if rule != nil && rule.AgentWrite(s.agent.config.NodeName, nil) != acl.Allow {
|
||||||
return nil, acl.ErrPermissionDenied
|
return nil, acl.ErrPermissionDenied
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -281,7 +281,8 @@ func (s *HTTPServer) AgentService(resp http.ResponseWriter, req *http.Request) (
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", nil, err
|
return "", nil, err
|
||||||
}
|
}
|
||||||
if rule != nil && !rule.ServiceRead(svc.Service) {
|
// TODO (namespaces) - pass through a real ent authz ctx
|
||||||
|
if rule != nil && rule.ServiceRead(svc.Service, nil) != acl.Allow {
|
||||||
return "", nil, acl.ErrPermissionDenied
|
return "", nil, acl.ErrPermissionDenied
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -388,7 +389,7 @@ func (s *HTTPServer) AgentJoin(resp http.ResponseWriter, req *http.Request) (int
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if rule != nil && !rule.AgentWrite(s.agent.config.NodeName) {
|
if rule != nil && rule.AgentWrite(s.agent.config.NodeName, nil) != acl.Allow {
|
||||||
return nil, acl.ErrPermissionDenied
|
return nil, acl.ErrPermissionDenied
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -416,7 +417,7 @@ func (s *HTTPServer) AgentLeave(resp http.ResponseWriter, req *http.Request) (in
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if rule != nil && !rule.AgentWrite(s.agent.config.NodeName) {
|
if rule != nil && rule.AgentWrite(s.agent.config.NodeName, nil) != acl.Allow {
|
||||||
return nil, acl.ErrPermissionDenied
|
return nil, acl.ErrPermissionDenied
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -434,7 +435,7 @@ func (s *HTTPServer) AgentForceLeave(resp http.ResponseWriter, req *http.Request
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if rule != nil && !rule.AgentWrite(s.agent.config.NodeName) {
|
if rule != nil && rule.AgentWrite(s.agent.config.NodeName, nil) != acl.Allow {
|
||||||
return nil, acl.ErrPermissionDenied
|
return nil, acl.ErrPermissionDenied
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1049,7 +1050,8 @@ func (s *HTTPServer) AgentNodeMaintenance(resp http.ResponseWriter, req *http.Re
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if rule != nil && !rule.NodeWrite(s.agent.config.NodeName, nil) {
|
// TODO (namespaces) - pass through a real ent authz ctx?
|
||||||
|
if rule != nil && rule.NodeWrite(s.agent.config.NodeName, nil) != acl.Allow {
|
||||||
return nil, acl.ErrPermissionDenied
|
return nil, acl.ErrPermissionDenied
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1070,7 +1072,7 @@ func (s *HTTPServer) AgentMonitor(resp http.ResponseWriter, req *http.Request) (
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if rule != nil && !rule.AgentRead(s.agent.config.NodeName) {
|
if rule != nil && rule.AgentRead(s.agent.config.NodeName, nil) != acl.Allow {
|
||||||
return nil, acl.ErrPermissionDenied
|
return nil, acl.ErrPermissionDenied
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1165,7 +1167,7 @@ func (s *HTTPServer) AgentToken(resp http.ResponseWriter, req *http.Request) (in
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if rule != nil && !rule.AgentWrite(s.agent.config.NodeName) {
|
if rule != nil && rule.AgentWrite(s.agent.config.NodeName, nil) != acl.Allow {
|
||||||
return nil, acl.ErrPermissionDenied
|
return nil, acl.ErrPermissionDenied
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1370,7 +1372,8 @@ func (s *HTTPServer) AgentHost(resp http.ResponseWriter, req *http.Request) (int
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if rule != nil && !rule.OperatorRead() {
|
// TODO (namespaces) - pass through a real ent authz ctx
|
||||||
|
if rule != nil && rule.OperatorRead(nil) != acl.Allow {
|
||||||
return nil, acl.ErrPermissionDenied
|
return nil, acl.ErrPermissionDenied
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -57,7 +57,8 @@ func (a *Agent) ConnectAuthorize(token string,
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return returnErr(err)
|
return returnErr(err)
|
||||||
}
|
}
|
||||||
if rule != nil && !rule.ServiceWrite(req.Target, nil) {
|
// TODO (namespaces) - pass through a real ent authz ctx
|
||||||
|
if rule != nil && rule.ServiceWrite(req.Target, nil) != acl.Allow {
|
||||||
return returnErr(acl.ErrPermissionDenied)
|
return returnErr(acl.ErrPermissionDenied)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -115,5 +116,6 @@ func (a *Agent) ConnectAuthorize(token string,
|
||||||
return true, "ACLs disabled, access is allowed by default", &meta, nil
|
return true, "ACLs disabled, access is allowed by default", &meta, nil
|
||||||
}
|
}
|
||||||
reason = "Default behavior configured by ACLs"
|
reason = "Default behavior configured by ACLs"
|
||||||
return rule.IntentionDefaultAllow(), reason, &meta, nil
|
// TODO (namespaces) - pass through a real ent authz ctx
|
||||||
|
return rule.IntentionDefaultAllow(nil) == acl.Allow, reason, &meta, nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,8 +11,6 @@ import (
|
||||||
metrics "github.com/armon/go-metrics"
|
metrics "github.com/armon/go-metrics"
|
||||||
"github.com/hashicorp/consul/acl"
|
"github.com/hashicorp/consul/acl"
|
||||||
"github.com/hashicorp/consul/agent/structs"
|
"github.com/hashicorp/consul/agent/structs"
|
||||||
"github.com/hashicorp/consul/api"
|
|
||||||
"github.com/hashicorp/consul/sentinel"
|
|
||||||
"golang.org/x/sync/singleflight"
|
"golang.org/x/sync/singleflight"
|
||||||
"golang.org/x/time/rate"
|
"golang.org/x/time/rate"
|
||||||
)
|
)
|
||||||
|
@ -126,7 +124,8 @@ type ACLResolverConfig struct {
|
||||||
// so that it can detect when the servers have gotten ACLs enabled.
|
// so that it can detect when the servers have gotten ACLs enabled.
|
||||||
AutoDisable bool
|
AutoDisable bool
|
||||||
|
|
||||||
Sentinel sentinel.Evaluator
|
// EnterpriseACLConfig contains Consul Enterprise specific ACL configuration
|
||||||
|
EnterpriseConfig *acl.EnterpriseACLConfig
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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.
|
||||||
|
@ -159,7 +158,7 @@ type ACLResolver struct {
|
||||||
logger *log.Logger
|
logger *log.Logger
|
||||||
|
|
||||||
delegate ACLResolverDelegate
|
delegate ACLResolverDelegate
|
||||||
sentinel sentinel.Evaluator
|
entConf *acl.EnterpriseACLConfig
|
||||||
|
|
||||||
cache *structs.ACLCaches
|
cache *structs.ACLCaches
|
||||||
identityGroup singleflight.Group
|
identityGroup singleflight.Group
|
||||||
|
@ -212,7 +211,7 @@ func NewACLResolver(config *ACLResolverConfig) (*ACLResolver, error) {
|
||||||
config: config.Config,
|
config: config.Config,
|
||||||
logger: config.Logger,
|
logger: config.Logger,
|
||||||
delegate: config.Delegate,
|
delegate: config.Delegate,
|
||||||
sentinel: config.Sentinel,
|
entConf: config.EnterpriseConfig,
|
||||||
cache: cache,
|
cache: cache,
|
||||||
autoDisable: config.AutoDisable,
|
autoDisable: config.AutoDisable,
|
||||||
down: down,
|
down: down,
|
||||||
|
@ -249,7 +248,7 @@ func (r *ACLResolver) fetchAndCacheTokenLegacy(token string, cached *structs.Aut
|
||||||
policies = append(policies, policy.ConvertFromLegacy())
|
policies = append(policies, policy.ConvertFromLegacy())
|
||||||
}
|
}
|
||||||
|
|
||||||
authorizer, err := acl.NewPolicyAuthorizer(parent, policies, r.sentinel)
|
authorizer, err := acl.NewPolicyAuthorizerWithDefaults(parent, policies, r.entConf)
|
||||||
|
|
||||||
r.cache.PutAuthorizerWithTTL(token, authorizer, reply.TTL)
|
r.cache.PutAuthorizerWithTTL(token, authorizer, reply.TTL)
|
||||||
return authorizer, err
|
return authorizer, err
|
||||||
|
@ -292,7 +291,7 @@ func (r *ACLResolver) resolveTokenLegacy(token string) (acl.Authorizer, error) {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return policies.Compile(acl.RootAuthorizer(r.config.ACLDefaultPolicy), r.cache, r.sentinel)
|
return policies.Compile(acl.RootAuthorizer(r.config.ACLDefaultPolicy), r.cache, r.entConf)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -1005,7 +1004,7 @@ func (r *ACLResolver) ResolveToken(token string) (acl.Authorizer, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Build the Authorizer
|
// Build the Authorizer
|
||||||
authorizer, err := policies.Compile(acl.RootAuthorizer(r.config.ACLDefaultPolicy), r.cache, r.sentinel)
|
authorizer, err := policies.Compile(acl.RootAuthorizer(r.config.ACLDefaultPolicy), r.cache, r.entConf)
|
||||||
return authorizer, err
|
return authorizer, err
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1035,7 +1034,7 @@ func (r *ACLResolver) GetMergedPolicyForToken(token string) (*acl.Policy, error)
|
||||||
return nil, acl.ErrNotFound
|
return nil, acl.ErrNotFound
|
||||||
}
|
}
|
||||||
|
|
||||||
return policies.Merge(r.cache, r.sentinel)
|
return policies.Merge(r.cache, r.entConf)
|
||||||
}
|
}
|
||||||
|
|
||||||
// aclFilter is used to filter results from our state store based on ACL rules
|
// aclFilter is used to filter results from our state store based on ACL rules
|
||||||
|
@ -1059,15 +1058,15 @@ func newACLFilter(authorizer acl.Authorizer, logger *log.Logger, enforceVersion8
|
||||||
}
|
}
|
||||||
|
|
||||||
// allowNode is used to determine if a node is accessible for an ACL.
|
// allowNode is used to determine if a node is accessible for an ACL.
|
||||||
func (f *aclFilter) allowNode(node string) bool {
|
func (f *aclFilter) allowNode(node string, ent *acl.EnterpriseAuthorizerContext) bool {
|
||||||
if !f.enforceVersion8 {
|
if !f.enforceVersion8 {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
return f.authorizer.NodeRead(node)
|
return f.authorizer.NodeRead(node, ent) == acl.Allow
|
||||||
}
|
}
|
||||||
|
|
||||||
// allowService is used to determine if a service is accessible for an ACL.
|
// allowService is used to determine if a service is accessible for an ACL.
|
||||||
func (f *aclFilter) allowService(service string) bool {
|
func (f *aclFilter) allowService(service string, ent *acl.EnterpriseAuthorizerContext) bool {
|
||||||
if service == "" {
|
if service == "" {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
@ -1075,16 +1074,16 @@ func (f *aclFilter) allowService(service string) bool {
|
||||||
if !f.enforceVersion8 && service == structs.ConsulServiceID {
|
if !f.enforceVersion8 && service == structs.ConsulServiceID {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
return f.authorizer.ServiceRead(service)
|
return f.authorizer.ServiceRead(service, ent) == acl.Allow
|
||||||
}
|
}
|
||||||
|
|
||||||
// allowSession is used to determine if a session for a node is accessible for
|
// allowSession is used to determine if a session for a node is accessible for
|
||||||
// an ACL.
|
// an ACL.
|
||||||
func (f *aclFilter) allowSession(node string) bool {
|
func (f *aclFilter) allowSession(node string, ent *acl.EnterpriseAuthorizerContext) bool {
|
||||||
if !f.enforceVersion8 {
|
if !f.enforceVersion8 {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
return f.authorizer.SessionRead(node)
|
return f.authorizer.SessionRead(node, ent) == acl.Allow
|
||||||
}
|
}
|
||||||
|
|
||||||
// filterHealthChecks is used to filter a set of health checks down based on
|
// filterHealthChecks is used to filter a set of health checks down based on
|
||||||
|
@ -1093,7 +1092,8 @@ func (f *aclFilter) filterHealthChecks(checks *structs.HealthChecks) {
|
||||||
hc := *checks
|
hc := *checks
|
||||||
for i := 0; i < len(hc); i++ {
|
for i := 0; i < len(hc); i++ {
|
||||||
check := hc[i]
|
check := hc[i]
|
||||||
if f.allowNode(check.Node) && f.allowService(check.ServiceName) {
|
// TODO (namespaces) update to call with an actual ent authz context once the catalog supports it
|
||||||
|
if f.allowNode(check.Node, nil) && f.allowService(check.ServiceName, nil) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
f.logger.Printf("[DEBUG] consul: dropping check %q from result due to ACLs", check.CheckID)
|
f.logger.Printf("[DEBUG] consul: dropping check %q from result due to ACLs", check.CheckID)
|
||||||
|
@ -1106,7 +1106,8 @@ func (f *aclFilter) filterHealthChecks(checks *structs.HealthChecks) {
|
||||||
// filterServices is used to filter a set of services based on ACLs.
|
// filterServices is used to filter a set of services based on ACLs.
|
||||||
func (f *aclFilter) filterServices(services structs.Services) {
|
func (f *aclFilter) filterServices(services structs.Services) {
|
||||||
for svc := range services {
|
for svc := range services {
|
||||||
if f.allowService(svc) {
|
// TODO (namespaces) update to call with an actual ent authz context once the catalog supports it
|
||||||
|
if f.allowService(svc, nil) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
f.logger.Printf("[DEBUG] consul: dropping service %q from result due to ACLs", svc)
|
f.logger.Printf("[DEBUG] consul: dropping service %q from result due to ACLs", svc)
|
||||||
|
@ -1120,7 +1121,8 @@ func (f *aclFilter) filterServiceNodes(nodes *structs.ServiceNodes) {
|
||||||
sn := *nodes
|
sn := *nodes
|
||||||
for i := 0; i < len(sn); i++ {
|
for i := 0; i < len(sn); i++ {
|
||||||
node := sn[i]
|
node := sn[i]
|
||||||
if f.allowNode(node.Node) && f.allowService(node.ServiceName) {
|
// TODO (namespaces) update to call with an actual ent authz context once the catalog supports it
|
||||||
|
if f.allowNode(node.Node, nil) && f.allowService(node.ServiceName, nil) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
f.logger.Printf("[DEBUG] consul: dropping node %q from result due to ACLs", node.Node)
|
f.logger.Printf("[DEBUG] consul: dropping node %q from result due to ACLs", node.Node)
|
||||||
|
@ -1136,13 +1138,15 @@ func (f *aclFilter) filterNodeServices(services **structs.NodeServices) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if !f.allowNode((*services).Node.Node) {
|
// TODO (namespaces) update to call with an actual ent authz context once the catalog supports it
|
||||||
|
if !f.allowNode((*services).Node.Node, nil) {
|
||||||
*services = nil
|
*services = nil
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
for svc := range (*services).Services {
|
for svc := range (*services).Services {
|
||||||
if f.allowService(svc) {
|
// TODO (namespaces) update to call with an actual ent authz context once the catalog supports it
|
||||||
|
if f.allowService(svc, nil) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
f.logger.Printf("[DEBUG] consul: dropping service %q from result due to ACLs", svc)
|
f.logger.Printf("[DEBUG] consul: dropping service %q from result due to ACLs", svc)
|
||||||
|
@ -1155,7 +1159,8 @@ func (f *aclFilter) filterCheckServiceNodes(nodes *structs.CheckServiceNodes) {
|
||||||
csn := *nodes
|
csn := *nodes
|
||||||
for i := 0; i < len(csn); i++ {
|
for i := 0; i < len(csn); i++ {
|
||||||
node := csn[i]
|
node := csn[i]
|
||||||
if f.allowNode(node.Node.Node) && f.allowService(node.Service.Service) {
|
// TODO (namespaces) update to call with an actual ent authz context once the catalog supports it
|
||||||
|
if f.allowNode(node.Node.Node, nil) && f.allowService(node.Service.Service, nil) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
f.logger.Printf("[DEBUG] consul: dropping node %q from result due to ACLs", node.Node.Node)
|
f.logger.Printf("[DEBUG] consul: dropping node %q from result due to ACLs", node.Node.Node)
|
||||||
|
@ -1170,7 +1175,8 @@ func (f *aclFilter) filterSessions(sessions *structs.Sessions) {
|
||||||
s := *sessions
|
s := *sessions
|
||||||
for i := 0; i < len(s); i++ {
|
for i := 0; i < len(s); i++ {
|
||||||
session := s[i]
|
session := s[i]
|
||||||
if f.allowSession(session.Node) {
|
// TODO (namespaces) update to call with an actual ent authz context once sessions supports ns
|
||||||
|
if f.allowSession(session.Node, nil) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
f.logger.Printf("[DEBUG] consul: dropping session %q from result due to ACLs", session.ID)
|
f.logger.Printf("[DEBUG] consul: dropping session %q from result due to ACLs", session.ID)
|
||||||
|
@ -1186,7 +1192,8 @@ func (f *aclFilter) filterCoordinates(coords *structs.Coordinates) {
|
||||||
c := *coords
|
c := *coords
|
||||||
for i := 0; i < len(c); i++ {
|
for i := 0; i < len(c); i++ {
|
||||||
node := c[i].Node
|
node := c[i].Node
|
||||||
if f.allowNode(node) {
|
// TODO (namespaces) update to call with an actual ent authz context once the catalog supports it
|
||||||
|
if f.allowNode(node, nil) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
f.logger.Printf("[DEBUG] consul: dropping node %q from result due to ACLs", node)
|
f.logger.Printf("[DEBUG] consul: dropping node %q from result due to ACLs", node)
|
||||||
|
@ -1201,7 +1208,8 @@ func (f *aclFilter) filterCoordinates(coords *structs.Coordinates) {
|
||||||
// if the user doesn't have a management token.
|
// if the user doesn't have a management token.
|
||||||
func (f *aclFilter) filterIntentions(ixns *structs.Intentions) {
|
func (f *aclFilter) filterIntentions(ixns *structs.Intentions) {
|
||||||
// Management tokens can see everything with no filtering.
|
// Management tokens can see everything with no filtering.
|
||||||
if f.authorizer.ACLRead() {
|
// TODO (namespaces) update to call with an actual ent authz context once acls support it
|
||||||
|
if f.authorizer.ACLRead(nil) == acl.Allow {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1212,7 +1220,8 @@ func (f *aclFilter) filterIntentions(ixns *structs.Intentions) {
|
||||||
// we know at this point the user doesn't have a management
|
// we know at this point the user doesn't have a management
|
||||||
// token, otherwise see what the policy says.
|
// token, otherwise see what the policy says.
|
||||||
prefix, ok := ixn.GetACLPrefix()
|
prefix, ok := ixn.GetACLPrefix()
|
||||||
if !ok || !f.authorizer.IntentionRead(prefix) {
|
// TODO (namespaces) update to call with an actual ent authz context once the catalog supports it
|
||||||
|
if !ok || f.authorizer.IntentionRead(prefix, nil) != acl.Allow {
|
||||||
f.logger.Printf("[DEBUG] consul: dropping intention %q from result due to ACLs", ixn.ID)
|
f.logger.Printf("[DEBUG] consul: dropping intention %q from result due to ACLs", ixn.ID)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
@ -1231,7 +1240,8 @@ func (f *aclFilter) filterNodeDump(dump *structs.NodeDump) {
|
||||||
info := nd[i]
|
info := nd[i]
|
||||||
|
|
||||||
// Filter nodes
|
// Filter nodes
|
||||||
if node := info.Node; !f.allowNode(node) {
|
// TODO (namespaces) update to call with an actual ent authz context once the catalog supports it
|
||||||
|
if node := info.Node; !f.allowNode(node, nil) {
|
||||||
f.logger.Printf("[DEBUG] consul: dropping node %q from result due to ACLs", node)
|
f.logger.Printf("[DEBUG] consul: dropping node %q from result due to ACLs", node)
|
||||||
nd = append(nd[:i], nd[i+1:]...)
|
nd = append(nd[:i], nd[i+1:]...)
|
||||||
i--
|
i--
|
||||||
|
@ -1241,7 +1251,8 @@ func (f *aclFilter) filterNodeDump(dump *structs.NodeDump) {
|
||||||
// Filter services
|
// Filter services
|
||||||
for j := 0; j < len(info.Services); j++ {
|
for j := 0; j < len(info.Services); j++ {
|
||||||
svc := info.Services[j].Service
|
svc := info.Services[j].Service
|
||||||
if f.allowService(svc) {
|
// TODO (namespaces) update to call with an actual ent authz context once the catalog supports it
|
||||||
|
if f.allowService(svc, nil) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
f.logger.Printf("[DEBUG] consul: dropping service %q from result due to ACLs", svc)
|
f.logger.Printf("[DEBUG] consul: dropping service %q from result due to ACLs", svc)
|
||||||
|
@ -1252,7 +1263,8 @@ func (f *aclFilter) filterNodeDump(dump *structs.NodeDump) {
|
||||||
// Filter checks
|
// Filter checks
|
||||||
for j := 0; j < len(info.Checks); j++ {
|
for j := 0; j < len(info.Checks); j++ {
|
||||||
chk := info.Checks[j]
|
chk := info.Checks[j]
|
||||||
if f.allowService(chk.ServiceName) {
|
// TODO (namespaces) update to call with an actual ent authz context once the catalog supports it
|
||||||
|
if f.allowService(chk.ServiceName, nil) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
f.logger.Printf("[DEBUG] consul: dropping check %q from result due to ACLs", chk.CheckID)
|
f.logger.Printf("[DEBUG] consul: dropping check %q from result due to ACLs", chk.CheckID)
|
||||||
|
@ -1269,7 +1281,8 @@ func (f *aclFilter) filterNodes(nodes *structs.Nodes) {
|
||||||
n := *nodes
|
n := *nodes
|
||||||
for i := 0; i < len(n); i++ {
|
for i := 0; i < len(n); i++ {
|
||||||
node := n[i].Node
|
node := n[i].Node
|
||||||
if f.allowNode(node) {
|
// TODO (namespaces) update to call with an actual ent authz context once the catalog supports it
|
||||||
|
if f.allowNode(node, nil) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
f.logger.Printf("[DEBUG] consul: dropping node %q from result due to ACLs", node)
|
f.logger.Printf("[DEBUG] consul: dropping node %q from result due to ACLs", node)
|
||||||
|
@ -1287,7 +1300,8 @@ func (f *aclFilter) filterNodes(nodes *structs.Nodes) {
|
||||||
// captured tokens, but they can at least see whether or not a token is set.
|
// captured tokens, but they can at least see whether or not a token is set.
|
||||||
func (f *aclFilter) redactPreparedQueryTokens(query **structs.PreparedQuery) {
|
func (f *aclFilter) redactPreparedQueryTokens(query **structs.PreparedQuery) {
|
||||||
// Management tokens can see everything with no filtering.
|
// Management tokens can see everything with no filtering.
|
||||||
if f.authorizer.ACLWrite() {
|
// TODO (namespaces) update to call with an actual ent authz context once acls support it
|
||||||
|
if f.authorizer.ACLWrite(nil) == acl.Allow {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1312,7 +1326,11 @@ func (f *aclFilter) redactPreparedQueryTokens(query **structs.PreparedQuery) {
|
||||||
// if the user doesn't have a management token.
|
// if the user doesn't have a management token.
|
||||||
func (f *aclFilter) filterPreparedQueries(queries *structs.PreparedQueries) {
|
func (f *aclFilter) filterPreparedQueries(queries *structs.PreparedQueries) {
|
||||||
// Management tokens can see everything with no filtering.
|
// Management tokens can see everything with no filtering.
|
||||||
if f.authorizer.ACLWrite() {
|
// TODO (namespaces) update to call with an actual ent authz context once acls support it
|
||||||
|
// TODO (namespaces) is this check even necessary - this looks like a search replace from
|
||||||
|
// the 1.4 ACL rewrite. The global-management token will provide unrestricted query privileges
|
||||||
|
// so asking for ACLWrite should be unnecessary.
|
||||||
|
if f.authorizer.ACLWrite(nil) == acl.Allow {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1323,7 +1341,7 @@ func (f *aclFilter) filterPreparedQueries(queries *structs.PreparedQueries) {
|
||||||
// we know at this point the user doesn't have a management
|
// we know at this point the user doesn't have a management
|
||||||
// token, otherwise see what the policy says.
|
// token, otherwise see what the policy says.
|
||||||
prefix, ok := query.GetACLPrefix()
|
prefix, ok := query.GetACLPrefix()
|
||||||
if !ok || !f.authorizer.PreparedQueryRead(prefix) {
|
if !ok || f.authorizer.PreparedQueryRead(prefix, nil) != acl.Allow {
|
||||||
f.logger.Printf("[DEBUG] consul: dropping prepared query %q from result due to ACLs", query.ID)
|
f.logger.Printf("[DEBUG] consul: dropping prepared query %q from result due to ACLs", query.ID)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
@ -1338,7 +1356,8 @@ func (f *aclFilter) filterPreparedQueries(queries *structs.PreparedQueries) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *aclFilter) redactTokenSecret(token **structs.ACLToken) {
|
func (f *aclFilter) redactTokenSecret(token **structs.ACLToken) {
|
||||||
if token == nil || *token == nil || f == nil || f.authorizer.ACLWrite() {
|
// TODO (namespaces) update to call with an actual ent authz context once acls support it
|
||||||
|
if token == nil || *token == nil || f == nil || f.authorizer.ACLWrite(nil) == acl.Allow {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
clone := *(*token)
|
clone := *(*token)
|
||||||
|
@ -1454,46 +1473,49 @@ func vetRegisterWithACL(rule acl.Authorizer, subj *structs.RegisterRequest,
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO (namespaces) update to create a sentinel scope - technically we never check this
|
||||||
|
// scope but we used to set it so we probably should continue?
|
||||||
// This gets called potentially from a few spots so we save it and
|
// This gets called potentially from a few spots so we save it and
|
||||||
// return the structure we made if we have it.
|
// return the structure we made if we have it.
|
||||||
var memo map[string]interface{}
|
// var memo map[string]interface{}
|
||||||
scope := func() map[string]interface{} {
|
// scope := func() map[string]interface{} {
|
||||||
if memo != nil {
|
// if memo != nil {
|
||||||
return memo
|
// return memo
|
||||||
}
|
// }
|
||||||
|
|
||||||
node := &api.Node{
|
// node := &api.Node{
|
||||||
ID: string(subj.ID),
|
// ID: string(subj.ID),
|
||||||
Node: subj.Node,
|
// Node: subj.Node,
|
||||||
Address: subj.Address,
|
// Address: subj.Address,
|
||||||
Datacenter: subj.Datacenter,
|
// Datacenter: subj.Datacenter,
|
||||||
TaggedAddresses: subj.TaggedAddresses,
|
// TaggedAddresses: subj.TaggedAddresses,
|
||||||
Meta: subj.NodeMeta,
|
// Meta: subj.NodeMeta,
|
||||||
}
|
// }
|
||||||
|
|
||||||
var service *api.AgentService
|
// var service *api.AgentService
|
||||||
if subj.Service != nil {
|
// if subj.Service != nil {
|
||||||
service = &api.AgentService{
|
// service = &api.AgentService{
|
||||||
ID: subj.Service.ID,
|
// ID: subj.Service.ID,
|
||||||
Service: subj.Service.Service,
|
// Service: subj.Service.Service,
|
||||||
Tags: subj.Service.Tags,
|
// Tags: subj.Service.Tags,
|
||||||
Meta: subj.Service.Meta,
|
// Meta: subj.Service.Meta,
|
||||||
Address: subj.Service.Address,
|
// Address: subj.Service.Address,
|
||||||
Port: subj.Service.Port,
|
// Port: subj.Service.Port,
|
||||||
EnableTagOverride: subj.Service.EnableTagOverride,
|
// EnableTagOverride: subj.Service.EnableTagOverride,
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
memo = sentinel.ScopeCatalogUpsert(node, service)
|
// memo = sentinel.ScopeCatalogUpsert(node, service)
|
||||||
return memo
|
// return memo
|
||||||
}
|
// }
|
||||||
|
|
||||||
// Vet the node info. This allows service updates to re-post the required
|
// Vet the node info. This allows service updates to re-post the required
|
||||||
// node info for each request without having to have node "write"
|
// node info for each request without having to have node "write"
|
||||||
// privileges.
|
// privileges.
|
||||||
needsNode := ns == nil || subj.ChangesNode(ns.Node)
|
needsNode := ns == nil || subj.ChangesNode(ns.Node)
|
||||||
|
|
||||||
if needsNode && !rule.NodeWrite(subj.Node, scope) {
|
// TODO (namespaces) update to call with an actual ent authz context once the catalog supports it
|
||||||
|
if needsNode && rule.NodeWrite(subj.Node, nil) != acl.Allow {
|
||||||
return acl.ErrPermissionDenied
|
return acl.ErrPermissionDenied
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1501,7 +1523,8 @@ func vetRegisterWithACL(rule acl.Authorizer, subj *structs.RegisterRequest,
|
||||||
// the given service, and that we can write to any existing service that
|
// the given service, and that we can write to any existing service that
|
||||||
// is being modified by id (if any).
|
// is being modified by id (if any).
|
||||||
if subj.Service != nil {
|
if subj.Service != nil {
|
||||||
if !rule.ServiceWrite(subj.Service.Service, scope) {
|
// TODO (namespaces) update to call with an actual ent authz context once the catalog supports it
|
||||||
|
if rule.ServiceWrite(subj.Service.Service, nil) != acl.Allow {
|
||||||
return acl.ErrPermissionDenied
|
return acl.ErrPermissionDenied
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1511,7 +1534,8 @@ func vetRegisterWithACL(rule acl.Authorizer, subj *structs.RegisterRequest,
|
||||||
// This is effectively a delete, so we DO NOT apply the
|
// This is effectively a delete, so we DO NOT apply the
|
||||||
// sentinel scope to the service we are overwriting, just
|
// sentinel scope to the service we are overwriting, just
|
||||||
// the regular ACL policy.
|
// the regular ACL policy.
|
||||||
if ok && !rule.ServiceWrite(other.Service, nil) {
|
// TODO (namespaces) update to call with an actual ent authz context once the catalog supports it
|
||||||
|
if ok && rule.ServiceWrite(other.Service, nil) != acl.Allow {
|
||||||
return acl.ErrPermissionDenied
|
return acl.ErrPermissionDenied
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1540,7 +1564,8 @@ func vetRegisterWithACL(rule acl.Authorizer, subj *structs.RegisterRequest,
|
||||||
|
|
||||||
// Node-level check.
|
// Node-level check.
|
||||||
if check.ServiceID == "" {
|
if check.ServiceID == "" {
|
||||||
if !rule.NodeWrite(subj.Node, scope) {
|
// TODO (namespaces) update to call with an actual ent authz context once the catalog supports it
|
||||||
|
if rule.NodeWrite(subj.Node, nil) != acl.Allow {
|
||||||
return acl.ErrPermissionDenied
|
return acl.ErrPermissionDenied
|
||||||
}
|
}
|
||||||
continue
|
continue
|
||||||
|
@ -1568,7 +1593,7 @@ func vetRegisterWithACL(rule acl.Authorizer, subj *structs.RegisterRequest,
|
||||||
// We are only adding a check here, so we don't add the scope,
|
// We are only adding a check here, so we don't add the scope,
|
||||||
// since the sentinel policy doesn't apply to adding checks at
|
// since the sentinel policy doesn't apply to adding checks at
|
||||||
// this time.
|
// this time.
|
||||||
if !rule.ServiceWrite(other.Service, nil) {
|
if rule.ServiceWrite(other.Service, nil) != acl.Allow {
|
||||||
return acl.ErrPermissionDenied
|
return acl.ErrPermissionDenied
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1595,7 +1620,8 @@ func vetDeregisterWithACL(rule acl.Authorizer, subj *structs.DeregisterRequest,
|
||||||
// Allow service deregistration if the token has write permission for the node.
|
// Allow service deregistration if the token has write permission for the node.
|
||||||
// This accounts for cases where the agent no longer has a token with write permission
|
// This accounts for cases where the agent no longer has a token with write permission
|
||||||
// on the service to deregister it.
|
// on the service to deregister it.
|
||||||
if rule.NodeWrite(subj.Node, nil) {
|
// TODO (namespaces) update to call with an actual ent authz context once the catalog supports it
|
||||||
|
if rule.NodeWrite(subj.Node, nil) == acl.Allow {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1607,7 +1633,8 @@ func vetDeregisterWithACL(rule acl.Authorizer, subj *structs.DeregisterRequest,
|
||||||
if ns == nil {
|
if ns == nil {
|
||||||
return fmt.Errorf("Unknown service '%s'", subj.ServiceID)
|
return fmt.Errorf("Unknown service '%s'", subj.ServiceID)
|
||||||
}
|
}
|
||||||
if !rule.ServiceWrite(ns.Service, nil) {
|
// TODO (namespaces) update to call with an actual ent authz context once the catalog supports it
|
||||||
|
if rule.ServiceWrite(ns.Service, nil) != acl.Allow {
|
||||||
return acl.ErrPermissionDenied
|
return acl.ErrPermissionDenied
|
||||||
}
|
}
|
||||||
} else if subj.CheckID != "" {
|
} else if subj.CheckID != "" {
|
||||||
|
@ -1615,11 +1642,13 @@ func vetDeregisterWithACL(rule acl.Authorizer, subj *structs.DeregisterRequest,
|
||||||
return fmt.Errorf("Unknown check '%s'", subj.CheckID)
|
return fmt.Errorf("Unknown check '%s'", subj.CheckID)
|
||||||
}
|
}
|
||||||
if nc.ServiceID != "" {
|
if nc.ServiceID != "" {
|
||||||
if !rule.ServiceWrite(nc.ServiceName, nil) {
|
// TODO (namespaces) update to call with an actual ent authz context once the catalog supports it
|
||||||
|
if rule.ServiceWrite(nc.ServiceName, nil) != acl.Allow {
|
||||||
return acl.ErrPermissionDenied
|
return acl.ErrPermissionDenied
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if !rule.NodeWrite(subj.Node, nil) {
|
// TODO (namespaces) update to call with an actual ent authz context once the catalog supports it
|
||||||
|
if rule.NodeWrite(subj.Node, nil) != acl.Allow {
|
||||||
return acl.ErrPermissionDenied
|
return acl.ErrPermissionDenied
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1641,24 +1670,27 @@ func vetNodeTxnOp(op *structs.TxnNodeOp, rule acl.Authorizer) error {
|
||||||
|
|
||||||
node := op.Node
|
node := op.Node
|
||||||
|
|
||||||
n := &api.Node{
|
// TODO (namespaces) uncomment once we bring back sentinel scope creation in the authz ctx
|
||||||
Node: node.Node,
|
// n := &api.Node{
|
||||||
ID: string(node.ID),
|
// Node: node.Node,
|
||||||
Address: node.Address,
|
// ID: string(node.ID),
|
||||||
Datacenter: node.Datacenter,
|
// Address: node.Address,
|
||||||
TaggedAddresses: node.TaggedAddresses,
|
// Datacenter: node.Datacenter,
|
||||||
Meta: node.Meta,
|
// TaggedAddresses: node.TaggedAddresses,
|
||||||
}
|
// Meta: node.Meta,
|
||||||
|
// }
|
||||||
|
|
||||||
|
// TODO (namespaces) update to create a authz context with a scope once the catalog supports it
|
||||||
// Sentinel doesn't apply to deletes, only creates/updates, so we don't need the scopeFn.
|
// Sentinel doesn't apply to deletes, only creates/updates, so we don't need the scopeFn.
|
||||||
var scope func() map[string]interface{}
|
// var scope func() map[string]interface{}
|
||||||
if op.Verb != api.NodeDelete && op.Verb != api.NodeDeleteCAS {
|
// if op.Verb != api.NodeDelete && op.Verb != api.NodeDeleteCAS {
|
||||||
scope = func() map[string]interface{} {
|
// scope = func() map[string]interface{} {
|
||||||
return sentinel.ScopeCatalogUpsert(n, nil)
|
// return sentinel.ScopeCatalogUpsert(n, nil)
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
if rule != nil && !rule.NodeWrite(node.Node, scope) {
|
// TODO (namespaces) update to call with an actual ent authz context once the catalog supports it
|
||||||
|
if rule != nil && rule.NodeWrite(node.Node, nil) != acl.Allow {
|
||||||
return acl.ErrPermissionDenied
|
return acl.ErrPermissionDenied
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1674,23 +1706,25 @@ func vetServiceTxnOp(op *structs.TxnServiceOp, rule acl.Authorizer) error {
|
||||||
|
|
||||||
service := op.Service
|
service := op.Service
|
||||||
|
|
||||||
n := &api.Node{Node: op.Node}
|
// TODO (namespaces) update to create authz context with the sentinel scope
|
||||||
svc := &api.AgentService{
|
// n := &api.Node{Node: op.Node}
|
||||||
ID: service.ID,
|
// svc := &api.AgentService{
|
||||||
Service: service.Service,
|
// ID: service.ID,
|
||||||
Tags: service.Tags,
|
// Service: service.Service,
|
||||||
Meta: service.Meta,
|
// Tags: service.Tags,
|
||||||
Address: service.Address,
|
// Meta: service.Meta,
|
||||||
Port: service.Port,
|
// Address: service.Address,
|
||||||
EnableTagOverride: service.EnableTagOverride,
|
// Port: service.Port,
|
||||||
}
|
// EnableTagOverride: service.EnableTagOverride,
|
||||||
var scope func() map[string]interface{}
|
// }
|
||||||
if op.Verb != api.ServiceDelete && op.Verb != api.ServiceDeleteCAS {
|
// var scope func() map[string]interface{}
|
||||||
scope = func() map[string]interface{} {
|
// if op.Verb != api.ServiceDelete && op.Verb != api.ServiceDeleteCAS {
|
||||||
return sentinel.ScopeCatalogUpsert(n, svc)
|
// scope = func() map[string]interface{} {
|
||||||
}
|
// return sentinel.ScopeCatalogUpsert(n, svc)
|
||||||
}
|
// }
|
||||||
if !rule.ServiceWrite(service.Service, scope) {
|
// }
|
||||||
|
// TODO (namespaces) update to call with an actual ent authz context once the catalog supports it
|
||||||
|
if rule.ServiceWrite(service.Service, nil) != acl.Allow {
|
||||||
return acl.ErrPermissionDenied
|
return acl.ErrPermissionDenied
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1704,31 +1738,36 @@ func vetCheckTxnOp(op *structs.TxnCheckOp, rule acl.Authorizer) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
n := &api.Node{Node: op.Check.Node}
|
// TODO (namespaces) uncomment once these are used for sentinel scope creation
|
||||||
svc := &api.AgentService{
|
// n := &api.Node{Node: op.Check.Node}
|
||||||
ID: op.Check.ServiceID,
|
// svc := &api.AgentService{
|
||||||
Service: op.Check.ServiceID,
|
// ID: op.Check.ServiceID,
|
||||||
Tags: op.Check.ServiceTags,
|
// Service: op.Check.ServiceID,
|
||||||
}
|
// Tags: op.Check.ServiceTags,
|
||||||
var scope func() map[string]interface{}
|
// }
|
||||||
|
// var scope func() map[string]interface{}
|
||||||
if op.Check.ServiceID == "" {
|
if op.Check.ServiceID == "" {
|
||||||
// Node-level check.
|
// Node-level check.
|
||||||
if op.Verb == api.CheckDelete || op.Verb == api.CheckDeleteCAS {
|
// TODO (namespaces) update to create authz with sentinel scope
|
||||||
scope = func() map[string]interface{} {
|
// if op.Verb == api.CheckDelete || op.Verb == api.CheckDeleteCAS {
|
||||||
return sentinel.ScopeCatalogUpsert(n, svc)
|
// scope = func() map[string]interface{} {
|
||||||
}
|
// return sentinel.ScopeCatalogUpsert(n, svc)
|
||||||
}
|
// }
|
||||||
if !rule.NodeWrite(op.Check.Node, scope) {
|
// }
|
||||||
|
// TODO (namespaces) update to call with an actual ent authz context once the catalog supports it
|
||||||
|
if rule.NodeWrite(op.Check.Node, nil) != acl.Allow {
|
||||||
return acl.ErrPermissionDenied
|
return acl.ErrPermissionDenied
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Service-level check.
|
// Service-level check.
|
||||||
if op.Verb == api.CheckDelete || op.Verb == api.CheckDeleteCAS {
|
// TODO (namespaces) update to create authz with sentinel scope
|
||||||
scope = func() map[string]interface{} {
|
// if op.Verb == api.CheckDelete || op.Verb == api.CheckDeleteCAS {
|
||||||
return sentinel.ScopeCatalogUpsert(n, svc)
|
// scope = func() map[string]interface{} {
|
||||||
}
|
// return sentinel.ScopeCatalogUpsert(n, svc)
|
||||||
}
|
// }
|
||||||
if !rule.ServiceWrite(op.Check.ServiceName, scope) {
|
// }
|
||||||
|
// TODO (namespaces) update to call with an actual ent authz context once the catalog supports it
|
||||||
|
if rule.ServiceWrite(op.Check.ServiceName, nil) != acl.Allow {
|
||||||
return acl.ErrPermissionDenied
|
return acl.ErrPermissionDenied
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -206,9 +206,10 @@ func (a *ACL) TokenRead(args *structs.ACLTokenGetRequest, reply *structs.ACLToke
|
||||||
// Only ACLRead privileges are required to list tokens
|
// Only ACLRead privileges are required to list tokens
|
||||||
// However if you do not have ACLWrite as well the token
|
// However if you do not have ACLWrite as well the token
|
||||||
// secrets will be redacted
|
// secrets will be redacted
|
||||||
|
// TODO (namespaces) update to call ACLRead with an authz context once ACLs support it
|
||||||
if rule, err = a.srv.ResolveToken(args.Token); err != nil {
|
if rule, err = a.srv.ResolveToken(args.Token); err != nil {
|
||||||
return err
|
return err
|
||||||
} else if rule == nil || !rule.ACLRead() {
|
} else if rule == nil || rule.ACLRead(nil) != acl.Allow {
|
||||||
return acl.ErrPermissionDenied
|
return acl.ErrPermissionDenied
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -223,7 +224,8 @@ func (a *ACL) TokenRead(args *structs.ACLTokenGetRequest, reply *structs.ACLToke
|
||||||
index, token, err = state.ACLTokenGetByAccessor(ws, args.TokenID)
|
index, token, err = state.ACLTokenGetByAccessor(ws, args.TokenID)
|
||||||
if token != nil {
|
if token != nil {
|
||||||
a.srv.filterACLWithAuthorizer(rule, &token)
|
a.srv.filterACLWithAuthorizer(rule, &token)
|
||||||
if !rule.ACLWrite() {
|
// TODO (namespaces) update to call ACLWrite with an authz context once ACLs support it
|
||||||
|
if rule.ACLWrite(nil) != acl.Allow {
|
||||||
reply.Redacted = true
|
reply.Redacted = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -261,9 +263,10 @@ func (a *ACL) TokenClone(args *structs.ACLTokenSetRequest, reply *structs.ACLTok
|
||||||
|
|
||||||
defer metrics.MeasureSince([]string{"acl", "token", "clone"}, time.Now())
|
defer metrics.MeasureSince([]string{"acl", "token", "clone"}, time.Now())
|
||||||
|
|
||||||
|
// TODO (namespaces) update to call ACLWrite with an authz context once ACLs support it
|
||||||
if rule, err := a.srv.ResolveToken(args.Token); err != nil {
|
if rule, err := a.srv.ResolveToken(args.Token); err != nil {
|
||||||
return err
|
return err
|
||||||
} else if rule == nil || !rule.ACLWrite() {
|
} else if rule == nil || rule.ACLWrite(nil) != acl.Allow {
|
||||||
return acl.ErrPermissionDenied
|
return acl.ErrPermissionDenied
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -324,9 +327,10 @@ func (a *ACL) TokenSet(args *structs.ACLTokenSetRequest, reply *structs.ACLToken
|
||||||
defer metrics.MeasureSince([]string{"acl", "token", "upsert"}, time.Now())
|
defer metrics.MeasureSince([]string{"acl", "token", "upsert"}, time.Now())
|
||||||
|
|
||||||
// Verify token is permitted to modify ACLs
|
// Verify token is permitted to modify ACLs
|
||||||
|
// TODO (namespaces) update to call ACLWrite with an authz context once ACLs support it
|
||||||
if rule, err := a.srv.ResolveToken(args.Token); err != nil {
|
if rule, err := a.srv.ResolveToken(args.Token); err != nil {
|
||||||
return err
|
return err
|
||||||
} else if rule == nil || !rule.ACLWrite() {
|
} else if rule == nil || rule.ACLWrite(nil) != acl.Allow {
|
||||||
return acl.ErrPermissionDenied
|
return acl.ErrPermissionDenied
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -683,9 +687,10 @@ func (a *ACL) TokenDelete(args *structs.ACLTokenDeleteRequest, reply *string) er
|
||||||
defer metrics.MeasureSince([]string{"acl", "token", "delete"}, time.Now())
|
defer metrics.MeasureSince([]string{"acl", "token", "delete"}, time.Now())
|
||||||
|
|
||||||
// Verify token is permitted to modify ACLs
|
// Verify token is permitted to modify ACLs
|
||||||
|
// TODO (namespaces) update to call ACLWrite with an authz context once ACLs support it
|
||||||
if rule, err := a.srv.ResolveToken(args.Token); err != nil {
|
if rule, err := a.srv.ResolveToken(args.Token); err != nil {
|
||||||
return err
|
return err
|
||||||
} else if rule == nil || !rule.ACLWrite() {
|
} else if rule == nil || rule.ACLWrite(nil) != acl.Allow {
|
||||||
return acl.ErrPermissionDenied
|
return acl.ErrPermissionDenied
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -760,9 +765,10 @@ func (a *ACL) TokenList(args *structs.ACLTokenListRequest, reply *structs.ACLTok
|
||||||
}
|
}
|
||||||
|
|
||||||
rule, err := a.srv.ResolveToken(args.Token)
|
rule, err := a.srv.ResolveToken(args.Token)
|
||||||
|
// TODO (namespaces) update to call ACLRead with an authz context once ACLs support it
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
} else if rule == nil || !rule.ACLRead() {
|
} else if rule == nil || rule.ACLRead(nil) != acl.Allow {
|
||||||
return acl.ErrPermissionDenied
|
return acl.ErrPermissionDenied
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -801,9 +807,10 @@ func (a *ACL) TokenBatchRead(args *structs.ACLTokenBatchGetRequest, reply *struc
|
||||||
}
|
}
|
||||||
|
|
||||||
rule, err := a.srv.ResolveToken(args.Token)
|
rule, err := a.srv.ResolveToken(args.Token)
|
||||||
|
// TODO (namespaces) update to call ACLRead with an authz context once ACLs support it
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
} else if rule == nil || !rule.ACLRead() {
|
} else if rule == nil || rule.ACLRead(nil) != acl.Allow {
|
||||||
return acl.ErrPermissionDenied
|
return acl.ErrPermissionDenied
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -819,7 +826,8 @@ func (a *ACL) TokenBatchRead(args *structs.ACLTokenBatchGetRequest, reply *struc
|
||||||
a.srv.filterACLWithAuthorizer(rule, &tokens)
|
a.srv.filterACLWithAuthorizer(rule, &tokens)
|
||||||
|
|
||||||
reply.Index, reply.Tokens = index, tokens
|
reply.Index, reply.Tokens = index, tokens
|
||||||
reply.Redacted = !rule.ACLWrite()
|
// TODO (namespaces) update to call ACLWrite with an authz context once ACLs support it
|
||||||
|
reply.Redacted = rule.ACLWrite(nil) != acl.Allow
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -835,7 +843,7 @@ func (a *ACL) PolicyRead(args *structs.ACLPolicyGetRequest, reply *structs.ACLPo
|
||||||
|
|
||||||
if rule, err := a.srv.ResolveToken(args.Token); err != nil {
|
if rule, err := a.srv.ResolveToken(args.Token); err != nil {
|
||||||
return err
|
return err
|
||||||
} else if rule == nil || !rule.ACLRead() {
|
} else if rule == nil || rule.ACLRead(nil) != acl.Allow {
|
||||||
return acl.ErrPermissionDenied
|
return acl.ErrPermissionDenied
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -863,7 +871,7 @@ func (a *ACL) PolicyBatchRead(args *structs.ACLPolicyBatchGetRequest, reply *str
|
||||||
|
|
||||||
if rule, err := a.srv.ResolveToken(args.Token); err != nil {
|
if rule, err := a.srv.ResolveToken(args.Token); err != nil {
|
||||||
return err
|
return err
|
||||||
} else if rule == nil || !rule.ACLRead() {
|
} else if rule == nil || rule.ACLRead(nil) != acl.Allow {
|
||||||
return acl.ErrPermissionDenied
|
return acl.ErrPermissionDenied
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -895,9 +903,10 @@ func (a *ACL) PolicySet(args *structs.ACLPolicySetRequest, reply *structs.ACLPol
|
||||||
defer metrics.MeasureSince([]string{"acl", "policy", "upsert"}, time.Now())
|
defer metrics.MeasureSince([]string{"acl", "policy", "upsert"}, time.Now())
|
||||||
|
|
||||||
// Verify token is permitted to modify ACLs
|
// Verify token is permitted to modify ACLs
|
||||||
|
// TODO (namespaces) update to call ACLWrite with an authz context once ACLs support it
|
||||||
if rule, err := a.srv.ResolveToken(args.Token); err != nil {
|
if rule, err := a.srv.ResolveToken(args.Token); err != nil {
|
||||||
return err
|
return err
|
||||||
} else if rule == nil || !rule.ACLWrite() {
|
} else if rule == nil || rule.ACLWrite(nil) != acl.Allow {
|
||||||
return acl.ErrPermissionDenied
|
return acl.ErrPermissionDenied
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -971,7 +980,7 @@ func (a *ACL) PolicySet(args *structs.ACLPolicySetRequest, reply *structs.ACLPol
|
||||||
}
|
}
|
||||||
|
|
||||||
// validate the rules
|
// validate the rules
|
||||||
_, err = acl.NewPolicyFromSource("", 0, policy.Rules, policy.Syntax, a.srv.sentinel)
|
_, err = acl.NewPolicyFromSource("", 0, policy.Rules, policy.Syntax, a.srv.enterpriseACLConfig)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -1018,9 +1027,10 @@ func (a *ACL) PolicyDelete(args *structs.ACLPolicyDeleteRequest, reply *string)
|
||||||
defer metrics.MeasureSince([]string{"acl", "policy", "delete"}, time.Now())
|
defer metrics.MeasureSince([]string{"acl", "policy", "delete"}, time.Now())
|
||||||
|
|
||||||
// Verify token is permitted to modify ACLs
|
// Verify token is permitted to modify ACLs
|
||||||
|
// TODO (namespaces) update to call ACLWrite with an authz context once ACLs support it
|
||||||
if rule, err := a.srv.ResolveToken(args.Token); err != nil {
|
if rule, err := a.srv.ResolveToken(args.Token); err != nil {
|
||||||
return err
|
return err
|
||||||
} else if rule == nil || !rule.ACLWrite() {
|
} else if rule == nil || rule.ACLWrite(nil) != acl.Allow {
|
||||||
return acl.ErrPermissionDenied
|
return acl.ErrPermissionDenied
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1068,9 +1078,10 @@ func (a *ACL) PolicyList(args *structs.ACLPolicyListRequest, reply *structs.ACLP
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO (namespaces) update to call ACLRead with an authz context once ACLs support it
|
||||||
if rule, err := a.srv.ResolveToken(args.Token); err != nil {
|
if rule, err := a.srv.ResolveToken(args.Token); err != nil {
|
||||||
return err
|
return err
|
||||||
} else if rule == nil || !rule.ACLRead() {
|
} else if rule == nil || rule.ACLRead(nil) != acl.Allow {
|
||||||
return acl.ErrPermissionDenied
|
return acl.ErrPermissionDenied
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1213,9 +1224,10 @@ func (a *ACL) RoleRead(args *structs.ACLRoleGetRequest, reply *structs.ACLRoleRe
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO (namespaces) update to create and use actual enterprise authorizer context
|
||||||
if rule, err := a.srv.ResolveToken(args.Token); err != nil {
|
if rule, err := a.srv.ResolveToken(args.Token); err != nil {
|
||||||
return err
|
return err
|
||||||
} else if rule == nil || !rule.ACLRead() {
|
} else if rule == nil || rule.ACLRead(nil) != acl.Allow {
|
||||||
return acl.ErrPermissionDenied
|
return acl.ErrPermissionDenied
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1250,9 +1262,10 @@ func (a *ACL) RoleBatchRead(args *structs.ACLRoleBatchGetRequest, reply *structs
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO (namespaces) update to create and use actual enterprise authorizer context
|
||||||
if rule, err := a.srv.ResolveToken(args.Token); err != nil {
|
if rule, err := a.srv.ResolveToken(args.Token); err != nil {
|
||||||
return err
|
return err
|
||||||
} else if rule == nil || !rule.ACLRead() {
|
} else if rule == nil || rule.ACLRead(nil) != acl.Allow {
|
||||||
return acl.ErrPermissionDenied
|
return acl.ErrPermissionDenied
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1284,9 +1297,10 @@ func (a *ACL) RoleSet(args *structs.ACLRoleSetRequest, reply *structs.ACLRole) e
|
||||||
defer metrics.MeasureSince([]string{"acl", "role", "upsert"}, time.Now())
|
defer metrics.MeasureSince([]string{"acl", "role", "upsert"}, time.Now())
|
||||||
|
|
||||||
// Verify token is permitted to modify ACLs
|
// Verify token is permitted to modify ACLs
|
||||||
|
// TODO (namespaces) update to call ACLWrite with an authz context once ACLs support it
|
||||||
if rule, err := a.srv.ResolveToken(args.Token); err != nil {
|
if rule, err := a.srv.ResolveToken(args.Token); err != nil {
|
||||||
return err
|
return err
|
||||||
} else if rule == nil || !rule.ACLWrite() {
|
} else if rule == nil || rule.ACLWrite(nil) != acl.Allow {
|
||||||
return acl.ErrPermissionDenied
|
return acl.ErrPermissionDenied
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1422,9 +1436,10 @@ func (a *ACL) RoleDelete(args *structs.ACLRoleDeleteRequest, reply *string) erro
|
||||||
defer metrics.MeasureSince([]string{"acl", "role", "delete"}, time.Now())
|
defer metrics.MeasureSince([]string{"acl", "role", "delete"}, time.Now())
|
||||||
|
|
||||||
// Verify token is permitted to modify ACLs
|
// Verify token is permitted to modify ACLs
|
||||||
|
// TODO (namespaces) update to call ACLWrite with an authz context once ACLs support it
|
||||||
if rule, err := a.srv.ResolveToken(args.Token); err != nil {
|
if rule, err := a.srv.ResolveToken(args.Token); err != nil {
|
||||||
return err
|
return err
|
||||||
} else if rule == nil || !rule.ACLWrite() {
|
} else if rule == nil || rule.ACLWrite(nil) != acl.Allow {
|
||||||
return acl.ErrPermissionDenied
|
return acl.ErrPermissionDenied
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1468,9 +1483,10 @@ func (a *ACL) RoleList(args *structs.ACLRoleListRequest, reply *structs.ACLRoleL
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO (namespaces) update to call ACLRead with an authz context once ACLs support it
|
||||||
if rule, err := a.srv.ResolveToken(args.Token); err != nil {
|
if rule, err := a.srv.ResolveToken(args.Token); err != nil {
|
||||||
return err
|
return err
|
||||||
} else if rule == nil || !rule.ACLRead() {
|
} else if rule == nil || rule.ACLRead(nil) != acl.Allow {
|
||||||
return acl.ErrPermissionDenied
|
return acl.ErrPermissionDenied
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1544,9 +1560,10 @@ func (a *ACL) BindingRuleRead(args *structs.ACLBindingRuleGetRequest, reply *str
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO (namespaces) update to call ACLRead with an authz context once ACLs support it
|
||||||
if rule, err := a.srv.ResolveToken(args.Token); err != nil {
|
if rule, err := a.srv.ResolveToken(args.Token); err != nil {
|
||||||
return err
|
return err
|
||||||
} else if rule == nil || !rule.ACLRead() {
|
} else if rule == nil || rule.ACLRead(nil) != acl.Allow {
|
||||||
return acl.ErrPermissionDenied
|
return acl.ErrPermissionDenied
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1579,9 +1596,10 @@ func (a *ACL) BindingRuleSet(args *structs.ACLBindingRuleSetRequest, reply *stru
|
||||||
defer metrics.MeasureSince([]string{"acl", "bindingrule", "upsert"}, time.Now())
|
defer metrics.MeasureSince([]string{"acl", "bindingrule", "upsert"}, time.Now())
|
||||||
|
|
||||||
// Verify token is permitted to modify ACLs
|
// Verify token is permitted to modify ACLs
|
||||||
|
// TODO (namespaces) update to call ACLWrite with an authz context once ACLs support it
|
||||||
if rule, err := a.srv.ResolveToken(args.Token); err != nil {
|
if rule, err := a.srv.ResolveToken(args.Token); err != nil {
|
||||||
return err
|
return err
|
||||||
} else if rule == nil || !rule.ACLWrite() {
|
} else if rule == nil || rule.ACLWrite(nil) != acl.Allow {
|
||||||
return acl.ErrPermissionDenied
|
return acl.ErrPermissionDenied
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1696,9 +1714,10 @@ func (a *ACL) BindingRuleDelete(args *structs.ACLBindingRuleDeleteRequest, reply
|
||||||
defer metrics.MeasureSince([]string{"acl", "bindingrule", "delete"}, time.Now())
|
defer metrics.MeasureSince([]string{"acl", "bindingrule", "delete"}, time.Now())
|
||||||
|
|
||||||
// Verify token is permitted to modify ACLs
|
// Verify token is permitted to modify ACLs
|
||||||
|
// TODO (namespaces) update to call ACLWrite with an authz context once ACLs support it
|
||||||
if rule, err := a.srv.ResolveToken(args.Token); err != nil {
|
if rule, err := a.srv.ResolveToken(args.Token); err != nil {
|
||||||
return err
|
return err
|
||||||
} else if rule == nil || !rule.ACLWrite() {
|
} else if rule == nil || rule.ACLWrite(nil) != acl.Allow {
|
||||||
return acl.ErrPermissionDenied
|
return acl.ErrPermissionDenied
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1742,9 +1761,10 @@ func (a *ACL) BindingRuleList(args *structs.ACLBindingRuleListRequest, reply *st
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO (namespaces) update to call ACLRead with an authz context once ACLs support it
|
||||||
if rule, err := a.srv.ResolveToken(args.Token); err != nil {
|
if rule, err := a.srv.ResolveToken(args.Token); err != nil {
|
||||||
return err
|
return err
|
||||||
} else if rule == nil || !rule.ACLRead() {
|
} else if rule == nil || rule.ACLRead(nil) != acl.Allow {
|
||||||
return acl.ErrPermissionDenied
|
return acl.ErrPermissionDenied
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1773,9 +1793,10 @@ func (a *ACL) AuthMethodRead(args *structs.ACLAuthMethodGetRequest, reply *struc
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO (namespaces) update to call ACLRead with an authz context once ACLs support it
|
||||||
if rule, err := a.srv.ResolveToken(args.Token); err != nil {
|
if rule, err := a.srv.ResolveToken(args.Token); err != nil {
|
||||||
return err
|
return err
|
||||||
} else if rule == nil || !rule.ACLRead() {
|
} else if rule == nil || rule.ACLRead(nil) != acl.Allow {
|
||||||
return acl.ErrPermissionDenied
|
return acl.ErrPermissionDenied
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1808,9 +1829,10 @@ func (a *ACL) AuthMethodSet(args *structs.ACLAuthMethodSetRequest, reply *struct
|
||||||
defer metrics.MeasureSince([]string{"acl", "authmethod", "upsert"}, time.Now())
|
defer metrics.MeasureSince([]string{"acl", "authmethod", "upsert"}, time.Now())
|
||||||
|
|
||||||
// Verify token is permitted to modify ACLs
|
// Verify token is permitted to modify ACLs
|
||||||
|
// TODO (namespaces) update to call ACLWrite with an authz context once ACLs support it
|
||||||
if rule, err := a.srv.ResolveToken(args.Token); err != nil {
|
if rule, err := a.srv.ResolveToken(args.Token); err != nil {
|
||||||
return err
|
return err
|
||||||
} else if rule == nil || !rule.ACLWrite() {
|
} else if rule == nil || rule.ACLWrite(nil) != acl.Allow {
|
||||||
return acl.ErrPermissionDenied
|
return acl.ErrPermissionDenied
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1885,9 +1907,10 @@ func (a *ACL) AuthMethodDelete(args *structs.ACLAuthMethodDeleteRequest, reply *
|
||||||
defer metrics.MeasureSince([]string{"acl", "authmethod", "delete"}, time.Now())
|
defer metrics.MeasureSince([]string{"acl", "authmethod", "delete"}, time.Now())
|
||||||
|
|
||||||
// Verify token is permitted to modify ACLs
|
// Verify token is permitted to modify ACLs
|
||||||
|
// TODO (namespaces) update to call ACLWrite with an authz context once ACLs support it
|
||||||
if rule, err := a.srv.ResolveToken(args.Token); err != nil {
|
if rule, err := a.srv.ResolveToken(args.Token); err != nil {
|
||||||
return err
|
return err
|
||||||
} else if rule == nil || !rule.ACLWrite() {
|
} else if rule == nil || rule.ACLWrite(nil) != acl.Allow {
|
||||||
return acl.ErrPermissionDenied
|
return acl.ErrPermissionDenied
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1931,9 +1954,10 @@ func (a *ACL) AuthMethodList(args *structs.ACLAuthMethodListRequest, reply *stru
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO (namespaces) update to call ACLRead with an authz context once ACLs support it
|
||||||
if rule, err := a.srv.ResolveToken(args.Token); err != nil {
|
if rule, err := a.srv.ResolveToken(args.Token); err != nil {
|
||||||
return err
|
return err
|
||||||
} else if rule == nil || !rule.ACLRead() {
|
} else if rule == nil || rule.ACLRead(nil) != acl.Allow {
|
||||||
return acl.ErrPermissionDenied
|
return acl.ErrPermissionDenied
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -114,7 +114,7 @@ func aclApplyInternal(srv *Server, args *structs.ACLRequest, reply *string) erro
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate the rules compile
|
// Validate the rules compile
|
||||||
_, err := acl.NewPolicyFromSource("", 0, args.ACL.Rules, acl.SyntaxLegacy, srv.sentinel)
|
_, err := acl.NewPolicyFromSource("", 0, args.ACL.Rules, acl.SyntaxLegacy, srv.enterpriseACLConfig)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("ACL rule compilation failed: %v", err)
|
return fmt.Errorf("ACL rule compilation failed: %v", err)
|
||||||
}
|
}
|
||||||
|
@ -160,9 +160,10 @@ func (a *ACL) Apply(args *structs.ACLRequest, reply *string) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Verify token is permitted to modify ACLs
|
// Verify token is permitted to modify ACLs
|
||||||
|
// NOTE: We will not support enterprise authorizer contexts with legacy ACLs
|
||||||
if rule, err := a.srv.ResolveToken(args.Token); err != nil {
|
if rule, err := a.srv.ResolveToken(args.Token); err != nil {
|
||||||
return err
|
return err
|
||||||
} else if rule == nil || !rule.ACLWrite() {
|
} else if rule == nil || rule.ACLWrite(nil) != acl.Allow {
|
||||||
return acl.ErrPermissionDenied
|
return acl.ErrPermissionDenied
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -198,6 +199,10 @@ func (a *ACL) Get(args *structs.ACLSpecificRequest,
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NOTE: This has no ACL check because legacy ACLs were managed with
|
||||||
|
// the secrets and therefore the argument to the Get request is
|
||||||
|
// authorization in and of itself.
|
||||||
|
|
||||||
// Verify we are allowed to serve this request
|
// Verify we are allowed to serve this request
|
||||||
if !a.srv.ACLsEnabled() {
|
if !a.srv.ACLsEnabled() {
|
||||||
return acl.ErrDisabled
|
return acl.ErrDisabled
|
||||||
|
@ -246,9 +251,11 @@ func (a *ACL) List(args *structs.DCSpecificRequest,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Verify token is permitted to list ACLs
|
// Verify token is permitted to list ACLs
|
||||||
|
// NOTES: Previously with legacy ACL there was no read-only ACL permissions
|
||||||
|
// and this check for ACLWrite is basically what it did before.
|
||||||
if rule, err := a.srv.ResolveToken(args.Token); err != nil {
|
if rule, err := a.srv.ResolveToken(args.Token); err != nil {
|
||||||
return err
|
return err
|
||||||
} else if rule == nil || !rule.ACLWrite() {
|
} else if rule == nil || rule.ACLWrite(nil) != acl.Allow {
|
||||||
return acl.ErrPermissionDenied
|
return acl.ErrPermissionDenied
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -212,7 +212,7 @@ func TestACLEndpoint_Update_PurgeCache(t *testing.T) {
|
||||||
if acl1 == nil {
|
if acl1 == nil {
|
||||||
t.Fatalf("should not be nil")
|
t.Fatalf("should not be nil")
|
||||||
}
|
}
|
||||||
if !acl1.KeyRead("foo") {
|
if acl1.KeyRead("foo", nil) != acl.Allow {
|
||||||
t.Fatalf("should be allowed")
|
t.Fatalf("should be allowed")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -234,7 +234,7 @@ func TestACLEndpoint_Update_PurgeCache(t *testing.T) {
|
||||||
if acl2 == acl1 {
|
if acl2 == acl1 {
|
||||||
t.Fatalf("should not be cached")
|
t.Fatalf("should not be cached")
|
||||||
}
|
}
|
||||||
if acl2.KeyRead("foo") {
|
if acl2.KeyRead("foo", nil) == acl.Allow {
|
||||||
t.Fatalf("should not be allowed")
|
t.Fatalf("should not be allowed")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,13 @@
|
||||||
|
// +build !consulent
|
||||||
|
|
||||||
|
package consul
|
||||||
|
|
||||||
|
import (
|
||||||
|
"log"
|
||||||
|
|
||||||
|
"github.com/hashicorp/consul/acl"
|
||||||
|
)
|
||||||
|
|
||||||
|
func newEnterpriseACLConfig(*log.Logger) *acl.EnterpriseACLConfig {
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -764,7 +764,7 @@ func TestACLResolver_DownPolicy(t *testing.T) {
|
||||||
authz, err := r.ResolveToken("found")
|
authz, err := r.ResolveToken("found")
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NotNil(t, authz)
|
require.NotNil(t, authz)
|
||||||
require.True(t, authz.NodeWrite("foo", nil))
|
require.Equal(t, acl.Allow, authz.NodeWrite("foo", nil))
|
||||||
|
|
||||||
requirePolicyCached(t, r, "node-wr", true, "cached") // from "found" token
|
requirePolicyCached(t, r, "node-wr", true, "cached") // from "found" token
|
||||||
requirePolicyCached(t, r, "dc2-key-wr", true, "cached") // from "found" token
|
requirePolicyCached(t, r, "dc2-key-wr", true, "cached") // from "found" token
|
||||||
|
@ -774,7 +774,7 @@ func TestACLResolver_DownPolicy(t *testing.T) {
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NotNil(t, authz2)
|
require.NotNil(t, authz2)
|
||||||
require.False(t, authz == authz2)
|
require.False(t, authz == authz2)
|
||||||
require.False(t, authz2.NodeWrite("foo", nil))
|
require.Equal(t, acl.Deny, authz2.NodeWrite("foo", nil))
|
||||||
|
|
||||||
requirePolicyCached(t, r, "node-wr", false, "expired") // from "found" token
|
requirePolicyCached(t, r, "node-wr", false, "expired") // from "found" token
|
||||||
requirePolicyCached(t, r, "dc2-key-wr", false, "expired") // from "found" token
|
requirePolicyCached(t, r, "dc2-key-wr", false, "expired") // from "found" token
|
||||||
|
@ -802,14 +802,14 @@ func TestACLResolver_DownPolicy(t *testing.T) {
|
||||||
authz, err := r.ResolveToken("found-role")
|
authz, err := r.ResolveToken("found-role")
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NotNil(t, authz)
|
require.NotNil(t, authz)
|
||||||
require.True(t, authz.NodeWrite("foo", nil))
|
require.Equal(t, acl.Allow, authz.NodeWrite("foo", nil))
|
||||||
|
|
||||||
// role cache expired - so we will fail to resolve that role and use the default policy only
|
// role cache expired - so we will fail to resolve that role and use the default policy only
|
||||||
authz2, err := r.ResolveToken("found-role")
|
authz2, err := r.ResolveToken("found-role")
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NotNil(t, authz2)
|
require.NotNil(t, authz2)
|
||||||
require.False(t, authz == authz2)
|
require.False(t, authz == authz2)
|
||||||
require.False(t, authz2.NodeWrite("foo", nil))
|
require.Equal(t, acl.Deny, authz2.NodeWrite("foo", nil))
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("Extend-Cache-Policy", func(t *testing.T) {
|
t.Run("Extend-Cache-Policy", func(t *testing.T) {
|
||||||
|
@ -832,7 +832,7 @@ func TestACLResolver_DownPolicy(t *testing.T) {
|
||||||
authz, err := r.ResolveToken("found")
|
authz, err := r.ResolveToken("found")
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NotNil(t, authz)
|
require.NotNil(t, authz)
|
||||||
require.True(t, authz.NodeWrite("foo", nil))
|
require.Equal(t, acl.Allow, authz.NodeWrite("foo", nil))
|
||||||
|
|
||||||
requireIdentityCached(t, r, "found", true, "cached")
|
requireIdentityCached(t, r, "found", true, "cached")
|
||||||
|
|
||||||
|
@ -841,7 +841,7 @@ func TestACLResolver_DownPolicy(t *testing.T) {
|
||||||
require.NotNil(t, authz2)
|
require.NotNil(t, authz2)
|
||||||
// testing pointer equality - these will be the same object because it is cached.
|
// testing pointer equality - these will be the same object because it is cached.
|
||||||
require.True(t, authz == authz2)
|
require.True(t, authz == authz2)
|
||||||
require.True(t, authz2.NodeWrite("foo", nil))
|
require.Equal(t, acl.Allow, authz2.NodeWrite("foo", nil))
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("Extend-Cache-Role", func(t *testing.T) {
|
t.Run("Extend-Cache-Role", func(t *testing.T) {
|
||||||
|
@ -864,7 +864,7 @@ func TestACLResolver_DownPolicy(t *testing.T) {
|
||||||
authz, err := r.ResolveToken("found-role")
|
authz, err := r.ResolveToken("found-role")
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NotNil(t, authz)
|
require.NotNil(t, authz)
|
||||||
require.True(t, authz.NodeWrite("foo", nil))
|
require.Equal(t, acl.Allow, authz.NodeWrite("foo", nil))
|
||||||
|
|
||||||
requireIdentityCached(t, r, "found-role", true, "still cached")
|
requireIdentityCached(t, r, "found-role", true, "still cached")
|
||||||
|
|
||||||
|
@ -873,7 +873,7 @@ func TestACLResolver_DownPolicy(t *testing.T) {
|
||||||
require.NotNil(t, authz2)
|
require.NotNil(t, authz2)
|
||||||
// testing pointer equality - these will be the same object because it is cached.
|
// testing pointer equality - these will be the same object because it is cached.
|
||||||
require.True(t, authz == authz2)
|
require.True(t, authz == authz2)
|
||||||
require.True(t, authz2.NodeWrite("foo", nil))
|
require.Equal(t, acl.Allow, authz2.NodeWrite("foo", nil))
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("Extend-Cache-Expired-Policy", func(t *testing.T) {
|
t.Run("Extend-Cache-Expired-Policy", func(t *testing.T) {
|
||||||
|
@ -897,7 +897,7 @@ func TestACLResolver_DownPolicy(t *testing.T) {
|
||||||
authz, err := r.ResolveToken("found")
|
authz, err := r.ResolveToken("found")
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NotNil(t, authz)
|
require.NotNil(t, authz)
|
||||||
require.True(t, authz.NodeWrite("foo", nil))
|
require.Equal(t, acl.Allow, authz.NodeWrite("foo", nil))
|
||||||
|
|
||||||
requirePolicyCached(t, r, "node-wr", true, "cached") // from "found" token
|
requirePolicyCached(t, r, "node-wr", true, "cached") // from "found" token
|
||||||
requirePolicyCached(t, r, "dc2-key-wr", true, "cached") // from "found" token
|
requirePolicyCached(t, r, "dc2-key-wr", true, "cached") // from "found" token
|
||||||
|
@ -907,7 +907,7 @@ func TestACLResolver_DownPolicy(t *testing.T) {
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NotNil(t, authz2)
|
require.NotNil(t, authz2)
|
||||||
require.True(t, authz == authz2)
|
require.True(t, authz == authz2)
|
||||||
require.True(t, authz.NodeWrite("foo", nil))
|
require.Equal(t, acl.Allow, authz.NodeWrite("foo", nil))
|
||||||
|
|
||||||
requirePolicyCached(t, r, "node-wr", true, "still cached") // from "found" token
|
requirePolicyCached(t, r, "node-wr", true, "still cached") // from "found" token
|
||||||
requirePolicyCached(t, r, "dc2-key-wr", true, "still cached") // from "found" token
|
requirePolicyCached(t, r, "dc2-key-wr", true, "still cached") // from "found" token
|
||||||
|
@ -935,14 +935,14 @@ func TestACLResolver_DownPolicy(t *testing.T) {
|
||||||
authz, err := r.ResolveToken("found-role")
|
authz, err := r.ResolveToken("found-role")
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NotNil(t, authz)
|
require.NotNil(t, authz)
|
||||||
require.True(t, authz.NodeWrite("foo", nil))
|
require.Equal(t, acl.Allow, authz.NodeWrite("foo", nil))
|
||||||
|
|
||||||
// Will just use the policy cache
|
// Will just use the policy cache
|
||||||
authz2, err := r.ResolveToken("found-role")
|
authz2, err := r.ResolveToken("found-role")
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NotNil(t, authz2)
|
require.NotNil(t, authz2)
|
||||||
require.True(t, authz == authz2)
|
require.True(t, authz == authz2)
|
||||||
require.True(t, authz.NodeWrite("foo", nil))
|
require.Equal(t, acl.Allow, authz.NodeWrite("foo", nil))
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("Async-Cache-Expired-Policy", func(t *testing.T) {
|
t.Run("Async-Cache-Expired-Policy", func(t *testing.T) {
|
||||||
|
@ -968,7 +968,7 @@ func TestACLResolver_DownPolicy(t *testing.T) {
|
||||||
authz, err := r.ResolveToken("found")
|
authz, err := r.ResolveToken("found")
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NotNil(t, authz)
|
require.NotNil(t, authz)
|
||||||
require.True(t, authz.NodeWrite("foo", nil))
|
require.Equal(t, acl.Allow, authz.NodeWrite("foo", nil))
|
||||||
|
|
||||||
requirePolicyCached(t, r, "node-wr", true, "cached") // from "found" token
|
requirePolicyCached(t, r, "node-wr", true, "cached") // from "found" token
|
||||||
requirePolicyCached(t, r, "dc2-key-wr", true, "cached") // from "found" token
|
requirePolicyCached(t, r, "dc2-key-wr", true, "cached") // from "found" token
|
||||||
|
@ -979,7 +979,7 @@ func TestACLResolver_DownPolicy(t *testing.T) {
|
||||||
require.NotNil(t, authz2)
|
require.NotNil(t, authz2)
|
||||||
// testing pointer equality - these will be the same object because it is cached.
|
// testing pointer equality - these will be the same object because it is cached.
|
||||||
require.True(t, authz == authz2)
|
require.True(t, authz == authz2)
|
||||||
require.True(t, authz.NodeWrite("foo", nil))
|
require.Equal(t, acl.Allow, authz.NodeWrite("foo", nil))
|
||||||
|
|
||||||
requirePolicyCached(t, r, "node-wr", true, "cached") // from "found" token
|
requirePolicyCached(t, r, "node-wr", true, "cached") // from "found" token
|
||||||
requirePolicyCached(t, r, "dc2-key-wr", true, "cached") // from "found" token
|
requirePolicyCached(t, r, "dc2-key-wr", true, "cached") // from "found" token
|
||||||
|
@ -989,7 +989,7 @@ func TestACLResolver_DownPolicy(t *testing.T) {
|
||||||
authz3, err := r.ResolveToken("found")
|
authz3, err := r.ResolveToken("found")
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.NotNil(t, authz3)
|
assert.NotNil(t, authz3)
|
||||||
assert.False(t, authz3.NodeWrite("foo", nil))
|
assert.Equal(t, acl.Deny, authz3.NodeWrite("foo", nil))
|
||||||
})
|
})
|
||||||
|
|
||||||
requirePolicyCached(t, r, "node-wr", false, "no longer cached") // from "found" token
|
requirePolicyCached(t, r, "node-wr", false, "no longer cached") // from "found" token
|
||||||
|
@ -1020,7 +1020,7 @@ func TestACLResolver_DownPolicy(t *testing.T) {
|
||||||
authz, err := r.ResolveToken("found-role")
|
authz, err := r.ResolveToken("found-role")
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NotNil(t, authz)
|
require.NotNil(t, authz)
|
||||||
require.True(t, authz.NodeWrite("foo", nil))
|
require.Equal(t, acl.Allow, authz.NodeWrite("foo", nil))
|
||||||
|
|
||||||
// The identity should have been cached so this should still be valid
|
// The identity should have been cached so this should still be valid
|
||||||
authz2, err := r.ResolveToken("found-role")
|
authz2, err := r.ResolveToken("found-role")
|
||||||
|
@ -1028,14 +1028,14 @@ func TestACLResolver_DownPolicy(t *testing.T) {
|
||||||
require.NotNil(t, authz2)
|
require.NotNil(t, authz2)
|
||||||
// testing pointer equality - these will be the same object because it is cached.
|
// testing pointer equality - these will be the same object because it is cached.
|
||||||
require.True(t, authz == authz2)
|
require.True(t, authz == authz2)
|
||||||
require.True(t, authz.NodeWrite("foo", nil))
|
require.Equal(t, acl.Allow, authz.NodeWrite("foo", nil))
|
||||||
|
|
||||||
// the go routine spawned will eventually return with a authz that doesn't have the policy
|
// the go routine spawned will eventually return with a authz that doesn't have the policy
|
||||||
retry.Run(t, func(t *retry.R) {
|
retry.Run(t, func(t *retry.R) {
|
||||||
authz3, err := r.ResolveToken("found-role")
|
authz3, err := r.ResolveToken("found-role")
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.NotNil(t, authz3)
|
assert.NotNil(t, authz3)
|
||||||
assert.False(t, authz3.NodeWrite("foo", nil))
|
assert.Equal(t, acl.Deny, authz3.NodeWrite("foo", nil))
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -1062,7 +1062,7 @@ func TestACLResolver_DownPolicy(t *testing.T) {
|
||||||
authz, err := r.ResolveToken("found")
|
authz, err := r.ResolveToken("found")
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NotNil(t, authz)
|
require.NotNil(t, authz)
|
||||||
require.True(t, authz.NodeWrite("foo", nil))
|
require.Equal(t, acl.Allow, authz.NodeWrite("foo", nil))
|
||||||
|
|
||||||
requirePolicyCached(t, r, "node-wr", true, "cached") // from "found" token
|
requirePolicyCached(t, r, "node-wr", true, "cached") // from "found" token
|
||||||
requirePolicyCached(t, r, "dc2-key-wr", true, "cached") // from "found" token
|
requirePolicyCached(t, r, "dc2-key-wr", true, "cached") // from "found" token
|
||||||
|
@ -1072,7 +1072,7 @@ func TestACLResolver_DownPolicy(t *testing.T) {
|
||||||
require.NotNil(t, authz2)
|
require.NotNil(t, authz2)
|
||||||
// testing pointer equality - these will be the same object because it is cached.
|
// testing pointer equality - these will be the same object because it is cached.
|
||||||
require.True(t, authz == authz2)
|
require.True(t, authz == authz2)
|
||||||
require.True(t, authz2.NodeWrite("foo", nil))
|
require.Equal(t, acl.Allow, authz2.NodeWrite("foo", nil))
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("Extend-Cache-Client-Role", func(t *testing.T) {
|
t.Run("Extend-Cache-Client-Role", func(t *testing.T) {
|
||||||
|
@ -1099,7 +1099,7 @@ func TestACLResolver_DownPolicy(t *testing.T) {
|
||||||
authz, err := r.ResolveToken("found-role")
|
authz, err := r.ResolveToken("found-role")
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NotNil(t, authz)
|
require.NotNil(t, authz)
|
||||||
require.True(t, authz.NodeWrite("foo", nil))
|
require.Equal(t, acl.Allow, authz.NodeWrite("foo", nil))
|
||||||
|
|
||||||
requirePolicyCached(t, r, "node-wr", true, "still cached") // from "found" token
|
requirePolicyCached(t, r, "node-wr", true, "still cached") // from "found" token
|
||||||
requirePolicyCached(t, r, "dc2-key-wr", true, "still cached") // from "found" token
|
requirePolicyCached(t, r, "dc2-key-wr", true, "still cached") // from "found" token
|
||||||
|
@ -1109,7 +1109,7 @@ func TestACLResolver_DownPolicy(t *testing.T) {
|
||||||
require.NotNil(t, authz2)
|
require.NotNil(t, authz2)
|
||||||
// testing pointer equality - these will be the same object because it is cached.
|
// testing pointer equality - these will be the same object because it is cached.
|
||||||
require.True(t, authz == authz2, "\n[1]={%+v} != \n[2]={%+v}", authz, authz2)
|
require.True(t, authz == authz2, "\n[1]={%+v} != \n[2]={%+v}", authz, authz2)
|
||||||
require.True(t, authz2.NodeWrite("foo", nil))
|
require.Equal(t, acl.Allow, authz2.NodeWrite("foo", nil))
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("Async-Cache", func(t *testing.T) {
|
t.Run("Async-Cache", func(t *testing.T) {
|
||||||
|
@ -1132,7 +1132,7 @@ func TestACLResolver_DownPolicy(t *testing.T) {
|
||||||
authz, err := r.ResolveToken("found")
|
authz, err := r.ResolveToken("found")
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NotNil(t, authz)
|
require.NotNil(t, authz)
|
||||||
require.True(t, authz.NodeWrite("foo", nil))
|
require.Equal(t, acl.Allow, authz.NodeWrite("foo", nil))
|
||||||
|
|
||||||
requireIdentityCached(t, r, "found", true, "cached")
|
requireIdentityCached(t, r, "found", true, "cached")
|
||||||
|
|
||||||
|
@ -1142,7 +1142,7 @@ func TestACLResolver_DownPolicy(t *testing.T) {
|
||||||
require.NotNil(t, authz2)
|
require.NotNil(t, authz2)
|
||||||
// testing pointer equality - these will be the same object because it is cached.
|
// testing pointer equality - these will be the same object because it is cached.
|
||||||
require.True(t, authz == authz2)
|
require.True(t, authz == authz2)
|
||||||
require.True(t, authz2.NodeWrite("foo", nil))
|
require.Equal(t, acl.Allow, authz2.NodeWrite("foo", nil))
|
||||||
|
|
||||||
requireIdentityCached(t, r, "found", true, "cached")
|
requireIdentityCached(t, r, "found", true, "cached")
|
||||||
|
|
||||||
|
@ -1205,7 +1205,7 @@ func TestACLResolver_DownPolicy(t *testing.T) {
|
||||||
authz, err := r.ResolveToken(secretID)
|
authz, err := r.ResolveToken(secretID)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NotNil(t, authz)
|
require.NotNil(t, authz)
|
||||||
require.True(t, authz.NodeWrite("foo", nil))
|
require.Equal(t, acl.Allow, authz.NodeWrite("foo", nil))
|
||||||
|
|
||||||
// Verify that the caches are setup properly.
|
// Verify that the caches are setup properly.
|
||||||
requireIdentityCached(t, r, secretID, true, "cached")
|
requireIdentityCached(t, r, secretID, true, "cached")
|
||||||
|
@ -1267,7 +1267,7 @@ func TestACLResolver_DownPolicy(t *testing.T) {
|
||||||
authz, err := r.ResolveToken(secretID)
|
authz, err := r.ResolveToken(secretID)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NotNil(t, authz)
|
require.NotNil(t, authz)
|
||||||
require.True(t, authz.NodeWrite("foo", nil))
|
require.Equal(t, acl.Allow, authz.NodeWrite("foo", nil))
|
||||||
|
|
||||||
// Verify that the caches are setup properly.
|
// Verify that the caches are setup properly.
|
||||||
requireIdentityCached(t, r, secretID, true, "cached")
|
requireIdentityCached(t, r, secretID, true, "cached")
|
||||||
|
@ -1304,9 +1304,9 @@ func TestACLResolver_DatacenterScoping(t *testing.T) {
|
||||||
authz, err := r.ResolveToken("found")
|
authz, err := r.ResolveToken("found")
|
||||||
require.NotNil(t, authz)
|
require.NotNil(t, authz)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.False(t, authz.ACLRead())
|
require.Equal(t, acl.Deny, authz.ACLRead(nil))
|
||||||
require.True(t, authz.NodeWrite("foo", nil))
|
require.Equal(t, acl.Allow, authz.NodeWrite("foo", nil))
|
||||||
require.False(t, authz.KeyWrite("foo", nil))
|
require.Equal(t, acl.Deny, authz.KeyWrite("foo", nil))
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("dc2", func(t *testing.T) {
|
t.Run("dc2", func(t *testing.T) {
|
||||||
|
@ -1326,9 +1326,9 @@ func TestACLResolver_DatacenterScoping(t *testing.T) {
|
||||||
authz, err := r.ResolveToken("found")
|
authz, err := r.ResolveToken("found")
|
||||||
require.NotNil(t, authz)
|
require.NotNil(t, authz)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.False(t, authz.ACLRead())
|
require.Equal(t, acl.Deny, authz.ACLRead(nil))
|
||||||
require.False(t, authz.NodeWrite("foo", nil))
|
require.Equal(t, acl.Deny, authz.NodeWrite("foo", nil))
|
||||||
require.True(t, authz.KeyWrite("foo", nil))
|
require.Equal(t, acl.Allow, authz.KeyWrite("foo", nil))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1403,8 +1403,8 @@ func TestACLResolver_Client(t *testing.T) {
|
||||||
authz, err := r.ResolveToken("a1a54629-5050-4d17-8a4e-560d2423f835")
|
authz, err := r.ResolveToken("a1a54629-5050-4d17-8a4e-560d2423f835")
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NotNil(t, authz)
|
require.NotNil(t, authz)
|
||||||
require.True(t, authz.NodeWrite("foo", nil))
|
require.Equal(t, acl.Allow, authz.NodeWrite("foo", nil))
|
||||||
require.False(t, authz.ACLRead())
|
require.Equal(t, acl.Deny, authz.ACLRead(nil))
|
||||||
require.True(t, modified)
|
require.True(t, modified)
|
||||||
require.True(t, deleted)
|
require.True(t, deleted)
|
||||||
require.Equal(t, int32(2), tokenReads)
|
require.Equal(t, int32(2), tokenReads)
|
||||||
|
@ -1577,49 +1577,49 @@ func testACLResolver_variousTokens(t *testing.T, delegate *ACLResolverTestDelega
|
||||||
authz, err := r.ResolveToken("missing-policy")
|
authz, err := r.ResolveToken("missing-policy")
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NotNil(t, authz)
|
require.NotNil(t, authz)
|
||||||
require.True(t, authz.ACLRead())
|
require.Equal(t, acl.Allow, authz.ACLRead(nil))
|
||||||
require.False(t, authz.NodeWrite("foo", nil))
|
require.Equal(t, acl.Deny, authz.NodeWrite("foo", nil))
|
||||||
})
|
})
|
||||||
|
|
||||||
runTwiceAndReset("Missing Role", func(t *testing.T) {
|
runTwiceAndReset("Missing Role", func(t *testing.T) {
|
||||||
authz, err := r.ResolveToken("missing-role")
|
authz, err := r.ResolveToken("missing-role")
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NotNil(t, authz)
|
require.NotNil(t, authz)
|
||||||
require.True(t, authz.ACLRead())
|
require.Equal(t, acl.Allow, authz.ACLRead(nil))
|
||||||
require.False(t, authz.NodeWrite("foo", nil))
|
require.Equal(t, acl.Deny, authz.NodeWrite("foo", nil))
|
||||||
})
|
})
|
||||||
|
|
||||||
runTwiceAndReset("Missing Policy on Role", func(t *testing.T) {
|
runTwiceAndReset("Missing Policy on Role", func(t *testing.T) {
|
||||||
authz, err := r.ResolveToken("missing-policy-on-role")
|
authz, err := r.ResolveToken("missing-policy-on-role")
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NotNil(t, authz)
|
require.NotNil(t, authz)
|
||||||
require.True(t, authz.ACLRead())
|
require.Equal(t, acl.Allow, authz.ACLRead(nil))
|
||||||
require.False(t, authz.NodeWrite("foo", nil))
|
require.Equal(t, acl.Deny, authz.NodeWrite("foo", nil))
|
||||||
})
|
})
|
||||||
|
|
||||||
runTwiceAndReset("Normal with Policy", func(t *testing.T) {
|
runTwiceAndReset("Normal with Policy", func(t *testing.T) {
|
||||||
authz, err := r.ResolveToken("found")
|
authz, err := r.ResolveToken("found")
|
||||||
require.NotNil(t, authz)
|
require.NotNil(t, authz)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.False(t, authz.ACLRead())
|
require.Equal(t, acl.Deny, authz.ACLRead(nil))
|
||||||
require.True(t, authz.NodeWrite("foo", nil))
|
require.Equal(t, acl.Allow, authz.NodeWrite("foo", nil))
|
||||||
})
|
})
|
||||||
|
|
||||||
runTwiceAndReset("Normal with Role", func(t *testing.T) {
|
runTwiceAndReset("Normal with Role", func(t *testing.T) {
|
||||||
authz, err := r.ResolveToken("found-role")
|
authz, err := r.ResolveToken("found-role")
|
||||||
require.NotNil(t, authz)
|
require.NotNil(t, authz)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.False(t, authz.ACLRead())
|
require.Equal(t, acl.Deny, authz.ACLRead(nil))
|
||||||
require.True(t, authz.NodeWrite("foo", nil))
|
require.Equal(t, acl.Allow, authz.NodeWrite("foo", nil))
|
||||||
})
|
})
|
||||||
|
|
||||||
runTwiceAndReset("Normal with Policy and Role", func(t *testing.T) {
|
runTwiceAndReset("Normal with Policy and Role", func(t *testing.T) {
|
||||||
authz, err := r.ResolveToken("found-policy-and-role")
|
authz, err := r.ResolveToken("found-policy-and-role")
|
||||||
require.NotNil(t, authz)
|
require.NotNil(t, authz)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.False(t, authz.ACLRead())
|
require.Equal(t, acl.Deny, authz.ACLRead(nil))
|
||||||
require.True(t, authz.NodeWrite("foo", nil))
|
require.Equal(t, acl.Allow, authz.NodeWrite("foo", nil))
|
||||||
require.True(t, authz.ServiceRead("bar"))
|
require.Equal(t, acl.Allow, authz.ServiceRead("bar", nil))
|
||||||
})
|
})
|
||||||
|
|
||||||
runTwiceAndReset("Synthetic Policies Independently Cache", func(t *testing.T) {
|
runTwiceAndReset("Synthetic Policies Independently Cache", func(t *testing.T) {
|
||||||
|
@ -1631,28 +1631,28 @@ func testACLResolver_variousTokens(t *testing.T, delegate *ACLResolverTestDelega
|
||||||
require.NotNil(t, authz)
|
require.NotNil(t, authz)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
// spot check some random perms
|
// spot check some random perms
|
||||||
require.False(t, authz.ACLRead())
|
require.Equal(t, acl.Deny, authz.ACLRead(nil))
|
||||||
require.False(t, authz.NodeWrite("foo", nil))
|
require.Equal(t, acl.Deny, authz.NodeWrite("foo", nil))
|
||||||
// ensure we didn't bleed over to the other synthetic policy
|
// ensure we didn't bleed over to the other synthetic policy
|
||||||
require.False(t, authz.ServiceWrite("service2", nil))
|
require.Equal(t, acl.Deny, authz.ServiceWrite("service2", nil))
|
||||||
// check our own synthetic policy
|
// check our own synthetic policy
|
||||||
require.True(t, authz.ServiceWrite("service1", nil))
|
require.Equal(t, acl.Allow, authz.ServiceWrite("service1", nil))
|
||||||
require.True(t, authz.ServiceRead("literally-anything"))
|
require.Equal(t, acl.Allow, authz.ServiceRead("literally-anything", nil))
|
||||||
require.True(t, authz.NodeRead("any-node"))
|
require.Equal(t, acl.Allow, authz.NodeRead("any-node", nil))
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
authz, err := r.ResolveToken("found-synthetic-policy-2")
|
authz, err := r.ResolveToken("found-synthetic-policy-2")
|
||||||
require.NotNil(t, authz)
|
require.NotNil(t, authz)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
// spot check some random perms
|
// spot check some random perms
|
||||||
require.False(t, authz.ACLRead())
|
require.Equal(t, acl.Deny, authz.ACLRead(nil))
|
||||||
require.False(t, authz.NodeWrite("foo", nil))
|
require.Equal(t, acl.Deny, authz.NodeWrite("foo", nil))
|
||||||
// ensure we didn't bleed over to the other synthetic policy
|
// ensure we didn't bleed over to the other synthetic policy
|
||||||
require.False(t, authz.ServiceWrite("service1", nil))
|
require.Equal(t, acl.Deny, authz.ServiceWrite("service1", nil))
|
||||||
// check our own synthetic policy
|
// check our own synthetic policy
|
||||||
require.True(t, authz.ServiceWrite("service2", nil))
|
require.Equal(t, acl.Allow, authz.ServiceWrite("service2", nil))
|
||||||
require.True(t, authz.ServiceRead("literally-anything"))
|
require.Equal(t, acl.Allow, authz.ServiceRead("literally-anything", nil))
|
||||||
require.True(t, authz.NodeRead("any-node"))
|
require.Equal(t, acl.Allow, authz.NodeRead("any-node", nil))
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -1660,24 +1660,24 @@ func testACLResolver_variousTokens(t *testing.T, delegate *ACLResolverTestDelega
|
||||||
authz, err := r.ResolveToken("")
|
authz, err := r.ResolveToken("")
|
||||||
require.NotNil(t, authz)
|
require.NotNil(t, authz)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.False(t, authz.ACLRead())
|
require.Equal(t, acl.Deny, authz.ACLRead(nil))
|
||||||
require.True(t, authz.NodeWrite("foo", nil))
|
require.Equal(t, acl.Allow, authz.NodeWrite("foo", nil))
|
||||||
})
|
})
|
||||||
|
|
||||||
runTwiceAndReset("legacy-management", func(t *testing.T) {
|
runTwiceAndReset("legacy-management", func(t *testing.T) {
|
||||||
authz, err := r.ResolveToken("legacy-management")
|
authz, err := r.ResolveToken("legacy-management")
|
||||||
require.NotNil(t, authz)
|
require.NotNil(t, authz)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.True(t, authz.ACLWrite())
|
require.Equal(t, acl.Allow, authz.ACLWrite(nil))
|
||||||
require.True(t, authz.KeyRead("foo"))
|
require.Equal(t, acl.Allow, authz.KeyRead("foo", nil))
|
||||||
})
|
})
|
||||||
|
|
||||||
runTwiceAndReset("legacy-client", func(t *testing.T) {
|
runTwiceAndReset("legacy-client", func(t *testing.T) {
|
||||||
authz, err := r.ResolveToken("legacy-client")
|
authz, err := r.ResolveToken("legacy-client")
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NotNil(t, authz)
|
require.NotNil(t, authz)
|
||||||
require.False(t, authz.OperatorRead())
|
require.Equal(t, acl.Deny, authz.OperatorRead(nil))
|
||||||
require.True(t, authz.ServiceRead("foo"))
|
require.Equal(t, acl.Allow, authz.ServiceRead("foo", nil))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1700,10 +1700,12 @@ func TestACLResolver_Legacy(t *testing.T) {
|
||||||
reply.ETag = "nothing"
|
reply.ETag = "nothing"
|
||||||
reply.Policy = &acl.Policy{
|
reply.Policy = &acl.Policy{
|
||||||
ID: "not-needed",
|
ID: "not-needed",
|
||||||
Nodes: []*acl.NodePolicy{
|
PolicyRules: acl.PolicyRules{
|
||||||
&acl.NodePolicy{
|
Nodes: []*acl.NodeRule{
|
||||||
Name: "foo",
|
&acl.NodeRule{
|
||||||
Policy: acl.PolicyWrite,
|
Name: "foo",
|
||||||
|
Policy: acl.PolicyWrite,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -1719,18 +1721,18 @@ func TestACLResolver_Legacy(t *testing.T) {
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NotNil(t, authz)
|
require.NotNil(t, authz)
|
||||||
// there is a bit of translation that happens
|
// there is a bit of translation that happens
|
||||||
require.True(t, authz.NodeWrite("foo", nil))
|
require.Equal(t, acl.Allow, authz.NodeWrite("foo", nil))
|
||||||
require.True(t, authz.NodeWrite("foo/bar", nil))
|
require.Equal(t, acl.Allow, authz.NodeWrite("foo/bar", nil))
|
||||||
require.False(t, authz.NodeWrite("fo", nil))
|
require.Equal(t, acl.Deny, authz.NodeWrite("fo", nil))
|
||||||
|
|
||||||
// this should be from the cache
|
// this should be from the cache
|
||||||
authz, err = r.ResolveToken("foo")
|
authz, err = r.ResolveToken("foo")
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NotNil(t, authz)
|
require.NotNil(t, authz)
|
||||||
// there is a bit of translation that happens
|
// there is a bit of translation that happens
|
||||||
require.True(t, authz.NodeWrite("foo", nil))
|
require.Equal(t, acl.Allow, authz.NodeWrite("foo", nil))
|
||||||
require.True(t, authz.NodeWrite("foo/bar", nil))
|
require.Equal(t, acl.Allow, authz.NodeWrite("foo/bar", nil))
|
||||||
require.False(t, authz.NodeWrite("fo", nil))
|
require.Equal(t, acl.Deny, authz.NodeWrite("fo", nil))
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("Cache-Expiry-Extend", func(t *testing.T) {
|
t.Run("Cache-Expiry-Extend", func(t *testing.T) {
|
||||||
|
@ -1749,10 +1751,12 @@ func TestACLResolver_Legacy(t *testing.T) {
|
||||||
reply.ETag = "nothing"
|
reply.ETag = "nothing"
|
||||||
reply.Policy = &acl.Policy{
|
reply.Policy = &acl.Policy{
|
||||||
ID: "not-needed",
|
ID: "not-needed",
|
||||||
Nodes: []*acl.NodePolicy{
|
PolicyRules: acl.PolicyRules{
|
||||||
&acl.NodePolicy{
|
Nodes: []*acl.NodeRule{
|
||||||
Name: "foo",
|
&acl.NodeRule{
|
||||||
Policy: acl.PolicyWrite,
|
Name: "foo",
|
||||||
|
Policy: acl.PolicyWrite,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -1770,18 +1774,18 @@ func TestACLResolver_Legacy(t *testing.T) {
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NotNil(t, authz)
|
require.NotNil(t, authz)
|
||||||
// there is a bit of translation that happens
|
// there is a bit of translation that happens
|
||||||
require.True(t, authz.NodeWrite("foo", nil))
|
require.Equal(t, acl.Allow, authz.NodeWrite("foo", nil))
|
||||||
require.True(t, authz.NodeWrite("foo/bar", nil))
|
require.Equal(t, acl.Allow, authz.NodeWrite("foo/bar", nil))
|
||||||
require.False(t, authz.NodeWrite("fo", nil))
|
require.Equal(t, acl.Deny, authz.NodeWrite("fo", nil))
|
||||||
|
|
||||||
// this should be from the cache
|
// this should be from the cache
|
||||||
authz, err = r.ResolveToken("foo")
|
authz, err = r.ResolveToken("foo")
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NotNil(t, authz)
|
require.NotNil(t, authz)
|
||||||
// there is a bit of translation that happens
|
// there is a bit of translation that happens
|
||||||
require.True(t, authz.NodeWrite("foo", nil))
|
require.Equal(t, acl.Allow, authz.NodeWrite("foo", nil))
|
||||||
require.True(t, authz.NodeWrite("foo/bar", nil))
|
require.Equal(t, acl.Allow, authz.NodeWrite("foo/bar", nil))
|
||||||
require.False(t, authz.NodeWrite("fo", nil))
|
require.Equal(t, acl.Deny, authz.NodeWrite("fo", nil))
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("Cache-Expiry-Allow", func(t *testing.T) {
|
t.Run("Cache-Expiry-Allow", func(t *testing.T) {
|
||||||
|
@ -1800,10 +1804,12 @@ func TestACLResolver_Legacy(t *testing.T) {
|
||||||
reply.ETag = "nothing"
|
reply.ETag = "nothing"
|
||||||
reply.Policy = &acl.Policy{
|
reply.Policy = &acl.Policy{
|
||||||
ID: "not-needed",
|
ID: "not-needed",
|
||||||
Nodes: []*acl.NodePolicy{
|
PolicyRules: acl.PolicyRules{
|
||||||
&acl.NodePolicy{
|
Nodes: []*acl.NodeRule{
|
||||||
Name: "foo",
|
&acl.NodeRule{
|
||||||
Policy: acl.PolicyWrite,
|
Name: "foo",
|
||||||
|
Policy: acl.PolicyWrite,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -1822,18 +1828,18 @@ func TestACLResolver_Legacy(t *testing.T) {
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NotNil(t, authz)
|
require.NotNil(t, authz)
|
||||||
// there is a bit of translation that happens
|
// there is a bit of translation that happens
|
||||||
require.True(t, authz.NodeWrite("foo", nil))
|
require.Equal(t, acl.Allow, authz.NodeWrite("foo", nil))
|
||||||
require.True(t, authz.NodeWrite("foo/bar", nil))
|
require.Equal(t, acl.Allow, authz.NodeWrite("foo/bar", nil))
|
||||||
require.False(t, authz.NodeWrite("fo", nil))
|
require.Equal(t, acl.Deny, authz.NodeWrite("fo", nil))
|
||||||
|
|
||||||
// this should be from the cache
|
// this should be from the cache
|
||||||
authz, err = r.ResolveToken("foo")
|
authz, err = r.ResolveToken("foo")
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NotNil(t, authz)
|
require.NotNil(t, authz)
|
||||||
// there is a bit of translation that happens
|
// there is a bit of translation that happens
|
||||||
require.True(t, authz.NodeWrite("foo", nil))
|
require.Equal(t, acl.Allow, authz.NodeWrite("foo", nil))
|
||||||
require.True(t, authz.NodeWrite("foo/bar", nil))
|
require.Equal(t, acl.Allow, authz.NodeWrite("foo/bar", nil))
|
||||||
require.True(t, authz.NodeWrite("fo", nil))
|
require.Equal(t, acl.Allow, authz.NodeWrite("fo", nil))
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("Cache-Expiry-Deny", func(t *testing.T) {
|
t.Run("Cache-Expiry-Deny", func(t *testing.T) {
|
||||||
|
@ -1852,10 +1858,12 @@ func TestACLResolver_Legacy(t *testing.T) {
|
||||||
reply.ETag = "nothing"
|
reply.ETag = "nothing"
|
||||||
reply.Policy = &acl.Policy{
|
reply.Policy = &acl.Policy{
|
||||||
ID: "not-needed",
|
ID: "not-needed",
|
||||||
Nodes: []*acl.NodePolicy{
|
PolicyRules: acl.PolicyRules{
|
||||||
&acl.NodePolicy{
|
Nodes: []*acl.NodeRule{
|
||||||
Name: "foo",
|
&acl.NodeRule{
|
||||||
Policy: acl.PolicyWrite,
|
Name: "foo",
|
||||||
|
Policy: acl.PolicyWrite,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -1874,18 +1882,18 @@ func TestACLResolver_Legacy(t *testing.T) {
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NotNil(t, authz)
|
require.NotNil(t, authz)
|
||||||
// there is a bit of translation that happens
|
// there is a bit of translation that happens
|
||||||
require.True(t, authz.NodeWrite("foo", nil))
|
require.Equal(t, acl.Allow, authz.NodeWrite("foo", nil))
|
||||||
require.True(t, authz.NodeWrite("foo/bar", nil))
|
require.Equal(t, acl.Allow, authz.NodeWrite("foo/bar", nil))
|
||||||
require.False(t, authz.NodeWrite("fo", nil))
|
require.Equal(t, acl.Deny, authz.NodeWrite("fo", nil))
|
||||||
|
|
||||||
// this should be from the cache
|
// this should be from the cache
|
||||||
authz, err = r.ResolveToken("foo")
|
authz, err = r.ResolveToken("foo")
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NotNil(t, authz)
|
require.NotNil(t, authz)
|
||||||
// there is a bit of translation that happens
|
// there is a bit of translation that happens
|
||||||
require.False(t, authz.NodeWrite("foo", nil))
|
require.Equal(t, acl.Deny, authz.NodeWrite("foo", nil))
|
||||||
require.False(t, authz.NodeWrite("foo/bar", nil))
|
require.Equal(t, acl.Deny, authz.NodeWrite("foo/bar", nil))
|
||||||
require.False(t, authz.NodeWrite("fo", nil))
|
require.Equal(t, acl.Deny, authz.NodeWrite("fo", nil))
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("Cache-Expiry-Async-Cache", func(t *testing.T) {
|
t.Run("Cache-Expiry-Async-Cache", func(t *testing.T) {
|
||||||
|
@ -1904,10 +1912,12 @@ func TestACLResolver_Legacy(t *testing.T) {
|
||||||
reply.ETag = "nothing"
|
reply.ETag = "nothing"
|
||||||
reply.Policy = &acl.Policy{
|
reply.Policy = &acl.Policy{
|
||||||
ID: "not-needed",
|
ID: "not-needed",
|
||||||
Nodes: []*acl.NodePolicy{
|
PolicyRules: acl.PolicyRules{
|
||||||
&acl.NodePolicy{
|
Nodes: []*acl.NodeRule{
|
||||||
Name: "foo",
|
&acl.NodeRule{
|
||||||
Policy: acl.PolicyWrite,
|
Name: "foo",
|
||||||
|
Policy: acl.PolicyWrite,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -1926,9 +1936,9 @@ func TestACLResolver_Legacy(t *testing.T) {
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NotNil(t, authz)
|
require.NotNil(t, authz)
|
||||||
// there is a bit of translation that happens
|
// there is a bit of translation that happens
|
||||||
require.True(t, authz.NodeWrite("foo", nil))
|
require.Equal(t, acl.Allow, authz.NodeWrite("foo", nil))
|
||||||
require.True(t, authz.NodeWrite("foo/bar", nil))
|
require.Equal(t, acl.Allow, authz.NodeWrite("foo/bar", nil))
|
||||||
require.False(t, authz.NodeWrite("fo", nil))
|
require.Equal(t, acl.Deny, authz.NodeWrite("fo", nil))
|
||||||
|
|
||||||
// delivered from the cache
|
// delivered from the cache
|
||||||
authz2, err := r.ResolveToken("foo")
|
authz2, err := r.ResolveToken("foo")
|
||||||
|
@ -2170,7 +2180,7 @@ service "foo" {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("err %v", err)
|
t.Fatalf("err %v", err)
|
||||||
}
|
}
|
||||||
perms, err := acl.NewPolicyAuthorizer(acl.DenyAll(), []*acl.Policy{policy}, nil)
|
perms, err := acl.NewPolicyAuthorizerWithDefaults(acl.DenyAll(), []*acl.Policy{policy}, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("err: %v", err)
|
t.Fatalf("err: %v", err)
|
||||||
}
|
}
|
||||||
|
@ -2204,7 +2214,7 @@ node "node1" {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("err %v", err)
|
t.Fatalf("err %v", err)
|
||||||
}
|
}
|
||||||
perms, err = acl.NewPolicyAuthorizer(perms, []*acl.Policy{policy}, nil)
|
perms, err = acl.NewPolicyAuthorizerWithDefaults(perms, []*acl.Policy{policy}, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("err: %v", err)
|
t.Fatalf("err: %v", err)
|
||||||
}
|
}
|
||||||
|
@ -2260,7 +2270,7 @@ service "foo" {
|
||||||
}
|
}
|
||||||
`, acl.SyntaxLegacy, nil)
|
`, acl.SyntaxLegacy, nil)
|
||||||
assert.Nil(err)
|
assert.Nil(err)
|
||||||
perms, err := acl.NewPolicyAuthorizer(acl.DenyAll(), []*acl.Policy{policy}, nil)
|
perms, err := acl.NewPolicyAuthorizerWithDefaults(acl.DenyAll(), []*acl.Policy{policy}, nil)
|
||||||
assert.Nil(err)
|
assert.Nil(err)
|
||||||
|
|
||||||
// Filter
|
// Filter
|
||||||
|
@ -2347,7 +2357,7 @@ service "foo" {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("err %v", err)
|
t.Fatalf("err %v", err)
|
||||||
}
|
}
|
||||||
perms, err := acl.NewPolicyAuthorizer(acl.DenyAll(), []*acl.Policy{policy}, nil)
|
perms, err := acl.NewPolicyAuthorizerWithDefaults(acl.DenyAll(), []*acl.Policy{policy}, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("err: %v", err)
|
t.Fatalf("err: %v", err)
|
||||||
}
|
}
|
||||||
|
@ -2381,7 +2391,7 @@ node "node1" {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("err %v", err)
|
t.Fatalf("err %v", err)
|
||||||
}
|
}
|
||||||
perms, err = acl.NewPolicyAuthorizer(perms, []*acl.Policy{policy}, nil)
|
perms, err = acl.NewPolicyAuthorizerWithDefaults(perms, []*acl.Policy{policy}, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("err: %v", err)
|
t.Fatalf("err: %v", err)
|
||||||
}
|
}
|
||||||
|
@ -2453,7 +2463,7 @@ service "foo" {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("err %v", err)
|
t.Fatalf("err %v", err)
|
||||||
}
|
}
|
||||||
perms, err := acl.NewPolicyAuthorizer(acl.DenyAll(), []*acl.Policy{policy}, nil)
|
perms, err := acl.NewPolicyAuthorizerWithDefaults(acl.DenyAll(), []*acl.Policy{policy}, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("err: %v", err)
|
t.Fatalf("err: %v", err)
|
||||||
}
|
}
|
||||||
|
@ -2487,7 +2497,7 @@ node "node1" {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("err %v", err)
|
t.Fatalf("err %v", err)
|
||||||
}
|
}
|
||||||
perms, err = acl.NewPolicyAuthorizer(perms, []*acl.Policy{policy}, nil)
|
perms, err = acl.NewPolicyAuthorizerWithDefaults(perms, []*acl.Policy{policy}, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("err: %v", err)
|
t.Fatalf("err: %v", err)
|
||||||
}
|
}
|
||||||
|
@ -2559,7 +2569,7 @@ service "foo" {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("err %v", err)
|
t.Fatalf("err %v", err)
|
||||||
}
|
}
|
||||||
perms, err := acl.NewPolicyAuthorizer(acl.DenyAll(), []*acl.Policy{policy}, nil)
|
perms, err := acl.NewPolicyAuthorizerWithDefaults(acl.DenyAll(), []*acl.Policy{policy}, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("err: %v", err)
|
t.Fatalf("err: %v", err)
|
||||||
}
|
}
|
||||||
|
@ -2596,7 +2606,7 @@ node "node1" {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("err %v", err)
|
t.Fatalf("err %v", err)
|
||||||
}
|
}
|
||||||
perms, err = acl.NewPolicyAuthorizer(perms, []*acl.Policy{policy}, nil)
|
perms, err = acl.NewPolicyAuthorizerWithDefaults(perms, []*acl.Policy{policy}, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("err: %v", err)
|
t.Fatalf("err: %v", err)
|
||||||
}
|
}
|
||||||
|
@ -2750,7 +2760,7 @@ service "foo" {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("err %v", err)
|
t.Fatalf("err %v", err)
|
||||||
}
|
}
|
||||||
perms, err := acl.NewPolicyAuthorizer(acl.DenyAll(), []*acl.Policy{policy}, nil)
|
perms, err := acl.NewPolicyAuthorizerWithDefaults(acl.DenyAll(), []*acl.Policy{policy}, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("err: %v", err)
|
t.Fatalf("err: %v", err)
|
||||||
}
|
}
|
||||||
|
@ -2790,7 +2800,7 @@ node "node1" {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("err %v", err)
|
t.Fatalf("err %v", err)
|
||||||
}
|
}
|
||||||
perms, err = acl.NewPolicyAuthorizer(perms, []*acl.Policy{policy}, nil)
|
perms, err = acl.NewPolicyAuthorizerWithDefaults(perms, []*acl.Policy{policy}, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("err: %v", err)
|
t.Fatalf("err: %v", err)
|
||||||
}
|
}
|
||||||
|
@ -3046,7 +3056,7 @@ node "node" {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("err %v", err)
|
t.Fatalf("err %v", err)
|
||||||
}
|
}
|
||||||
perms, err := acl.NewPolicyAuthorizer(acl.DenyAll(), []*acl.Policy{policy}, nil)
|
perms, err := acl.NewPolicyAuthorizerWithDefaults(acl.DenyAll(), []*acl.Policy{policy}, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("err: %v", err)
|
t.Fatalf("err: %v", err)
|
||||||
}
|
}
|
||||||
|
@ -3091,7 +3101,7 @@ service "service" {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("err %v", err)
|
t.Fatalf("err %v", err)
|
||||||
}
|
}
|
||||||
perms, err = acl.NewPolicyAuthorizer(perms, []*acl.Policy{policy}, nil)
|
perms, err = acl.NewPolicyAuthorizerWithDefaults(perms, []*acl.Policy{policy}, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("err: %v", err)
|
t.Fatalf("err: %v", err)
|
||||||
}
|
}
|
||||||
|
@ -3121,7 +3131,7 @@ service "other" {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("err %v", err)
|
t.Fatalf("err %v", err)
|
||||||
}
|
}
|
||||||
perms, err = acl.NewPolicyAuthorizer(perms, []*acl.Policy{policy}, nil)
|
perms, err = acl.NewPolicyAuthorizerWithDefaults(perms, []*acl.Policy{policy}, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("err: %v", err)
|
t.Fatalf("err: %v", err)
|
||||||
}
|
}
|
||||||
|
@ -3195,7 +3205,7 @@ service "other" {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("err %v", err)
|
t.Fatalf("err %v", err)
|
||||||
}
|
}
|
||||||
perms, err = acl.NewPolicyAuthorizer(perms, []*acl.Policy{policy}, nil)
|
perms, err = acl.NewPolicyAuthorizerWithDefaults(perms, []*acl.Policy{policy}, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("err: %v", err)
|
t.Fatalf("err: %v", err)
|
||||||
}
|
}
|
||||||
|
@ -3225,7 +3235,7 @@ node "node" {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("err %v", err)
|
t.Fatalf("err %v", err)
|
||||||
}
|
}
|
||||||
perms, err = acl.NewPolicyAuthorizer(perms, []*acl.Policy{policy}, nil)
|
perms, err = acl.NewPolicyAuthorizerWithDefaults(perms, []*acl.Policy{policy}, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("err: %v", err)
|
t.Fatalf("err: %v", err)
|
||||||
}
|
}
|
||||||
|
@ -3272,7 +3282,7 @@ node "node" {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("err %v", err)
|
t.Fatalf("err %v", err)
|
||||||
}
|
}
|
||||||
nodePerms, err := acl.NewPolicyAuthorizer(acl.DenyAll(), []*acl.Policy{policy}, nil)
|
nodePerms, err := acl.NewPolicyAuthorizerWithDefaults(acl.DenyAll(), []*acl.Policy{policy}, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("err: %v", err)
|
t.Fatalf("err: %v", err)
|
||||||
}
|
}
|
||||||
|
@ -3285,7 +3295,7 @@ node "node" {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("err %v", err)
|
t.Fatalf("err %v", err)
|
||||||
}
|
}
|
||||||
servicePerms, err := acl.NewPolicyAuthorizer(acl.DenyAll(), []*acl.Policy{policy}, nil)
|
servicePerms, err := acl.NewPolicyAuthorizerWithDefaults(acl.DenyAll(), []*acl.Policy{policy}, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("err: %v", err)
|
t.Fatalf("err: %v", err)
|
||||||
}
|
}
|
||||||
|
@ -3294,7 +3304,7 @@ node "node" {
|
||||||
DeregisterRequest structs.DeregisterRequest
|
DeregisterRequest structs.DeregisterRequest
|
||||||
Service *structs.NodeService
|
Service *structs.NodeService
|
||||||
Check *structs.HealthCheck
|
Check *structs.HealthCheck
|
||||||
Perms *acl.PolicyAuthorizer
|
Perms acl.Authorizer
|
||||||
Expected bool
|
Expected bool
|
||||||
Name string
|
Name string
|
||||||
}{
|
}{
|
||||||
|
|
|
@ -65,14 +65,16 @@ func servicePreApply(service *structs.NodeService, rule acl.Authorizer) error {
|
||||||
// later if version 0.8 is enabled, so we can eventually just
|
// later if version 0.8 is enabled, so we can eventually just
|
||||||
// delete this and do all the ACL checks down there.
|
// delete this and do all the ACL checks down there.
|
||||||
if service.Service != structs.ConsulServiceName {
|
if service.Service != structs.ConsulServiceName {
|
||||||
if rule != nil && !rule.ServiceWrite(service.Service, nil) {
|
// TODO (namespaces) update to send an actual enterprise authorizer context
|
||||||
|
if rule != nil && rule.ServiceWrite(service.Service, nil) != acl.Allow {
|
||||||
return acl.ErrPermissionDenied
|
return acl.ErrPermissionDenied
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Proxies must have write permission on their destination
|
// Proxies must have write permission on their destination
|
||||||
if service.Kind == structs.ServiceKindConnectProxy {
|
if service.Kind == structs.ServiceKindConnectProxy {
|
||||||
if rule != nil && !rule.ServiceWrite(service.Proxy.DestinationServiceName, nil) {
|
// TODO (namespaces) update to send an actual enterprise authorizer context
|
||||||
|
if rule != nil && rule.ServiceWrite(service.Proxy.DestinationServiceName, nil) != acl.Allow {
|
||||||
return acl.ErrPermissionDenied
|
return acl.ErrPermissionDenied
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -334,7 +336,8 @@ func (c *Catalog) ServiceNodes(args *structs.ServiceSpecificRequest, reply *stru
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if rule != nil && !rule.ServiceRead(args.ServiceName) {
|
// TODO (namespaces) update to send an actual enterprise authorizer context
|
||||||
|
if rule != nil && rule.ServiceRead(args.ServiceName, nil) != acl.Allow {
|
||||||
// Just return nil, which will return an empty response (tested)
|
// Just return nil, which will return an empty response (tested)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -154,12 +154,12 @@ func NewClientLogger(config *Config, logger *log.Logger, tlsConfigurator *tlsuti
|
||||||
|
|
||||||
c.useNewACLs = 0
|
c.useNewACLs = 0
|
||||||
aclConfig := ACLResolverConfig{
|
aclConfig := ACLResolverConfig{
|
||||||
Config: config,
|
Config: config,
|
||||||
Delegate: c,
|
Delegate: c,
|
||||||
Logger: logger,
|
Logger: logger,
|
||||||
AutoDisable: true,
|
AutoDisable: true,
|
||||||
CacheConfig: clientACLCacheConfig,
|
CacheConfig: clientACLCacheConfig,
|
||||||
Sentinel: nil,
|
EnterpriseConfig: newEnterpriseACLConfig(logger),
|
||||||
}
|
}
|
||||||
var err error
|
var err error
|
||||||
if c.acls, err = NewACLResolver(&aclConfig); err != nil {
|
if c.acls, err = NewACLResolver(&aclConfig); err != nil {
|
||||||
|
|
|
@ -231,7 +231,8 @@ func (c *ConfigEntry) ResolveServiceConfig(args *structs.ServiceConfigRequest, r
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if rule != nil && !rule.ServiceRead(args.Name) {
|
// TODO (namespaces) use actual ent authz context
|
||||||
|
if rule != nil && rule.ServiceRead(args.Name, nil) != acl.Allow {
|
||||||
return acl.ErrPermissionDenied
|
return acl.ErrPermissionDenied
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -119,7 +119,7 @@ func (s *ConnectCA) ConfigurationGet(
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if rule != nil && !rule.OperatorRead() {
|
if rule != nil && rule.OperatorRead(nil) != acl.Allow {
|
||||||
return acl.ErrPermissionDenied
|
return acl.ErrPermissionDenied
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -151,7 +151,7 @@ func (s *ConnectCA) ConfigurationSet(
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if rule != nil && !rule.OperatorWrite() {
|
if rule != nil && rule.OperatorWrite(nil) != acl.Allow {
|
||||||
return acl.ErrPermissionDenied
|
return acl.ErrPermissionDenied
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -431,7 +431,8 @@ func (s *ConnectCA) Sign(
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if isService {
|
if isService {
|
||||||
if rule != nil && !rule.ServiceWrite(serviceID.Service, nil) {
|
// TODO (namespaces) use actual ent authz context
|
||||||
|
if rule != nil && rule.ServiceWrite(serviceID.Service, nil) != acl.Allow {
|
||||||
return acl.ErrPermissionDenied
|
return acl.ErrPermissionDenied
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -442,7 +443,8 @@ func (s *ConnectCA) Sign(
|
||||||
"we are %s", serviceID.Datacenter, s.srv.config.Datacenter)
|
"we are %s", serviceID.Datacenter, s.srv.config.Datacenter)
|
||||||
}
|
}
|
||||||
} else if isAgent {
|
} else if isAgent {
|
||||||
if rule != nil && !rule.NodeWrite(agentID.Agent, nil) {
|
// TODO (namespaces) use actual ent authz context
|
||||||
|
if rule != nil && rule.NodeWrite(agentID.Agent, nil) != acl.Allow {
|
||||||
return acl.ErrPermissionDenied
|
return acl.ErrPermissionDenied
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -569,7 +571,7 @@ func (s *ConnectCA) SignIntermediate(
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if rule != nil && !rule.OperatorWrite() {
|
if rule != nil && rule.OperatorWrite(nil) != acl.Allow {
|
||||||
return acl.ErrPermissionDenied
|
return acl.ErrPermissionDenied
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -139,7 +139,8 @@ func (c *Coordinate) Update(args *structs.CoordinateUpdateRequest, reply *struct
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if rule != nil && c.srv.config.ACLEnforceVersion8 {
|
if rule != nil && c.srv.config.ACLEnforceVersion8 {
|
||||||
if !rule.NodeWrite(args.Node, nil) {
|
// TODO (namespaces) use actual ent authz context
|
||||||
|
if rule.NodeWrite(args.Node, nil) != acl.Allow {
|
||||||
return acl.ErrPermissionDenied
|
return acl.ErrPermissionDenied
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -210,7 +211,8 @@ func (c *Coordinate) Node(args *structs.NodeSpecificRequest, reply *structs.Inde
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if rule != nil && c.srv.config.ACLEnforceVersion8 {
|
if rule != nil && c.srv.config.ACLEnforceVersion8 {
|
||||||
if !rule.NodeRead(args.Node) {
|
// TODO (namespaces) use actual ent authz context
|
||||||
|
if rule.NodeRead(args.Node, nil) != acl.Allow {
|
||||||
return acl.ErrPermissionDenied
|
return acl.ErrPermissionDenied
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,7 +34,8 @@ func (c *DiscoveryChain) Get(args *structs.DiscoveryChainRequest, reply *structs
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if rule != nil && !rule.ServiceRead(args.Name) {
|
// TODO (namespaces) use actual ent authz context
|
||||||
|
if rule != nil && rule.ServiceRead(args.Name, nil) != acl.Allow {
|
||||||
return acl.ErrPermissionDenied
|
return acl.ErrPermissionDenied
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -14,7 +14,7 @@ func (d *dirEntFilter) Len() int {
|
||||||
return len(d.ent)
|
return len(d.ent)
|
||||||
}
|
}
|
||||||
func (d *dirEntFilter) Filter(i int) bool {
|
func (d *dirEntFilter) Filter(i int) bool {
|
||||||
return !d.authorizer.KeyRead(d.ent[i].Key)
|
return d.authorizer.KeyRead(d.ent[i].Key, nil) != acl.Allow
|
||||||
}
|
}
|
||||||
func (d *dirEntFilter) Move(dst, src, span int) {
|
func (d *dirEntFilter) Move(dst, src, span int) {
|
||||||
copy(d.ent[dst:dst+span], d.ent[src:src+span])
|
copy(d.ent[dst:dst+span], d.ent[src:src+span])
|
||||||
|
@ -36,7 +36,8 @@ func (k *keyFilter) Len() int {
|
||||||
return len(k.keys)
|
return len(k.keys)
|
||||||
}
|
}
|
||||||
func (k *keyFilter) Filter(i int) bool {
|
func (k *keyFilter) Filter(i int) bool {
|
||||||
return !k.authorizer.KeyRead(k.keys[i])
|
// TODO (namespaces) use a real ent authz context here
|
||||||
|
return k.authorizer.KeyRead(k.keys[i], nil) != acl.Allow
|
||||||
}
|
}
|
||||||
|
|
||||||
func (k *keyFilter) Move(dst, src, span int) {
|
func (k *keyFilter) Move(dst, src, span int) {
|
||||||
|
@ -60,19 +61,20 @@ func (t *txnResultsFilter) Len() int {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *txnResultsFilter) Filter(i int) bool {
|
func (t *txnResultsFilter) Filter(i int) bool {
|
||||||
|
// TODO (namespaces) use a real ent authz context for most of these checks
|
||||||
result := t.results[i]
|
result := t.results[i]
|
||||||
switch {
|
switch {
|
||||||
case result.KV != nil:
|
case result.KV != nil:
|
||||||
return !t.authorizer.KeyRead(result.KV.Key)
|
return t.authorizer.KeyRead(result.KV.Key, nil) != acl.Allow
|
||||||
case result.Node != nil:
|
case result.Node != nil:
|
||||||
return !t.authorizer.NodeRead(result.Node.Node)
|
return t.authorizer.NodeRead(result.Node.Node, nil) != acl.Allow
|
||||||
case result.Service != nil:
|
case result.Service != nil:
|
||||||
return !t.authorizer.ServiceRead(result.Service.Service)
|
return t.authorizer.ServiceRead(result.Service.Service, nil) != acl.Allow
|
||||||
case result.Check != nil:
|
case result.Check != nil:
|
||||||
if result.Check.ServiceName != "" {
|
if result.Check.ServiceName != "" {
|
||||||
return !t.authorizer.ServiceRead(result.Check.ServiceName)
|
return t.authorizer.ServiceRead(result.Check.ServiceName, nil) != acl.Allow
|
||||||
}
|
}
|
||||||
return !t.authorizer.NodeRead(result.Check.Node)
|
return t.authorizer.NodeRead(result.Check.Node, nil) != acl.Allow
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,7 +11,7 @@ import (
|
||||||
func TestFilter_DirEnt(t *testing.T) {
|
func TestFilter_DirEnt(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
policy, _ := acl.NewPolicyFromSource("", 0, testFilterRules, acl.SyntaxLegacy, nil)
|
policy, _ := acl.NewPolicyFromSource("", 0, testFilterRules, acl.SyntaxLegacy, nil)
|
||||||
aclR, _ := acl.NewPolicyAuthorizer(acl.DenyAll(), []*acl.Policy{policy}, nil)
|
aclR, _ := acl.NewPolicyAuthorizerWithDefaults(acl.DenyAll(), []*acl.Policy{policy}, nil)
|
||||||
|
|
||||||
type tcase struct {
|
type tcase struct {
|
||||||
in []string
|
in []string
|
||||||
|
@ -53,7 +53,7 @@ func TestFilter_DirEnt(t *testing.T) {
|
||||||
func TestFilter_Keys(t *testing.T) {
|
func TestFilter_Keys(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
policy, _ := acl.NewPolicyFromSource("", 0, testFilterRules, acl.SyntaxLegacy, nil)
|
policy, _ := acl.NewPolicyFromSource("", 0, testFilterRules, acl.SyntaxLegacy, nil)
|
||||||
aclR, _ := acl.NewPolicyAuthorizer(acl.DenyAll(), []*acl.Policy{policy}, nil)
|
aclR, _ := acl.NewPolicyAuthorizerWithDefaults(acl.DenyAll(), []*acl.Policy{policy}, nil)
|
||||||
|
|
||||||
type tcase struct {
|
type tcase struct {
|
||||||
in []string
|
in []string
|
||||||
|
@ -85,7 +85,7 @@ func TestFilter_Keys(t *testing.T) {
|
||||||
func TestFilter_TxnResults(t *testing.T) {
|
func TestFilter_TxnResults(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
policy, _ := acl.NewPolicyFromSource("", 0, testFilterRules, acl.SyntaxLegacy, nil)
|
policy, _ := acl.NewPolicyFromSource("", 0, testFilterRules, acl.SyntaxLegacy, nil)
|
||||||
aclR, _ := acl.NewPolicyAuthorizer(acl.DenyAll(), []*acl.Policy{policy}, nil)
|
aclR, _ := acl.NewPolicyAuthorizerWithDefaults(acl.DenyAll(), []*acl.Policy{policy}, nil)
|
||||||
|
|
||||||
type tcase struct {
|
type tcase struct {
|
||||||
in []string
|
in []string
|
||||||
|
|
|
@ -5,6 +5,7 @@ import (
|
||||||
"sort"
|
"sort"
|
||||||
|
|
||||||
"github.com/armon/go-metrics"
|
"github.com/armon/go-metrics"
|
||||||
|
"github.com/hashicorp/consul/acl"
|
||||||
"github.com/hashicorp/consul/agent/consul/state"
|
"github.com/hashicorp/consul/agent/consul/state"
|
||||||
"github.com/hashicorp/consul/agent/structs"
|
"github.com/hashicorp/consul/agent/structs"
|
||||||
bexpr "github.com/hashicorp/go-bexpr"
|
bexpr "github.com/hashicorp/go-bexpr"
|
||||||
|
@ -171,7 +172,7 @@ func (h *Health) ServiceNodes(args *structs.ServiceSpecificRequest, reply *struc
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if rule != nil && !rule.ServiceRead(args.ServiceName) {
|
if rule != nil && rule.ServiceRead(args.ServiceName, nil) != acl.Allow {
|
||||||
// Just return nil, which will return an empty response (tested)
|
// Just return nil, which will return an empty response (tested)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -87,7 +87,7 @@ func (s *Intention) Apply(
|
||||||
|
|
||||||
// Perform the ACL check
|
// Perform the ACL check
|
||||||
if prefix, ok := args.Intention.GetACLPrefix(); ok {
|
if prefix, ok := args.Intention.GetACLPrefix(); ok {
|
||||||
if rule != nil && !rule.IntentionWrite(prefix) {
|
if rule != nil && rule.IntentionWrite(prefix, nil) != acl.Allow {
|
||||||
s.srv.logger.Printf("[WARN] consul.intention: Operation on intention '%s' denied due to ACLs", args.Intention.ID)
|
s.srv.logger.Printf("[WARN] consul.intention: Operation on intention '%s' denied due to ACLs", args.Intention.ID)
|
||||||
return acl.ErrPermissionDenied
|
return acl.ErrPermissionDenied
|
||||||
}
|
}
|
||||||
|
@ -107,7 +107,7 @@ func (s *Intention) Apply(
|
||||||
// Perform the ACL check that we have write to the old prefix too,
|
// Perform the ACL check that we have write to the old prefix too,
|
||||||
// which must be true to perform any rename.
|
// which must be true to perform any rename.
|
||||||
if prefix, ok := ixn.GetACLPrefix(); ok {
|
if prefix, ok := ixn.GetACLPrefix(); ok {
|
||||||
if rule != nil && !rule.IntentionWrite(prefix) {
|
if rule != nil && rule.IntentionWrite(prefix, nil) != acl.Allow {
|
||||||
s.srv.logger.Printf("[WARN] consul.intention: Operation on intention '%s' denied due to ACLs", args.Intention.ID)
|
s.srv.logger.Printf("[WARN] consul.intention: Operation on intention '%s' denied due to ACLs", args.Intention.ID)
|
||||||
return acl.ErrPermissionDenied
|
return acl.ErrPermissionDenied
|
||||||
}
|
}
|
||||||
|
@ -243,7 +243,7 @@ func (s *Intention) Match(
|
||||||
// We go through each entry and test the destination to check if it
|
// We go through each entry and test the destination to check if it
|
||||||
// matches.
|
// matches.
|
||||||
for _, entry := range args.Match.Entries {
|
for _, entry := range args.Match.Entries {
|
||||||
if prefix := entry.Name; prefix != "" && !rule.IntentionRead(prefix) {
|
if prefix := entry.Name; prefix != "" && rule.IntentionRead(prefix, nil) != acl.Allow {
|
||||||
s.srv.logger.Printf("[WARN] consul.intention: Operation on intention prefix '%s' denied due to ACLs", prefix)
|
s.srv.logger.Printf("[WARN] consul.intention: Operation on intention prefix '%s' denied due to ACLs", prefix)
|
||||||
return acl.ErrPermissionDenied
|
return acl.ErrPermissionDenied
|
||||||
}
|
}
|
||||||
|
@ -309,7 +309,7 @@ func (s *Intention) Check(
|
||||||
// NOT IntentionRead because the Check API only returns pass/fail and
|
// NOT IntentionRead because the Check API only returns pass/fail and
|
||||||
// returns no other information about the intentions used.
|
// returns no other information about the intentions used.
|
||||||
if prefix, ok := query.GetACLPrefix(); ok {
|
if prefix, ok := query.GetACLPrefix(); ok {
|
||||||
if rule != nil && !rule.ServiceRead(prefix) {
|
if rule != nil && rule.ServiceRead(prefix, nil) != acl.Allow {
|
||||||
s.srv.logger.Printf("[WARN] consul.intention: test on intention '%s' denied due to ACLs", prefix)
|
s.srv.logger.Printf("[WARN] consul.intention: test on intention '%s' denied due to ACLs", prefix)
|
||||||
return acl.ErrPermissionDenied
|
return acl.ErrPermissionDenied
|
||||||
}
|
}
|
||||||
|
@ -360,7 +360,7 @@ func (s *Intention) Check(
|
||||||
|
|
||||||
reply.Allowed = true
|
reply.Allowed = true
|
||||||
if rule != nil {
|
if rule != nil {
|
||||||
reply.Allowed = rule.IntentionDefaultAllow()
|
reply.Allowed = rule.IntentionDefaultAllow(nil) == acl.Allow
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
|
|
@ -125,7 +125,7 @@ func (m *Internal) EventFire(args *structs.EventFireRequest,
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if rule != nil && !rule.EventWrite(args.Name) {
|
if rule != nil && rule.EventWrite(args.Name, nil) != acl.Allow {
|
||||||
m.srv.logger.Printf("[WARN] consul: user event %q blocked by ACLs", args.Name)
|
m.srv.logger.Printf("[WARN] consul: user event %q blocked by ACLs", args.Name)
|
||||||
return acl.ErrPermissionDenied
|
return acl.ErrPermissionDenied
|
||||||
}
|
}
|
||||||
|
@ -162,7 +162,7 @@ func (m *Internal) KeyringOperation(
|
||||||
if rule != nil {
|
if rule != nil {
|
||||||
switch args.Operation {
|
switch args.Operation {
|
||||||
case structs.KeyringList:
|
case structs.KeyringList:
|
||||||
if !rule.KeyringRead() {
|
if rule.KeyringRead(nil) != acl.Allow {
|
||||||
return fmt.Errorf("Reading keyring denied by ACLs")
|
return fmt.Errorf("Reading keyring denied by ACLs")
|
||||||
}
|
}
|
||||||
case structs.KeyringInstall:
|
case structs.KeyringInstall:
|
||||||
|
@ -170,7 +170,7 @@ func (m *Internal) KeyringOperation(
|
||||||
case structs.KeyringUse:
|
case structs.KeyringUse:
|
||||||
fallthrough
|
fallthrough
|
||||||
case structs.KeyringRemove:
|
case structs.KeyringRemove:
|
||||||
if !rule.KeyringWrite() {
|
if rule.KeyringWrite(nil) != acl.Allow {
|
||||||
return fmt.Errorf("Modifying keyring denied due to ACLs")
|
return fmt.Errorf("Modifying keyring denied due to ACLs")
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
|
|
|
@ -9,7 +9,6 @@ import (
|
||||||
"github.com/hashicorp/consul/agent/consul/state"
|
"github.com/hashicorp/consul/agent/consul/state"
|
||||||
"github.com/hashicorp/consul/agent/structs"
|
"github.com/hashicorp/consul/agent/structs"
|
||||||
"github.com/hashicorp/consul/api"
|
"github.com/hashicorp/consul/api"
|
||||||
"github.com/hashicorp/consul/sentinel"
|
|
||||||
"github.com/hashicorp/go-memdb"
|
"github.com/hashicorp/go-memdb"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -32,7 +31,8 @@ func kvsPreApply(srv *Server, rule acl.Authorizer, op api.KVOp, dirEnt *structs.
|
||||||
if rule != nil {
|
if rule != nil {
|
||||||
switch op {
|
switch op {
|
||||||
case api.KVDeleteTree:
|
case api.KVDeleteTree:
|
||||||
if !rule.KeyWritePrefix(dirEnt.Key) {
|
// TODO (namespaces) use actual ent authz context - ensure we set the Sentinel Scope
|
||||||
|
if rule.KeyWritePrefix(dirEnt.Key, nil) != acl.Allow {
|
||||||
return false, acl.ErrPermissionDenied
|
return false, acl.ErrPermissionDenied
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -43,15 +43,13 @@ func kvsPreApply(srv *Server, rule acl.Authorizer, op api.KVOp, dirEnt *structs.
|
||||||
// These could reveal information based on the outcome
|
// These could reveal information based on the outcome
|
||||||
// of the transaction, and they operate on individual
|
// of the transaction, and they operate on individual
|
||||||
// keys so we check them here.
|
// keys so we check them here.
|
||||||
if !rule.KeyRead(dirEnt.Key) {
|
if rule.KeyRead(dirEnt.Key, nil) != acl.Allow {
|
||||||
return false, acl.ErrPermissionDenied
|
return false, acl.ErrPermissionDenied
|
||||||
}
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
scope := func() map[string]interface{} {
|
// TODO (namespaces) use actual ent authz context - ensure we set the Sentinel Scope
|
||||||
return sentinel.ScopeKVUpsert(dirEnt.Key, dirEnt.Value, dirEnt.Flags)
|
if rule.KeyWrite(dirEnt.Key, nil) != acl.Allow {
|
||||||
}
|
|
||||||
if !rule.KeyWrite(dirEnt.Key, scope) {
|
|
||||||
return false, acl.ErrPermissionDenied
|
return false, acl.ErrPermissionDenied
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -132,7 +130,7 @@ func (k *KVS) Get(args *structs.KeyRequest, reply *structs.IndexedDirEntries) er
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if aclRule != nil && !aclRule.KeyRead(args.Key) {
|
if aclRule != nil && aclRule.KeyRead(args.Key, nil) != acl.Allow {
|
||||||
return acl.ErrPermissionDenied
|
return acl.ErrPermissionDenied
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -164,7 +162,7 @@ func (k *KVS) List(args *structs.KeyRequest, reply *structs.IndexedDirEntries) e
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if aclToken != nil && k.srv.config.ACLEnableKeyListPolicy && !aclToken.KeyList(args.Key) {
|
if aclToken != nil && k.srv.config.ACLEnableKeyListPolicy && aclToken.KeyList(args.Key, nil) != acl.Allow {
|
||||||
return acl.ErrPermissionDenied
|
return acl.ErrPermissionDenied
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -208,7 +206,7 @@ func (k *KVS) ListKeys(args *structs.KeyListRequest, reply *structs.IndexedKeyLi
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if aclToken != nil && k.srv.config.ACLEnableKeyListPolicy && !aclToken.KeyList(args.Prefix) {
|
if aclToken != nil && k.srv.config.ACLEnableKeyListPolicy && aclToken.KeyList(args.Prefix, nil) != acl.Allow {
|
||||||
return acl.ErrPermissionDenied
|
return acl.ErrPermissionDenied
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -498,23 +498,28 @@ func (s *Server) initializeACLs(upgrade bool) error {
|
||||||
|
|
||||||
s.logger.Printf("[INFO] acl: initializing acls")
|
s.logger.Printf("[INFO] acl: initializing acls")
|
||||||
|
|
||||||
// Create the builtin global-management policy
|
// Create/Upgrade the builtin global-management policy
|
||||||
_, policy, err := s.fsm.State().ACLPolicyGetByID(nil, structs.ACLPolicyGlobalManagementID)
|
_, policy, err := s.fsm.State().ACLPolicyGetByID(nil, structs.ACLPolicyGlobalManagementID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to get the builtin global-management policy")
|
return fmt.Errorf("failed to get the builtin global-management policy")
|
||||||
}
|
}
|
||||||
if policy == nil {
|
if policy == nil || policy.Rules != structs.ACLPolicyGlobalManagement {
|
||||||
policy := structs.ACLPolicy{
|
newPolicy := structs.ACLPolicy{
|
||||||
ID: structs.ACLPolicyGlobalManagementID,
|
ID: structs.ACLPolicyGlobalManagementID,
|
||||||
Name: "global-management",
|
Name: "global-management",
|
||||||
Description: "Builtin Policy that grants unlimited access",
|
Description: "Builtin Policy that grants unlimited access",
|
||||||
Rules: structs.ACLPolicyGlobalManagement,
|
Rules: structs.ACLPolicyGlobalManagement,
|
||||||
Syntax: acl.SyntaxCurrent,
|
Syntax: acl.SyntaxCurrent,
|
||||||
}
|
}
|
||||||
policy.SetHash(true)
|
if policy != nil {
|
||||||
|
newPolicy.Name = policy.Name
|
||||||
|
newPolicy.Description = policy.Description
|
||||||
|
}
|
||||||
|
|
||||||
|
newPolicy.SetHash(true)
|
||||||
|
|
||||||
req := structs.ACLPolicyBatchSetRequest{
|
req := structs.ACLPolicyBatchSetRequest{
|
||||||
Policies: structs.ACLPolicies{&policy},
|
Policies: structs.ACLPolicies{&newPolicy},
|
||||||
}
|
}
|
||||||
_, err := s.raftApply(structs.ACLPolicySetRequestType, &req)
|
_, err := s.raftApply(structs.ACLPolicySetRequestType, &req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -19,7 +19,7 @@ func (op *Operator) AutopilotGetConfiguration(args *structs.DCSpecificRequest, r
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if rule != nil && !rule.OperatorRead() {
|
if rule != nil && rule.OperatorRead(nil) != acl.Allow {
|
||||||
return acl.ErrPermissionDenied
|
return acl.ErrPermissionDenied
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -48,7 +48,7 @@ func (op *Operator) AutopilotSetConfiguration(args *structs.AutopilotSetConfigRe
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if rule != nil && !rule.OperatorWrite() {
|
if rule != nil && rule.OperatorWrite(nil) != acl.Allow {
|
||||||
return acl.ErrPermissionDenied
|
return acl.ErrPermissionDenied
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -84,7 +84,7 @@ func (op *Operator) ServerHealth(args *structs.DCSpecificRequest, reply *autopil
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if rule != nil && !rule.OperatorRead() {
|
if rule != nil && rule.OperatorRead(nil) != acl.Allow {
|
||||||
return acl.ErrPermissionDenied
|
return acl.ErrPermissionDenied
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -22,7 +22,7 @@ func (op *Operator) RaftGetConfiguration(args *structs.DCSpecificRequest, reply
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if rule != nil && !rule.OperatorRead() {
|
if rule != nil && rule.OperatorRead(nil) != acl.Allow {
|
||||||
return acl.ErrPermissionDenied
|
return acl.ErrPermissionDenied
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -84,7 +84,7 @@ func (op *Operator) RaftRemovePeerByAddress(args *structs.RaftRemovePeerRequest,
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if rule != nil && !rule.OperatorWrite() {
|
if rule != nil && rule.OperatorWrite(nil) != acl.Allow {
|
||||||
return acl.ErrPermissionDenied
|
return acl.ErrPermissionDenied
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -151,7 +151,7 @@ func (op *Operator) RaftRemovePeerByID(args *structs.RaftRemovePeerRequest, repl
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if rule != nil && !rule.OperatorWrite() {
|
if rule != nil && rule.OperatorWrite(nil) != acl.Allow {
|
||||||
return acl.ErrPermissionDenied
|
return acl.ErrPermissionDenied
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -69,7 +69,7 @@ func (p *PreparedQuery) Apply(args *structs.PreparedQueryRequest, reply *string)
|
||||||
// need to make sure they have write access for whatever they are
|
// need to make sure they have write access for whatever they are
|
||||||
// proposing.
|
// proposing.
|
||||||
if prefix, ok := args.Query.GetACLPrefix(); ok {
|
if prefix, ok := args.Query.GetACLPrefix(); ok {
|
||||||
if rule != nil && !rule.PreparedQueryWrite(prefix) {
|
if rule != nil && rule.PreparedQueryWrite(prefix, nil) != acl.Allow {
|
||||||
p.srv.logger.Printf("[WARN] consul.prepared_query: Operation on prepared query '%s' denied due to ACLs", args.Query.ID)
|
p.srv.logger.Printf("[WARN] consul.prepared_query: Operation on prepared query '%s' denied due to ACLs", args.Query.ID)
|
||||||
return acl.ErrPermissionDenied
|
return acl.ErrPermissionDenied
|
||||||
}
|
}
|
||||||
|
@ -89,7 +89,7 @@ func (p *PreparedQuery) Apply(args *structs.PreparedQueryRequest, reply *string)
|
||||||
}
|
}
|
||||||
|
|
||||||
if prefix, ok := query.GetACLPrefix(); ok {
|
if prefix, ok := query.GetACLPrefix(); ok {
|
||||||
if rule != nil && !rule.PreparedQueryWrite(prefix) {
|
if rule != nil && rule.PreparedQueryWrite(prefix, nil) != acl.Allow {
|
||||||
p.srv.logger.Printf("[WARN] consul.prepared_query: Operation on prepared query '%s' denied due to ACLs", args.Query.ID)
|
p.srv.logger.Printf("[WARN] consul.prepared_query: Operation on prepared query '%s' denied due to ACLs", args.Query.ID)
|
||||||
return acl.ErrPermissionDenied
|
return acl.ErrPermissionDenied
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,6 +18,7 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
metrics "github.com/armon/go-metrics"
|
metrics "github.com/armon/go-metrics"
|
||||||
|
"github.com/hashicorp/consul/acl"
|
||||||
ca "github.com/hashicorp/consul/agent/connect/ca"
|
ca "github.com/hashicorp/consul/agent/connect/ca"
|
||||||
"github.com/hashicorp/consul/agent/consul/autopilot"
|
"github.com/hashicorp/consul/agent/consul/autopilot"
|
||||||
"github.com/hashicorp/consul/agent/consul/fsm"
|
"github.com/hashicorp/consul/agent/consul/fsm"
|
||||||
|
@ -28,7 +29,6 @@ import (
|
||||||
"github.com/hashicorp/consul/agent/structs"
|
"github.com/hashicorp/consul/agent/structs"
|
||||||
"github.com/hashicorp/consul/agent/token"
|
"github.com/hashicorp/consul/agent/token"
|
||||||
"github.com/hashicorp/consul/lib"
|
"github.com/hashicorp/consul/lib"
|
||||||
"github.com/hashicorp/consul/sentinel"
|
|
||||||
"github.com/hashicorp/consul/tlsutil"
|
"github.com/hashicorp/consul/tlsutil"
|
||||||
"github.com/hashicorp/consul/types"
|
"github.com/hashicorp/consul/types"
|
||||||
"github.com/hashicorp/go-hclog"
|
"github.com/hashicorp/go-hclog"
|
||||||
|
@ -107,8 +107,9 @@ var (
|
||||||
// Server is Consul server which manages the service discovery,
|
// Server is Consul server which manages the service discovery,
|
||||||
// health checking, DC forwarding, Raft, and multiple Serf pools.
|
// health checking, DC forwarding, Raft, and multiple Serf pools.
|
||||||
type Server struct {
|
type Server struct {
|
||||||
// sentinel is the Sentinel code engine (can be nil).
|
// enterpriseACLConfig is the Consul Enterprise specific items
|
||||||
sentinel sentinel.Evaluator
|
// necessary for ACLs
|
||||||
|
enterpriseACLConfig *acl.EnterpriseACLConfig
|
||||||
|
|
||||||
// acls is used to resolve tokens to effective policies
|
// acls is used to resolve tokens to effective policies
|
||||||
acls *ACLResolver
|
acls *ACLResolver
|
||||||
|
@ -391,15 +392,15 @@ func NewServerLogger(config *Config, logger *log.Logger, tokens *token.Store, tl
|
||||||
// Initialize the stats fetcher that autopilot will use.
|
// Initialize the stats fetcher that autopilot will use.
|
||||||
s.statsFetcher = NewStatsFetcher(logger, s.connPool, s.config.Datacenter)
|
s.statsFetcher = NewStatsFetcher(logger, s.connPool, s.config.Datacenter)
|
||||||
|
|
||||||
s.sentinel = sentinel.New(logger)
|
s.enterpriseACLConfig = newEnterpriseACLConfig(logger)
|
||||||
s.useNewACLs = 0
|
s.useNewACLs = 0
|
||||||
aclConfig := ACLResolverConfig{
|
aclConfig := ACLResolverConfig{
|
||||||
Config: config,
|
Config: config,
|
||||||
Delegate: s,
|
Delegate: s,
|
||||||
CacheConfig: serverACLCacheConfig,
|
CacheConfig: serverACLCacheConfig,
|
||||||
AutoDisable: false,
|
AutoDisable: false,
|
||||||
Logger: logger,
|
Logger: logger,
|
||||||
Sentinel: s.sentinel,
|
EnterpriseConfig: s.enterpriseACLConfig,
|
||||||
}
|
}
|
||||||
// Initialize the ACL resolver.
|
// Initialize the ACL resolver.
|
||||||
if s.acls, err = NewACLResolver(&aclConfig); err != nil {
|
if s.acls, err = NewACLResolver(&aclConfig); err != nil {
|
||||||
|
|
|
@ -49,12 +49,14 @@ func (s *Session) Apply(args *structs.SessionRequest, reply *string) error {
|
||||||
if existing == nil {
|
if existing == nil {
|
||||||
return fmt.Errorf("Unknown session %q", args.Session.ID)
|
return fmt.Errorf("Unknown session %q", args.Session.ID)
|
||||||
}
|
}
|
||||||
if !rule.SessionWrite(existing.Node) {
|
// TODO (namespaces) - pass through a real ent authz ctx
|
||||||
|
if rule.SessionWrite(existing.Node, nil) != acl.Allow {
|
||||||
return acl.ErrPermissionDenied
|
return acl.ErrPermissionDenied
|
||||||
}
|
}
|
||||||
|
|
||||||
case structs.SessionCreate:
|
case structs.SessionCreate:
|
||||||
if !rule.SessionWrite(args.Session.Node) {
|
// TODO (namespaces) - pass through a real ent authz ctx
|
||||||
|
if rule.SessionWrite(args.Session.Node, nil) != acl.Allow {
|
||||||
return acl.ErrPermissionDenied
|
return acl.ErrPermissionDenied
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -241,7 +243,8 @@ func (s *Session) Renew(args *structs.SessionSpecificRequest,
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if rule != nil && s.srv.config.ACLEnforceVersion8 {
|
if rule != nil && s.srv.config.ACLEnforceVersion8 {
|
||||||
if !rule.SessionWrite(session.Node) {
|
// TODO (namespaces) - pass through a real ent authz ctx
|
||||||
|
if rule.SessionWrite(session.Node, nil) != acl.Allow {
|
||||||
return acl.ErrPermissionDenied
|
return acl.ErrPermissionDenied
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -61,7 +61,7 @@ func (s *Server) dispatchSnapshotRequest(args *structs.SnapshotRequest, in io.Re
|
||||||
// all the ACLs and you could escalate from there.
|
// all the ACLs and you could escalate from there.
|
||||||
if rule, err := s.ResolveToken(args.Token); err != nil {
|
if rule, err := s.ResolveToken(args.Token); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
} else if rule != nil && !rule.Snapshot() {
|
} else if rule != nil && rule.Snapshot(nil) != acl.Allow {
|
||||||
return nil, acl.ErrPermissionDenied
|
return nil, acl.ErrPermissionDenied
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1373,11 +1373,13 @@ func (s *Store) aclPolicySetTxn(tx *memdb.Txn, idx uint64, policy *structs.ACLPo
|
||||||
}
|
}
|
||||||
|
|
||||||
if existing != nil {
|
if existing != nil {
|
||||||
policyMatch := existing.(*structs.ACLPolicy)
|
|
||||||
|
|
||||||
if policy.ID == structs.ACLPolicyGlobalManagementID {
|
if policy.ID == structs.ACLPolicyGlobalManagementID {
|
||||||
// Only the name and description are modifiable
|
// Only the name and description are modifiable
|
||||||
if policy.Rules != policyMatch.Rules {
|
// Here we specifically check that the rules on the global management policy
|
||||||
|
// are identical to the correct policy rules within the binary. This is opposed
|
||||||
|
// to checking against the current rules to allow us to update the rules during
|
||||||
|
// upgrades.
|
||||||
|
if policy.Rules != structs.ACLPolicyGlobalManagement {
|
||||||
return fmt.Errorf("Changing the Rules for the builtin global-management policy is not permitted")
|
return fmt.Errorf("Changing the Rules for the builtin global-management policy is not permitted")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -78,7 +78,7 @@ func (s *HTTPServer) EventList(resp http.ResponseWriter, req *http.Request) (int
|
||||||
// Fetch the ACL token, if any.
|
// Fetch the ACL token, if any.
|
||||||
var token string
|
var token string
|
||||||
s.parseToken(req, &token)
|
s.parseToken(req, &token)
|
||||||
acl, err := s.agent.resolveToken(token)
|
authz, err := s.agent.resolveToken(token)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -128,10 +128,10 @@ RUN_QUERY:
|
||||||
events := s.agent.UserEvents()
|
events := s.agent.UserEvents()
|
||||||
|
|
||||||
// Filter the events using the ACL, if present
|
// Filter the events using the ACL, if present
|
||||||
if acl != nil {
|
if authz != nil {
|
||||||
for i := 0; i < len(events); i++ {
|
for i := 0; i < len(events); i++ {
|
||||||
name := events[i].Name
|
name := events[i].Name
|
||||||
if acl.EventRead(name) {
|
if authz.EventRead(name, nil) == acl.Allow {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
s.agent.logger.Printf("[DEBUG] agent: dropping event %q from result due to ACLs", name)
|
s.agent.logger.Printf("[DEBUG] agent: dropping event %q from result due to ACLs", name)
|
||||||
|
|
|
@ -279,7 +279,7 @@ func (s *HTTPServer) handler(enableDebug bool) http.Handler {
|
||||||
|
|
||||||
// If the token provided does not have the necessary permissions,
|
// If the token provided does not have the necessary permissions,
|
||||||
// write a forbidden response
|
// write a forbidden response
|
||||||
if rule != nil && !rule.OperatorRead() {
|
if rule != nil && rule.OperatorRead(nil) != acl.Allow {
|
||||||
resp.WriteHeader(http.StatusForbidden)
|
resp.WriteHeader(http.StatusForbidden)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,7 +11,6 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/hashicorp/consul/acl"
|
"github.com/hashicorp/consul/acl"
|
||||||
"github.com/hashicorp/consul/sentinel"
|
|
||||||
"golang.org/x/crypto/blake2b"
|
"golang.org/x/crypto/blake2b"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -80,7 +79,7 @@ service_prefix "" {
|
||||||
}
|
}
|
||||||
session_prefix "" {
|
session_prefix "" {
|
||||||
policy = "write"
|
policy = "write"
|
||||||
}`
|
}` + EnterpriseACLPolicyGlobalManagement
|
||||||
|
|
||||||
// This is the policy ID for anonymous access. This is configurable by the
|
// This is the policy ID for anonymous access. This is configurable by the
|
||||||
// user.
|
// user.
|
||||||
|
@ -645,7 +644,7 @@ func (policies ACLPolicyListStubs) Sort() {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (policies ACLPolicies) resolveWithCache(cache *ACLCaches, sentinel sentinel.Evaluator) ([]*acl.Policy, error) {
|
func (policies ACLPolicies) resolveWithCache(cache *ACLCaches, entConf *acl.EnterpriseACLConfig) ([]*acl.Policy, error) {
|
||||||
// Parse the policies
|
// Parse the policies
|
||||||
parsed := make([]*acl.Policy, 0, len(policies))
|
parsed := make([]*acl.Policy, 0, len(policies))
|
||||||
for _, policy := range policies {
|
for _, policy := range policies {
|
||||||
|
@ -658,7 +657,7 @@ func (policies ACLPolicies) resolveWithCache(cache *ACLCaches, sentinel sentinel
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
p, err := acl.NewPolicyFromSource(policy.ID, policy.ModifyIndex, policy.Rules, policy.Syntax, sentinel)
|
p, err := acl.NewPolicyFromSource(policy.ID, policy.ModifyIndex, policy.Rules, policy.Syntax, entConf)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to parse %q: %v", policy.Name, err)
|
return nil, fmt.Errorf("failed to parse %q: %v", policy.Name, err)
|
||||||
}
|
}
|
||||||
|
@ -670,7 +669,7 @@ func (policies ACLPolicies) resolveWithCache(cache *ACLCaches, sentinel sentinel
|
||||||
return parsed, nil
|
return parsed, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (policies ACLPolicies) Compile(parent acl.Authorizer, cache *ACLCaches, sentinel sentinel.Evaluator) (acl.Authorizer, error) {
|
func (policies ACLPolicies) Compile(parent acl.Authorizer, cache *ACLCaches, entConf *acl.EnterpriseACLConfig) (acl.Authorizer, error) {
|
||||||
// Determine the cache key
|
// Determine the cache key
|
||||||
cacheKey := policies.HashKey()
|
cacheKey := policies.HashKey()
|
||||||
entry := cache.GetAuthorizer(cacheKey)
|
entry := cache.GetAuthorizer(cacheKey)
|
||||||
|
@ -679,13 +678,13 @@ func (policies ACLPolicies) Compile(parent acl.Authorizer, cache *ACLCaches, sen
|
||||||
return entry.Authorizer, nil
|
return entry.Authorizer, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
parsed, err := policies.resolveWithCache(cache, sentinel)
|
parsed, err := policies.resolveWithCache(cache, entConf)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to parse the ACL policies: %v", err)
|
return nil, fmt.Errorf("failed to parse the ACL policies: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create the ACL object
|
// Create the ACL object
|
||||||
authorizer, err := acl.NewPolicyAuthorizer(parent, parsed, sentinel)
|
authorizer, err := acl.NewPolicyAuthorizerWithDefaults(parent, parsed, entConf)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to construct ACL Authorizer: %v", err)
|
return nil, fmt.Errorf("failed to construct ACL Authorizer: %v", err)
|
||||||
}
|
}
|
||||||
|
@ -695,8 +694,8 @@ func (policies ACLPolicies) Compile(parent acl.Authorizer, cache *ACLCaches, sen
|
||||||
return authorizer, nil
|
return authorizer, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (policies ACLPolicies) Merge(cache *ACLCaches, sentinel sentinel.Evaluator) (*acl.Policy, error) {
|
func (policies ACLPolicies) Merge(cache *ACLCaches, entConf *acl.EnterpriseACLConfig) (*acl.Policy, error) {
|
||||||
parsed, err := policies.resolveWithCache(cache, sentinel)
|
parsed, err := policies.resolveWithCache(cache, entConf)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
// +build !consulent
|
||||||
|
|
||||||
|
package structs
|
||||||
|
|
||||||
|
const (
|
||||||
|
EnterpriseACLPolicyGlobalManagement = ""
|
||||||
|
)
|
|
@ -665,11 +665,11 @@ func TestStructs_ACLPolicies_Compile(t *testing.T) {
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NotNil(t, authz)
|
require.NotNil(t, authz)
|
||||||
|
|
||||||
require.True(t, authz.NodeRead("foo"))
|
require.Equal(t, acl.Allow, authz.NodeRead("foo", nil))
|
||||||
require.True(t, authz.AgentRead("foo"))
|
require.Equal(t, acl.Allow, authz.AgentRead("foo", nil))
|
||||||
require.True(t, authz.KeyRead("foo"))
|
require.Equal(t, acl.Allow, authz.KeyRead("foo", nil))
|
||||||
require.True(t, authz.ServiceRead("foo"))
|
require.Equal(t, acl.Allow, authz.ServiceRead("foo", nil))
|
||||||
require.False(t, authz.ACLRead())
|
require.Equal(t, acl.Deny, authz.ACLRead(nil))
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("Check Cache", func(t *testing.T) {
|
t.Run("Check Cache", func(t *testing.T) {
|
||||||
|
@ -678,11 +678,11 @@ func TestStructs_ACLPolicies_Compile(t *testing.T) {
|
||||||
authz := entry.Authorizer
|
authz := entry.Authorizer
|
||||||
require.NotNil(t, authz)
|
require.NotNil(t, authz)
|
||||||
|
|
||||||
require.True(t, authz.NodeRead("foo"))
|
require.Equal(t, acl.Allow, authz.NodeRead("foo", nil))
|
||||||
require.True(t, authz.AgentRead("foo"))
|
require.Equal(t, acl.Allow, authz.AgentRead("foo", nil))
|
||||||
require.True(t, authz.KeyRead("foo"))
|
require.Equal(t, acl.Allow, authz.KeyRead("foo", nil))
|
||||||
require.True(t, authz.ServiceRead("foo"))
|
require.Equal(t, acl.Allow, authz.ServiceRead("foo", nil))
|
||||||
require.False(t, authz.ACLRead())
|
require.Equal(t, acl.Deny, authz.ACLRead(nil))
|
||||||
|
|
||||||
// setup the cache for the next test
|
// setup the cache for the next test
|
||||||
cache.PutAuthorizer(testPolicies.HashKey(), acl.DenyAll())
|
cache.PutAuthorizer(testPolicies.HashKey(), acl.DenyAll())
|
||||||
|
@ -694,10 +694,10 @@ func TestStructs_ACLPolicies_Compile(t *testing.T) {
|
||||||
require.NotNil(t, authz)
|
require.NotNil(t, authz)
|
||||||
|
|
||||||
// we reset the Authorizer in the cache so now everything should be denied
|
// we reset the Authorizer in the cache so now everything should be denied
|
||||||
require.False(t, authz.NodeRead("foo"))
|
require.Equal(t, acl.Deny, authz.NodeRead("foo", nil))
|
||||||
require.False(t, authz.AgentRead("foo"))
|
require.Equal(t, acl.Deny, authz.AgentRead("foo", nil))
|
||||||
require.False(t, authz.KeyRead("foo"))
|
require.Equal(t, acl.Deny, authz.KeyRead("foo", nil))
|
||||||
require.False(t, authz.ServiceRead("foo"))
|
require.Equal(t, acl.Deny, authz.ServiceRead("foo", nil))
|
||||||
require.False(t, authz.ACLRead())
|
require.Equal(t, acl.Deny, authz.ACLRead(nil))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -92,11 +92,11 @@ func (e *ServiceConfigEntry) Validate() error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *ServiceConfigEntry) CanRead(rule acl.Authorizer) bool {
|
func (e *ServiceConfigEntry) CanRead(rule acl.Authorizer) bool {
|
||||||
return rule.ServiceRead(e.Name)
|
return rule.ServiceRead(e.Name, nil) == acl.Allow
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *ServiceConfigEntry) CanWrite(rule acl.Authorizer) bool {
|
func (e *ServiceConfigEntry) CanWrite(rule acl.Authorizer) bool {
|
||||||
return rule.ServiceWrite(e.Name, nil)
|
return rule.ServiceWrite(e.Name, nil) == acl.Allow
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *ServiceConfigEntry) GetRaftIndex() *RaftIndex {
|
func (e *ServiceConfigEntry) GetRaftIndex() *RaftIndex {
|
||||||
|
@ -162,7 +162,7 @@ func (e *ProxyConfigEntry) CanRead(rule acl.Authorizer) bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *ProxyConfigEntry) CanWrite(rule acl.Authorizer) bool {
|
func (e *ProxyConfigEntry) CanWrite(rule acl.Authorizer) bool {
|
||||||
return rule.OperatorWrite()
|
return rule.OperatorWrite(nil) == acl.Allow
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *ProxyConfigEntry) GetRaftIndex() *RaftIndex {
|
func (e *ProxyConfigEntry) GetRaftIndex() *RaftIndex {
|
||||||
|
|
|
@ -892,13 +892,13 @@ type discoveryChainConfigEntry interface {
|
||||||
}
|
}
|
||||||
|
|
||||||
func canReadDiscoveryChain(entry discoveryChainConfigEntry, rule acl.Authorizer) bool {
|
func canReadDiscoveryChain(entry discoveryChainConfigEntry, rule acl.Authorizer) bool {
|
||||||
return rule.ServiceRead(entry.GetName())
|
return rule.ServiceRead(entry.GetName(), nil) == acl.Allow
|
||||||
}
|
}
|
||||||
|
|
||||||
func canWriteDiscoveryChain(entry discoveryChainConfigEntry, rule acl.Authorizer) bool {
|
func canWriteDiscoveryChain(entry discoveryChainConfigEntry, rule acl.Authorizer) bool {
|
||||||
name := entry.GetName()
|
name := entry.GetName()
|
||||||
|
|
||||||
if !rule.ServiceWrite(name, nil) {
|
if rule.ServiceWrite(name, nil) != acl.Allow {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -909,7 +909,7 @@ func canWriteDiscoveryChain(entry discoveryChainConfigEntry, rule acl.Authorizer
|
||||||
|
|
||||||
// You only need read on related services to redirect traffic flow for
|
// You only need read on related services to redirect traffic flow for
|
||||||
// your own service.
|
// your own service.
|
||||||
if !rule.ServiceRead(svc) {
|
if rule.ServiceRead(svc, nil) != acl.Allow {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,7 +27,7 @@ func TestConfigEntries_ListRelatedServices_AndACLs(t *testing.T) {
|
||||||
policy, err := acl.NewPolicyFromSource("", 0, buf.String(), acl.SyntaxCurrent, nil)
|
policy, err := acl.NewPolicyFromSource("", 0, buf.String(), acl.SyntaxCurrent, nil)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
authorizer, err := acl.NewPolicyAuthorizer(acl.DenyAll(), []*acl.Policy{policy}, nil)
|
authorizer, err := acl.NewPolicyAuthorizerWithDefaults(acl.DenyAll(), []*acl.Policy{policy}, nil)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
return authorizer
|
return authorizer
|
||||||
}
|
}
|
||||||
|
|
|
@ -251,12 +251,13 @@ func (s *Server) process(stream ADSStream, reqCh <-chan *envoy.DiscoveryRequest)
|
||||||
|
|
||||||
switch cfgSnap.Kind {
|
switch cfgSnap.Kind {
|
||||||
case structs.ServiceKindConnectProxy:
|
case structs.ServiceKindConnectProxy:
|
||||||
if rule != nil && !rule.ServiceWrite(cfgSnap.Proxy.DestinationServiceName, nil) {
|
// TODO (namespaces) - pass through a real ent authz ctx
|
||||||
|
if rule != nil && rule.ServiceWrite(cfgSnap.Proxy.DestinationServiceName, nil) != acl.Allow {
|
||||||
return status.Errorf(codes.PermissionDenied, "permission denied")
|
return status.Errorf(codes.PermissionDenied, "permission denied")
|
||||||
}
|
}
|
||||||
case structs.ServiceKindMeshGateway:
|
case structs.ServiceKindMeshGateway:
|
||||||
// TODO (mesh-gateway) - figure out what ACLs to check for the Gateways
|
// TODO (namespaces) - pass through a real ent authz ctx
|
||||||
if rule != nil && !rule.ServiceWrite(cfgSnap.Service, nil) {
|
if rule != nil && rule.ServiceWrite(cfgSnap.Service, nil) != acl.Allow {
|
||||||
return status.Errorf(codes.PermissionDenied, "permission denied")
|
return status.Errorf(codes.PermissionDenied, "permission denied")
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
|
|
|
@ -450,7 +450,7 @@ func TestServer_StreamAggregatedResources_ACLEnforcement(t *testing.T) {
|
||||||
// Parse the ACL and enforce it
|
// Parse the ACL and enforce it
|
||||||
policy, err := acl.NewPolicyFromSource("", 0, tt.acl, acl.SyntaxLegacy, nil)
|
policy, err := acl.NewPolicyFromSource("", 0, tt.acl, acl.SyntaxLegacy, nil)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
return acl.NewPolicyAuthorizer(acl.RootAuthorizer("deny"), []*acl.Policy{policy}, nil)
|
return acl.NewPolicyAuthorizerWithDefaults(acl.RootAuthorizer("deny"), []*acl.Policy{policy}, nil)
|
||||||
}
|
}
|
||||||
envoy := NewTestEnvoy(t, "web-sidecar-proxy", tt.token)
|
envoy := NewTestEnvoy(t, "web-sidecar-proxy", tt.token)
|
||||||
defer envoy.Close()
|
defer envoy.Close()
|
||||||
|
@ -521,7 +521,7 @@ func TestServer_StreamAggregatedResources_ACLTokenDeleted_StreamTerminatedDuring
|
||||||
return nil, acl.ErrNotFound
|
return nil, acl.ErrNotFound
|
||||||
}
|
}
|
||||||
|
|
||||||
return acl.NewPolicyAuthorizer(acl.RootAuthorizer("deny"), []*acl.Policy{policy}, nil)
|
return acl.NewPolicyAuthorizerWithDefaults(acl.RootAuthorizer("deny"), []*acl.Policy{policy}, nil)
|
||||||
}
|
}
|
||||||
envoy := NewTestEnvoy(t, "web-sidecar-proxy", token)
|
envoy := NewTestEnvoy(t, "web-sidecar-proxy", token)
|
||||||
defer envoy.Close()
|
defer envoy.Close()
|
||||||
|
@ -612,7 +612,7 @@ func TestServer_StreamAggregatedResources_ACLTokenDeleted_StreamTerminatedInBack
|
||||||
return nil, acl.ErrNotFound
|
return nil, acl.ErrNotFound
|
||||||
}
|
}
|
||||||
|
|
||||||
return acl.NewPolicyAuthorizer(acl.RootAuthorizer("deny"), []*acl.Policy{policy}, nil)
|
return acl.NewPolicyAuthorizerWithDefaults(acl.RootAuthorizer("deny"), []*acl.Policy{policy}, nil)
|
||||||
}
|
}
|
||||||
envoy := NewTestEnvoy(t, "web-sidecar-proxy", token)
|
envoy := NewTestEnvoy(t, "web-sidecar-proxy", token)
|
||||||
defer envoy.Close()
|
defer envoy.Close()
|
||||||
|
|
Loading…
Reference in New Issue