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 policy
This commit is contained in:
parent
d53aae22f5
commit
f9a43a1e2d
|
@ -293,7 +293,7 @@ jobs:
|
|||
|
||||
# make dev build of nomad
|
||||
- run:
|
||||
command: make dev
|
||||
command: make pkg/linux_amd64/nomad
|
||||
working_directory: *NOMAD_WORKING_DIR
|
||||
|
||||
# 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
|
||||
// entirely unnecessary so it is now using an exact match.
|
||||
policy := &acl.Policy{
|
||||
Agents: []*acl.AgentPolicy{
|
||||
&acl.AgentPolicy{
|
||||
Node: a.config.NodeName,
|
||||
Policy: acl.PolicyWrite,
|
||||
PolicyRules: acl.PolicyRules{
|
||||
Agents: []*acl.AgentRule{
|
||||
&acl.AgentRule{
|
||||
Node: a.config.NodeName,
|
||||
Policy: acl.PolicyWrite,
|
||||
},
|
||||
},
|
||||
},
|
||||
NodePrefixes: []*acl.NodePolicy{
|
||||
&acl.NodePolicy{
|
||||
Name: "",
|
||||
Policy: acl.PolicyRead,
|
||||
NodePrefixes: []*acl.NodeRule{
|
||||
&acl.NodeRule{
|
||||
Name: "",
|
||||
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 {
|
||||
return err
|
||||
}
|
||||
|
@ -75,14 +77,16 @@ func (a *Agent) vetServiceRegister(token string, service *structs.NodeService) e
|
|||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
// Vet any service that might be getting overwritten.
|
||||
services := a.State.Services()
|
||||
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
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
// since it can be discovered as an instance of that service.
|
||||
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
|
||||
}
|
||||
}
|
||||
|
@ -113,7 +118,8 @@ func (a *Agent) vetServiceUpdate(token string, serviceID string) error {
|
|||
// Vet any changes based on the existing services's info.
|
||||
services := a.State.Services()
|
||||
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
|
||||
}
|
||||
} else {
|
||||
|
@ -137,11 +143,13 @@ func (a *Agent) vetCheckRegister(token string, check *structs.HealthCheck) error
|
|||
|
||||
// Vet the check itself.
|
||||
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
|
||||
}
|
||||
} 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
|
||||
}
|
||||
}
|
||||
|
@ -150,11 +158,13 @@ func (a *Agent) vetCheckRegister(token string, check *structs.HealthCheck) error
|
|||
checks := a.State.Checks()
|
||||
if existing, ok := checks[check.CheckID]; ok {
|
||||
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
|
||||
}
|
||||
} 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
|
||||
}
|
||||
}
|
||||
|
@ -178,11 +188,13 @@ func (a *Agent) vetCheckUpdate(token string, checkID types.CheckID) error {
|
|||
checks := a.State.Checks()
|
||||
if existing, ok := checks[checkID]; ok {
|
||||
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
|
||||
}
|
||||
} 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
|
||||
}
|
||||
}
|
||||
|
@ -208,7 +220,8 @@ func (a *Agent) filterMembers(token string, members *[]serf.Member) error {
|
|||
m := *members
|
||||
for i := 0; i < len(m); i++ {
|
||||
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
|
||||
}
|
||||
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.
|
||||
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
|
||||
}
|
||||
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.
|
||||
for id, check := range *checks {
|
||||
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
|
||||
}
|
||||
} 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
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
// 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
|
||||
}
|
||||
|
||||
|
|
|
@ -195,10 +195,10 @@ func TestACL_AgentMasterToken(t *testing.T) {
|
|||
require.NotNil(t, authz)
|
||||
require.Nil(t, err)
|
||||
|
||||
require.True(t, authz.AgentRead(a.config.NodeName))
|
||||
require.True(t, authz.AgentWrite(a.config.NodeName))
|
||||
require.True(t, authz.NodeRead("foobarbaz"))
|
||||
require.False(t, authz.NodeWrite("foobarbaz", nil))
|
||||
require.Equal(t, acl.Allow, authz.AgentRead(a.config.NodeName, nil))
|
||||
require.Equal(t, acl.Allow, authz.AgentWrite(a.config.NodeName, nil))
|
||||
require.Equal(t, acl.Allow, authz.NodeRead("foobarbaz", nil))
|
||||
require.Equal(t, acl.Deny, authz.NodeWrite("foobarbaz", nil))
|
||||
}
|
||||
|
||||
func TestACL_RootAuthorizersDenied(t *testing.T) {
|
||||
|
@ -225,7 +225,7 @@ func TestACL_RootAuthorizersDenied(t *testing.T) {
|
|||
}
|
||||
|
||||
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
|
||||
|
@ -235,32 +235,42 @@ func catalogPolicy(token string) (acl.Authorizer, error) {
|
|||
|
||||
case "node-ro":
|
||||
return authzFromPolicy(&acl.Policy{
|
||||
NodePrefixes: []*acl.NodePolicy{
|
||||
&acl.NodePolicy{Name: "Node", Policy: "read"},
|
||||
PolicyRules: acl.PolicyRules{
|
||||
NodePrefixes: []*acl.NodeRule{
|
||||
&acl.NodeRule{Name: "Node", Policy: "read"},
|
||||
},
|
||||
},
|
||||
})
|
||||
case "node-rw":
|
||||
return authzFromPolicy(&acl.Policy{
|
||||
NodePrefixes: []*acl.NodePolicy{
|
||||
&acl.NodePolicy{Name: "Node", Policy: "write"},
|
||||
PolicyRules: acl.PolicyRules{
|
||||
NodePrefixes: []*acl.NodeRule{
|
||||
&acl.NodeRule{Name: "Node", Policy: "write"},
|
||||
},
|
||||
},
|
||||
})
|
||||
case "service-ro":
|
||||
return authzFromPolicy(&acl.Policy{
|
||||
ServicePrefixes: []*acl.ServicePolicy{
|
||||
&acl.ServicePolicy{Name: "service", Policy: "read"},
|
||||
PolicyRules: acl.PolicyRules{
|
||||
ServicePrefixes: []*acl.ServiceRule{
|
||||
&acl.ServiceRule{Name: "service", Policy: "read"},
|
||||
},
|
||||
},
|
||||
})
|
||||
case "service-rw":
|
||||
return authzFromPolicy(&acl.Policy{
|
||||
ServicePrefixes: []*acl.ServicePolicy{
|
||||
&acl.ServicePolicy{Name: "service", Policy: "write"},
|
||||
PolicyRules: acl.PolicyRules{
|
||||
ServicePrefixes: []*acl.ServiceRule{
|
||||
&acl.ServiceRule{Name: "service", Policy: "write"},
|
||||
},
|
||||
},
|
||||
})
|
||||
case "other-rw":
|
||||
return authzFromPolicy(&acl.Policy{
|
||||
ServicePrefixes: []*acl.ServicePolicy{
|
||||
&acl.ServicePolicy{Name: "other", Policy: "write"},
|
||||
PolicyRules: acl.PolicyRules{
|
||||
ServicePrefixes: []*acl.ServiceRule{
|
||||
&acl.ServiceRule{Name: "other", Policy: "write"},
|
||||
},
|
||||
},
|
||||
})
|
||||
default:
|
||||
|
|
|
@ -48,7 +48,7 @@ func (s *HTTPServer) AgentSelf(resp http.ResponseWriter, req *http.Request) (int
|
|||
if err != nil {
|
||||
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
|
||||
}
|
||||
|
||||
|
@ -101,7 +101,7 @@ func (s *HTTPServer) AgentMetrics(resp http.ResponseWriter, req *http.Request) (
|
|||
if err != nil {
|
||||
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
|
||||
}
|
||||
if enablePrometheusOutput(req) {
|
||||
|
@ -130,7 +130,7 @@ func (s *HTTPServer) AgentReload(resp http.ResponseWriter, req *http.Request) (i
|
|||
if err != nil {
|
||||
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
|
||||
}
|
||||
|
||||
|
@ -281,7 +281,8 @@ func (s *HTTPServer) AgentService(resp http.ResponseWriter, req *http.Request) (
|
|||
if err != nil {
|
||||
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
|
||||
}
|
||||
|
||||
|
@ -388,7 +389,7 @@ func (s *HTTPServer) AgentJoin(resp http.ResponseWriter, req *http.Request) (int
|
|||
if err != nil {
|
||||
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
|
||||
}
|
||||
|
||||
|
@ -416,7 +417,7 @@ func (s *HTTPServer) AgentLeave(resp http.ResponseWriter, req *http.Request) (in
|
|||
if err != nil {
|
||||
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
|
||||
}
|
||||
|
||||
|
@ -434,7 +435,7 @@ func (s *HTTPServer) AgentForceLeave(resp http.ResponseWriter, req *http.Request
|
|||
if err != nil {
|
||||
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
|
||||
}
|
||||
|
||||
|
@ -1049,7 +1050,8 @@ func (s *HTTPServer) AgentNodeMaintenance(resp http.ResponseWriter, req *http.Re
|
|||
if err != nil {
|
||||
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
|
||||
}
|
||||
|
||||
|
@ -1070,7 +1072,7 @@ func (s *HTTPServer) AgentMonitor(resp http.ResponseWriter, req *http.Request) (
|
|||
if err != nil {
|
||||
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
|
||||
}
|
||||
|
||||
|
@ -1165,7 +1167,7 @@ func (s *HTTPServer) AgentToken(resp http.ResponseWriter, req *http.Request) (in
|
|||
if err != nil {
|
||||
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
|
||||
}
|
||||
|
||||
|
@ -1370,7 +1372,8 @@ func (s *HTTPServer) AgentHost(resp http.ResponseWriter, req *http.Request) (int
|
|||
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
|
||||
}
|
||||
|
||||
|
|
|
@ -57,7 +57,8 @@ func (a *Agent) ConnectAuthorize(token string,
|
|||
if err != nil {
|
||||
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)
|
||||
}
|
||||
|
||||
|
@ -115,5 +116,6 @@ func (a *Agent) ConnectAuthorize(token string,
|
|||
return true, "ACLs disabled, access is allowed by default", &meta, nil
|
||||
}
|
||||
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"
|
||||
"github.com/hashicorp/consul/acl"
|
||||
"github.com/hashicorp/consul/agent/structs"
|
||||
"github.com/hashicorp/consul/api"
|
||||
"github.com/hashicorp/consul/sentinel"
|
||||
"golang.org/x/sync/singleflight"
|
||||
"golang.org/x/time/rate"
|
||||
)
|
||||
|
@ -126,7 +124,8 @@ type ACLResolverConfig struct {
|
|||
// so that it can detect when the servers have gotten ACLs enabled.
|
||||
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.
|
||||
|
@ -159,7 +158,7 @@ type ACLResolver struct {
|
|||
logger *log.Logger
|
||||
|
||||
delegate ACLResolverDelegate
|
||||
sentinel sentinel.Evaluator
|
||||
entConf *acl.EnterpriseACLConfig
|
||||
|
||||
cache *structs.ACLCaches
|
||||
identityGroup singleflight.Group
|
||||
|
@ -212,7 +211,7 @@ func NewACLResolver(config *ACLResolverConfig) (*ACLResolver, error) {
|
|||
config: config.Config,
|
||||
logger: config.Logger,
|
||||
delegate: config.Delegate,
|
||||
sentinel: config.Sentinel,
|
||||
entConf: config.EnterpriseConfig,
|
||||
cache: cache,
|
||||
autoDisable: config.AutoDisable,
|
||||
down: down,
|
||||
|
@ -249,7 +248,7 @@ func (r *ACLResolver) fetchAndCacheTokenLegacy(token string, cached *structs.Aut
|
|||
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)
|
||||
return authorizer, err
|
||||
|
@ -292,7 +291,7 @@ func (r *ACLResolver) resolveTokenLegacy(token string) (acl.Authorizer, error) {
|
|||
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
|
||||
|
@ -1005,7 +1004,7 @@ func (r *ACLResolver) ResolveToken(token string) (acl.Authorizer, error) {
|
|||
}
|
||||
|
||||
// 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
|
||||
|
||||
}
|
||||
|
@ -1035,7 +1034,7 @@ func (r *ACLResolver) GetMergedPolicyForToken(token string) (*acl.Policy, error)
|
|||
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
|
||||
|
@ -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.
|
||||
func (f *aclFilter) allowNode(node string) bool {
|
||||
func (f *aclFilter) allowNode(node string, ent *acl.EnterpriseAuthorizerContext) bool {
|
||||
if !f.enforceVersion8 {
|
||||
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.
|
||||
func (f *aclFilter) allowService(service string) bool {
|
||||
func (f *aclFilter) allowService(service string, ent *acl.EnterpriseAuthorizerContext) bool {
|
||||
if service == "" {
|
||||
return true
|
||||
}
|
||||
|
@ -1075,16 +1074,16 @@ func (f *aclFilter) allowService(service string) bool {
|
|||
if !f.enforceVersion8 && service == structs.ConsulServiceID {
|
||||
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
|
||||
// an ACL.
|
||||
func (f *aclFilter) allowSession(node string) bool {
|
||||
func (f *aclFilter) allowSession(node string, ent *acl.EnterpriseAuthorizerContext) bool {
|
||||
if !f.enforceVersion8 {
|
||||
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
|
||||
|
@ -1093,7 +1092,8 @@ func (f *aclFilter) filterHealthChecks(checks *structs.HealthChecks) {
|
|||
hc := *checks
|
||||
for i := 0; i < len(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
|
||||
}
|
||||
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.
|
||||
func (f *aclFilter) filterServices(services structs.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
|
||||
}
|
||||
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
|
||||
for i := 0; i < len(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
|
||||
}
|
||||
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
|
||||
}
|
||||
|
||||
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
|
||||
return
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
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
|
||||
for i := 0; i < len(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
|
||||
}
|
||||
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
|
||||
for i := 0; i < len(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
|
||||
}
|
||||
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
|
||||
for i := 0; i < len(c); i++ {
|
||||
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
|
||||
}
|
||||
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.
|
||||
func (f *aclFilter) filterIntentions(ixns *structs.Intentions) {
|
||||
// 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
|
||||
}
|
||||
|
||||
|
@ -1212,7 +1220,8 @@ func (f *aclFilter) filterIntentions(ixns *structs.Intentions) {
|
|||
// we know at this point the user doesn't have a management
|
||||
// token, otherwise see what the policy says.
|
||||
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)
|
||||
continue
|
||||
}
|
||||
|
@ -1231,7 +1240,8 @@ func (f *aclFilter) filterNodeDump(dump *structs.NodeDump) {
|
|||
info := nd[i]
|
||||
|
||||
// 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)
|
||||
nd = append(nd[:i], nd[i+1:]...)
|
||||
i--
|
||||
|
@ -1241,7 +1251,8 @@ func (f *aclFilter) filterNodeDump(dump *structs.NodeDump) {
|
|||
// Filter services
|
||||
for j := 0; j < len(info.Services); j++ {
|
||||
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
|
||||
}
|
||||
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
|
||||
for j := 0; j < len(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
|
||||
}
|
||||
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
|
||||
for i := 0; i < len(n); i++ {
|
||||
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
|
||||
}
|
||||
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.
|
||||
func (f *aclFilter) redactPreparedQueryTokens(query **structs.PreparedQuery) {
|
||||
// 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
|
||||
}
|
||||
|
||||
|
@ -1312,7 +1326,11 @@ func (f *aclFilter) redactPreparedQueryTokens(query **structs.PreparedQuery) {
|
|||
// if the user doesn't have a management token.
|
||||
func (f *aclFilter) filterPreparedQueries(queries *structs.PreparedQueries) {
|
||||
// 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
|
||||
}
|
||||
|
||||
|
@ -1323,7 +1341,7 @@ func (f *aclFilter) filterPreparedQueries(queries *structs.PreparedQueries) {
|
|||
// we know at this point the user doesn't have a management
|
||||
// token, otherwise see what the policy says.
|
||||
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)
|
||||
continue
|
||||
}
|
||||
|
@ -1338,7 +1356,8 @@ func (f *aclFilter) filterPreparedQueries(queries *structs.PreparedQueries) {
|
|||
}
|
||||
|
||||
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
|
||||
}
|
||||
clone := *(*token)
|
||||
|
@ -1454,46 +1473,49 @@ func vetRegisterWithACL(rule acl.Authorizer, subj *structs.RegisterRequest,
|
|||
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
|
||||
// return the structure we made if we have it.
|
||||
var memo map[string]interface{}
|
||||
scope := func() map[string]interface{} {
|
||||
if memo != nil {
|
||||
return memo
|
||||
}
|
||||
// var memo map[string]interface{}
|
||||
// scope := func() map[string]interface{} {
|
||||
// if memo != nil {
|
||||
// return memo
|
||||
// }
|
||||
|
||||
node := &api.Node{
|
||||
ID: string(subj.ID),
|
||||
Node: subj.Node,
|
||||
Address: subj.Address,
|
||||
Datacenter: subj.Datacenter,
|
||||
TaggedAddresses: subj.TaggedAddresses,
|
||||
Meta: subj.NodeMeta,
|
||||
}
|
||||
// node := &api.Node{
|
||||
// ID: string(subj.ID),
|
||||
// Node: subj.Node,
|
||||
// Address: subj.Address,
|
||||
// Datacenter: subj.Datacenter,
|
||||
// TaggedAddresses: subj.TaggedAddresses,
|
||||
// Meta: subj.NodeMeta,
|
||||
// }
|
||||
|
||||
var service *api.AgentService
|
||||
if subj.Service != nil {
|
||||
service = &api.AgentService{
|
||||
ID: subj.Service.ID,
|
||||
Service: subj.Service.Service,
|
||||
Tags: subj.Service.Tags,
|
||||
Meta: subj.Service.Meta,
|
||||
Address: subj.Service.Address,
|
||||
Port: subj.Service.Port,
|
||||
EnableTagOverride: subj.Service.EnableTagOverride,
|
||||
}
|
||||
}
|
||||
// var service *api.AgentService
|
||||
// if subj.Service != nil {
|
||||
// service = &api.AgentService{
|
||||
// ID: subj.Service.ID,
|
||||
// Service: subj.Service.Service,
|
||||
// Tags: subj.Service.Tags,
|
||||
// Meta: subj.Service.Meta,
|
||||
// Address: subj.Service.Address,
|
||||
// Port: subj.Service.Port,
|
||||
// EnableTagOverride: subj.Service.EnableTagOverride,
|
||||
// }
|
||||
// }
|
||||
|
||||
memo = sentinel.ScopeCatalogUpsert(node, service)
|
||||
return memo
|
||||
}
|
||||
// memo = sentinel.ScopeCatalogUpsert(node, service)
|
||||
// return memo
|
||||
// }
|
||||
|
||||
// Vet the node info. This allows service updates to re-post the required
|
||||
// node info for each request without having to have node "write"
|
||||
// privileges.
|
||||
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
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
// is being modified by id (if any).
|
||||
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
|
||||
}
|
||||
|
||||
|
@ -1511,7 +1534,8 @@ func vetRegisterWithACL(rule acl.Authorizer, subj *structs.RegisterRequest,
|
|||
// This is effectively a delete, so we DO NOT apply the
|
||||
// sentinel scope to the service we are overwriting, just
|
||||
// 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
|
||||
}
|
||||
}
|
||||
|
@ -1540,7 +1564,8 @@ func vetRegisterWithACL(rule acl.Authorizer, subj *structs.RegisterRequest,
|
|||
|
||||
// Node-level check.
|
||||
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
|
||||
}
|
||||
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,
|
||||
// since the sentinel policy doesn't apply to adding checks at
|
||||
// this time.
|
||||
if !rule.ServiceWrite(other.Service, nil) {
|
||||
if rule.ServiceWrite(other.Service, nil) != acl.Allow {
|
||||
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.
|
||||
// This accounts for cases where the agent no longer has a token with write permission
|
||||
// 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
|
||||
}
|
||||
|
||||
|
@ -1607,7 +1633,8 @@ func vetDeregisterWithACL(rule acl.Authorizer, subj *structs.DeregisterRequest,
|
|||
if ns == nil {
|
||||
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
|
||||
}
|
||||
} else if subj.CheckID != "" {
|
||||
|
@ -1615,11 +1642,13 @@ func vetDeregisterWithACL(rule acl.Authorizer, subj *structs.DeregisterRequest,
|
|||
return fmt.Errorf("Unknown check '%s'", subj.CheckID)
|
||||
}
|
||||
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
|
||||
}
|
||||
} 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
|
||||
}
|
||||
}
|
||||
|
@ -1641,24 +1670,27 @@ func vetNodeTxnOp(op *structs.TxnNodeOp, rule acl.Authorizer) error {
|
|||
|
||||
node := op.Node
|
||||
|
||||
n := &api.Node{
|
||||
Node: node.Node,
|
||||
ID: string(node.ID),
|
||||
Address: node.Address,
|
||||
Datacenter: node.Datacenter,
|
||||
TaggedAddresses: node.TaggedAddresses,
|
||||
Meta: node.Meta,
|
||||
}
|
||||
// TODO (namespaces) uncomment once we bring back sentinel scope creation in the authz ctx
|
||||
// n := &api.Node{
|
||||
// Node: node.Node,
|
||||
// ID: string(node.ID),
|
||||
// Address: node.Address,
|
||||
// Datacenter: node.Datacenter,
|
||||
// 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.
|
||||
var scope func() map[string]interface{}
|
||||
if op.Verb != api.NodeDelete && op.Verb != api.NodeDeleteCAS {
|
||||
scope = func() map[string]interface{} {
|
||||
return sentinel.ScopeCatalogUpsert(n, nil)
|
||||
}
|
||||
}
|
||||
// var scope func() map[string]interface{}
|
||||
// if op.Verb != api.NodeDelete && op.Verb != api.NodeDeleteCAS {
|
||||
// scope = func() map[string]interface{} {
|
||||
// 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
|
||||
}
|
||||
|
||||
|
@ -1674,23 +1706,25 @@ func vetServiceTxnOp(op *structs.TxnServiceOp, rule acl.Authorizer) error {
|
|||
|
||||
service := op.Service
|
||||
|
||||
n := &api.Node{Node: op.Node}
|
||||
svc := &api.AgentService{
|
||||
ID: service.ID,
|
||||
Service: service.Service,
|
||||
Tags: service.Tags,
|
||||
Meta: service.Meta,
|
||||
Address: service.Address,
|
||||
Port: service.Port,
|
||||
EnableTagOverride: service.EnableTagOverride,
|
||||
}
|
||||
var scope func() map[string]interface{}
|
||||
if op.Verb != api.ServiceDelete && op.Verb != api.ServiceDeleteCAS {
|
||||
scope = func() map[string]interface{} {
|
||||
return sentinel.ScopeCatalogUpsert(n, svc)
|
||||
}
|
||||
}
|
||||
if !rule.ServiceWrite(service.Service, scope) {
|
||||
// TODO (namespaces) update to create authz context with the sentinel scope
|
||||
// n := &api.Node{Node: op.Node}
|
||||
// svc := &api.AgentService{
|
||||
// ID: service.ID,
|
||||
// Service: service.Service,
|
||||
// Tags: service.Tags,
|
||||
// Meta: service.Meta,
|
||||
// Address: service.Address,
|
||||
// Port: service.Port,
|
||||
// EnableTagOverride: service.EnableTagOverride,
|
||||
// }
|
||||
// var scope func() map[string]interface{}
|
||||
// if op.Verb != api.ServiceDelete && op.Verb != api.ServiceDeleteCAS {
|
||||
// scope = func() map[string]interface{} {
|
||||
// return sentinel.ScopeCatalogUpsert(n, svc)
|
||||
// }
|
||||
// }
|
||||
// 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
|
||||
}
|
||||
|
||||
|
@ -1704,31 +1738,36 @@ func vetCheckTxnOp(op *structs.TxnCheckOp, rule acl.Authorizer) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
n := &api.Node{Node: op.Check.Node}
|
||||
svc := &api.AgentService{
|
||||
ID: op.Check.ServiceID,
|
||||
Service: op.Check.ServiceID,
|
||||
Tags: op.Check.ServiceTags,
|
||||
}
|
||||
var scope func() map[string]interface{}
|
||||
// TODO (namespaces) uncomment once these are used for sentinel scope creation
|
||||
// n := &api.Node{Node: op.Check.Node}
|
||||
// svc := &api.AgentService{
|
||||
// ID: op.Check.ServiceID,
|
||||
// Service: op.Check.ServiceID,
|
||||
// Tags: op.Check.ServiceTags,
|
||||
// }
|
||||
// var scope func() map[string]interface{}
|
||||
if op.Check.ServiceID == "" {
|
||||
// Node-level check.
|
||||
if op.Verb == api.CheckDelete || op.Verb == api.CheckDeleteCAS {
|
||||
scope = func() map[string]interface{} {
|
||||
return sentinel.ScopeCatalogUpsert(n, svc)
|
||||
}
|
||||
}
|
||||
if !rule.NodeWrite(op.Check.Node, scope) {
|
||||
// TODO (namespaces) update to create authz with sentinel scope
|
||||
// if op.Verb == api.CheckDelete || op.Verb == api.CheckDeleteCAS {
|
||||
// scope = func() map[string]interface{} {
|
||||
// return sentinel.ScopeCatalogUpsert(n, svc)
|
||||
// }
|
||||
// }
|
||||
// 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
|
||||
}
|
||||
} else {
|
||||
// Service-level check.
|
||||
if op.Verb == api.CheckDelete || op.Verb == api.CheckDeleteCAS {
|
||||
scope = func() map[string]interface{} {
|
||||
return sentinel.ScopeCatalogUpsert(n, svc)
|
||||
}
|
||||
}
|
||||
if !rule.ServiceWrite(op.Check.ServiceName, scope) {
|
||||
// TODO (namespaces) update to create authz with sentinel scope
|
||||
// if op.Verb == api.CheckDelete || op.Verb == api.CheckDeleteCAS {
|
||||
// scope = func() map[string]interface{} {
|
||||
// return sentinel.ScopeCatalogUpsert(n, svc)
|
||||
// }
|
||||
// }
|
||||
// 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
|
||||
}
|
||||
}
|
||||
|
|
|
@ -206,9 +206,10 @@ func (a *ACL) TokenRead(args *structs.ACLTokenGetRequest, reply *structs.ACLToke
|
|||
// Only ACLRead privileges are required to list tokens
|
||||
// However if you do not have ACLWrite as well the token
|
||||
// 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 {
|
||||
return err
|
||||
} else if rule == nil || !rule.ACLRead() {
|
||||
} else if rule == nil || rule.ACLRead(nil) != acl.Allow {
|
||||
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)
|
||||
if token != nil {
|
||||
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
|
||||
}
|
||||
}
|
||||
|
@ -261,9 +263,10 @@ func (a *ACL) TokenClone(args *structs.ACLTokenSetRequest, reply *structs.ACLTok
|
|||
|
||||
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 {
|
||||
return err
|
||||
} else if rule == nil || !rule.ACLWrite() {
|
||||
} else if rule == nil || rule.ACLWrite(nil) != acl.Allow {
|
||||
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())
|
||||
|
||||
// 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 {
|
||||
return err
|
||||
} else if rule == nil || !rule.ACLWrite() {
|
||||
} else if rule == nil || rule.ACLWrite(nil) != acl.Allow {
|
||||
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())
|
||||
|
||||
// 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 {
|
||||
return err
|
||||
} else if rule == nil || !rule.ACLWrite() {
|
||||
} else if rule == nil || rule.ACLWrite(nil) != acl.Allow {
|
||||
return acl.ErrPermissionDenied
|
||||
}
|
||||
|
||||
|
@ -760,9 +765,10 @@ func (a *ACL) TokenList(args *structs.ACLTokenListRequest, reply *structs.ACLTok
|
|||
}
|
||||
|
||||
rule, err := a.srv.ResolveToken(args.Token)
|
||||
// TODO (namespaces) update to call ACLRead with an authz context once ACLs support it
|
||||
if err != nil {
|
||||
return err
|
||||
} else if rule == nil || !rule.ACLRead() {
|
||||
} else if rule == nil || rule.ACLRead(nil) != acl.Allow {
|
||||
return acl.ErrPermissionDenied
|
||||
}
|
||||
|
||||
|
@ -801,9 +807,10 @@ func (a *ACL) TokenBatchRead(args *structs.ACLTokenBatchGetRequest, reply *struc
|
|||
}
|
||||
|
||||
rule, err := a.srv.ResolveToken(args.Token)
|
||||
// TODO (namespaces) update to call ACLRead with an authz context once ACLs support it
|
||||
if err != nil {
|
||||
return err
|
||||
} else if rule == nil || !rule.ACLRead() {
|
||||
} else if rule == nil || rule.ACLRead(nil) != acl.Allow {
|
||||
return acl.ErrPermissionDenied
|
||||
}
|
||||
|
||||
|
@ -819,7 +826,8 @@ func (a *ACL) TokenBatchRead(args *structs.ACLTokenBatchGetRequest, reply *struc
|
|||
a.srv.filterACLWithAuthorizer(rule, &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
|
||||
})
|
||||
}
|
||||
|
@ -835,7 +843,7 @@ func (a *ACL) PolicyRead(args *structs.ACLPolicyGetRequest, reply *structs.ACLPo
|
|||
|
||||
if rule, err := a.srv.ResolveToken(args.Token); err != nil {
|
||||
return err
|
||||
} else if rule == nil || !rule.ACLRead() {
|
||||
} else if rule == nil || rule.ACLRead(nil) != acl.Allow {
|
||||
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 {
|
||||
return err
|
||||
} else if rule == nil || !rule.ACLRead() {
|
||||
} else if rule == nil || rule.ACLRead(nil) != acl.Allow {
|
||||
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())
|
||||
|
||||
// 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 {
|
||||
return err
|
||||
} else if rule == nil || !rule.ACLWrite() {
|
||||
} else if rule == nil || rule.ACLWrite(nil) != acl.Allow {
|
||||
return acl.ErrPermissionDenied
|
||||
}
|
||||
|
||||
|
@ -971,7 +980,7 @@ func (a *ACL) PolicySet(args *structs.ACLPolicySetRequest, reply *structs.ACLPol
|
|||
}
|
||||
|
||||
// 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 {
|
||||
return err
|
||||
}
|
||||
|
@ -1018,9 +1027,10 @@ func (a *ACL) PolicyDelete(args *structs.ACLPolicyDeleteRequest, reply *string)
|
|||
defer metrics.MeasureSince([]string{"acl", "policy", "delete"}, time.Now())
|
||||
|
||||
// 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 {
|
||||
return err
|
||||
} else if rule == nil || !rule.ACLWrite() {
|
||||
} else if rule == nil || rule.ACLWrite(nil) != acl.Allow {
|
||||
return acl.ErrPermissionDenied
|
||||
}
|
||||
|
||||
|
@ -1068,9 +1078,10 @@ func (a *ACL) PolicyList(args *structs.ACLPolicyListRequest, reply *structs.ACLP
|
|||
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 {
|
||||
return err
|
||||
} else if rule == nil || !rule.ACLRead() {
|
||||
} else if rule == nil || rule.ACLRead(nil) != acl.Allow {
|
||||
return acl.ErrPermissionDenied
|
||||
}
|
||||
|
||||
|
@ -1213,9 +1224,10 @@ func (a *ACL) RoleRead(args *structs.ACLRoleGetRequest, reply *structs.ACLRoleRe
|
|||
return err
|
||||
}
|
||||
|
||||
// TODO (namespaces) update to create and use actual enterprise authorizer context
|
||||
if rule, err := a.srv.ResolveToken(args.Token); err != nil {
|
||||
return err
|
||||
} else if rule == nil || !rule.ACLRead() {
|
||||
} else if rule == nil || rule.ACLRead(nil) != acl.Allow {
|
||||
return acl.ErrPermissionDenied
|
||||
}
|
||||
|
||||
|
@ -1250,9 +1262,10 @@ func (a *ACL) RoleBatchRead(args *structs.ACLRoleBatchGetRequest, reply *structs
|
|||
return err
|
||||
}
|
||||
|
||||
// TODO (namespaces) update to create and use actual enterprise authorizer context
|
||||
if rule, err := a.srv.ResolveToken(args.Token); err != nil {
|
||||
return err
|
||||
} else if rule == nil || !rule.ACLRead() {
|
||||
} else if rule == nil || rule.ACLRead(nil) != acl.Allow {
|
||||
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())
|
||||
|
||||
// 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 {
|
||||
return err
|
||||
} else if rule == nil || !rule.ACLWrite() {
|
||||
} else if rule == nil || rule.ACLWrite(nil) != acl.Allow {
|
||||
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())
|
||||
|
||||
// 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 {
|
||||
return err
|
||||
} else if rule == nil || !rule.ACLWrite() {
|
||||
} else if rule == nil || rule.ACLWrite(nil) != acl.Allow {
|
||||
return acl.ErrPermissionDenied
|
||||
}
|
||||
|
||||
|
@ -1468,9 +1483,10 @@ func (a *ACL) RoleList(args *structs.ACLRoleListRequest, reply *structs.ACLRoleL
|
|||
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 {
|
||||
return err
|
||||
} else if rule == nil || !rule.ACLRead() {
|
||||
} else if rule == nil || rule.ACLRead(nil) != acl.Allow {
|
||||
return acl.ErrPermissionDenied
|
||||
}
|
||||
|
||||
|
@ -1544,9 +1560,10 @@ func (a *ACL) BindingRuleRead(args *structs.ACLBindingRuleGetRequest, reply *str
|
|||
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 {
|
||||
return err
|
||||
} else if rule == nil || !rule.ACLRead() {
|
||||
} else if rule == nil || rule.ACLRead(nil) != acl.Allow {
|
||||
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())
|
||||
|
||||
// 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 {
|
||||
return err
|
||||
} else if rule == nil || !rule.ACLWrite() {
|
||||
} else if rule == nil || rule.ACLWrite(nil) != acl.Allow {
|
||||
return acl.ErrPermissionDenied
|
||||
}
|
||||
|
||||
|
@ -1696,9 +1714,10 @@ func (a *ACL) BindingRuleDelete(args *structs.ACLBindingRuleDeleteRequest, reply
|
|||
defer metrics.MeasureSince([]string{"acl", "bindingrule", "delete"}, time.Now())
|
||||
|
||||
// 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 {
|
||||
return err
|
||||
} else if rule == nil || !rule.ACLWrite() {
|
||||
} else if rule == nil || rule.ACLWrite(nil) != acl.Allow {
|
||||
return acl.ErrPermissionDenied
|
||||
}
|
||||
|
||||
|
@ -1742,9 +1761,10 @@ func (a *ACL) BindingRuleList(args *structs.ACLBindingRuleListRequest, reply *st
|
|||
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 {
|
||||
return err
|
||||
} else if rule == nil || !rule.ACLRead() {
|
||||
} else if rule == nil || rule.ACLRead(nil) != acl.Allow {
|
||||
return acl.ErrPermissionDenied
|
||||
}
|
||||
|
||||
|
@ -1773,9 +1793,10 @@ func (a *ACL) AuthMethodRead(args *structs.ACLAuthMethodGetRequest, reply *struc
|
|||
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 {
|
||||
return err
|
||||
} else if rule == nil || !rule.ACLRead() {
|
||||
} else if rule == nil || rule.ACLRead(nil) != acl.Allow {
|
||||
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())
|
||||
|
||||
// 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 {
|
||||
return err
|
||||
} else if rule == nil || !rule.ACLWrite() {
|
||||
} else if rule == nil || rule.ACLWrite(nil) != acl.Allow {
|
||||
return acl.ErrPermissionDenied
|
||||
}
|
||||
|
||||
|
@ -1885,9 +1907,10 @@ func (a *ACL) AuthMethodDelete(args *structs.ACLAuthMethodDeleteRequest, reply *
|
|||
defer metrics.MeasureSince([]string{"acl", "authmethod", "delete"}, time.Now())
|
||||
|
||||
// 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 {
|
||||
return err
|
||||
} else if rule == nil || !rule.ACLWrite() {
|
||||
} else if rule == nil || rule.ACLWrite(nil) != acl.Allow {
|
||||
return acl.ErrPermissionDenied
|
||||
}
|
||||
|
||||
|
@ -1931,9 +1954,10 @@ func (a *ACL) AuthMethodList(args *structs.ACLAuthMethodListRequest, reply *stru
|
|||
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 {
|
||||
return err
|
||||
} else if rule == nil || !rule.ACLRead() {
|
||||
} else if rule == nil || rule.ACLRead(nil) != acl.Allow {
|
||||
return acl.ErrPermissionDenied
|
||||
}
|
||||
|
||||
|
|
|
@ -114,7 +114,7 @@ func aclApplyInternal(srv *Server, args *structs.ACLRequest, reply *string) erro
|
|||
}
|
||||
|
||||
// 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 {
|
||||
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
|
||||
// NOTE: We will not support enterprise authorizer contexts with legacy ACLs
|
||||
if rule, err := a.srv.ResolveToken(args.Token); err != nil {
|
||||
return err
|
||||
} else if rule == nil || !rule.ACLWrite() {
|
||||
} else if rule == nil || rule.ACLWrite(nil) != acl.Allow {
|
||||
return acl.ErrPermissionDenied
|
||||
}
|
||||
|
||||
|
@ -198,6 +199,10 @@ func (a *ACL) Get(args *structs.ACLSpecificRequest,
|
|||
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
|
||||
if !a.srv.ACLsEnabled() {
|
||||
return acl.ErrDisabled
|
||||
|
@ -246,9 +251,11 @@ func (a *ACL) List(args *structs.DCSpecificRequest,
|
|||
}
|
||||
|
||||
// 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 {
|
||||
return err
|
||||
} else if rule == nil || !rule.ACLWrite() {
|
||||
} else if rule == nil || rule.ACLWrite(nil) != acl.Allow {
|
||||
return acl.ErrPermissionDenied
|
||||
}
|
||||
|
||||
|
|
|
@ -212,7 +212,7 @@ func TestACLEndpoint_Update_PurgeCache(t *testing.T) {
|
|||
if acl1 == nil {
|
||||
t.Fatalf("should not be nil")
|
||||
}
|
||||
if !acl1.KeyRead("foo") {
|
||||
if acl1.KeyRead("foo", nil) != acl.Allow {
|
||||
t.Fatalf("should be allowed")
|
||||
}
|
||||
|
||||
|
@ -234,7 +234,7 @@ func TestACLEndpoint_Update_PurgeCache(t *testing.T) {
|
|||
if acl2 == acl1 {
|
||||
t.Fatalf("should not be cached")
|
||||
}
|
||||
if acl2.KeyRead("foo") {
|
||||
if acl2.KeyRead("foo", nil) == acl.Allow {
|
||||
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")
|
||||
require.NoError(t, err)
|
||||
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, "dc2-key-wr", true, "cached") // from "found" token
|
||||
|
@ -774,7 +774,7 @@ func TestACLResolver_DownPolicy(t *testing.T) {
|
|||
require.NoError(t, err)
|
||||
require.NotNil(t, 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, "dc2-key-wr", false, "expired") // from "found" token
|
||||
|
@ -802,14 +802,14 @@ func TestACLResolver_DownPolicy(t *testing.T) {
|
|||
authz, err := r.ResolveToken("found-role")
|
||||
require.NoError(t, err)
|
||||
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
|
||||
authz2, err := r.ResolveToken("found-role")
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, 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) {
|
||||
|
@ -832,7 +832,7 @@ func TestACLResolver_DownPolicy(t *testing.T) {
|
|||
authz, err := r.ResolveToken("found")
|
||||
require.NoError(t, err)
|
||||
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")
|
||||
|
||||
|
@ -841,7 +841,7 @@ func TestACLResolver_DownPolicy(t *testing.T) {
|
|||
require.NotNil(t, authz2)
|
||||
// testing pointer equality - these will be the same object because it is cached.
|
||||
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) {
|
||||
|
@ -864,7 +864,7 @@ func TestACLResolver_DownPolicy(t *testing.T) {
|
|||
authz, err := r.ResolveToken("found-role")
|
||||
require.NoError(t, err)
|
||||
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")
|
||||
|
||||
|
@ -873,7 +873,7 @@ func TestACLResolver_DownPolicy(t *testing.T) {
|
|||
require.NotNil(t, authz2)
|
||||
// testing pointer equality - these will be the same object because it is cached.
|
||||
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) {
|
||||
|
@ -897,7 +897,7 @@ func TestACLResolver_DownPolicy(t *testing.T) {
|
|||
authz, err := r.ResolveToken("found")
|
||||
require.NoError(t, err)
|
||||
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, "dc2-key-wr", true, "cached") // from "found" token
|
||||
|
@ -907,7 +907,7 @@ func TestACLResolver_DownPolicy(t *testing.T) {
|
|||
require.NoError(t, err)
|
||||
require.NotNil(t, 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, "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")
|
||||
require.NoError(t, err)
|
||||
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
|
||||
authz2, err := r.ResolveToken("found-role")
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, 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) {
|
||||
|
@ -968,7 +968,7 @@ func TestACLResolver_DownPolicy(t *testing.T) {
|
|||
authz, err := r.ResolveToken("found")
|
||||
require.NoError(t, err)
|
||||
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, "dc2-key-wr", true, "cached") // from "found" token
|
||||
|
@ -979,7 +979,7 @@ func TestACLResolver_DownPolicy(t *testing.T) {
|
|||
require.NotNil(t, authz2)
|
||||
// testing pointer equality - these will be the same object because it is cached.
|
||||
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, "dc2-key-wr", true, "cached") // from "found" token
|
||||
|
@ -989,7 +989,7 @@ func TestACLResolver_DownPolicy(t *testing.T) {
|
|||
authz3, err := r.ResolveToken("found")
|
||||
assert.NoError(t, err)
|
||||
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
|
||||
|
@ -1020,7 +1020,7 @@ func TestACLResolver_DownPolicy(t *testing.T) {
|
|||
authz, err := r.ResolveToken("found-role")
|
||||
require.NoError(t, err)
|
||||
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
|
||||
authz2, err := r.ResolveToken("found-role")
|
||||
|
@ -1028,14 +1028,14 @@ func TestACLResolver_DownPolicy(t *testing.T) {
|
|||
require.NotNil(t, authz2)
|
||||
// testing pointer equality - these will be the same object because it is cached.
|
||||
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
|
||||
retry.Run(t, func(t *retry.R) {
|
||||
authz3, err := r.ResolveToken("found-role")
|
||||
assert.NoError(t, err)
|
||||
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")
|
||||
require.NoError(t, err)
|
||||
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, "dc2-key-wr", true, "cached") // from "found" token
|
||||
|
@ -1072,7 +1072,7 @@ func TestACLResolver_DownPolicy(t *testing.T) {
|
|||
require.NotNil(t, authz2)
|
||||
// testing pointer equality - these will be the same object because it is cached.
|
||||
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) {
|
||||
|
@ -1099,7 +1099,7 @@ func TestACLResolver_DownPolicy(t *testing.T) {
|
|||
authz, err := r.ResolveToken("found-role")
|
||||
require.NoError(t, err)
|
||||
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, "dc2-key-wr", true, "still cached") // from "found" token
|
||||
|
@ -1109,7 +1109,7 @@ func TestACLResolver_DownPolicy(t *testing.T) {
|
|||
require.NotNil(t, authz2)
|
||||
// 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, authz2.NodeWrite("foo", nil))
|
||||
require.Equal(t, acl.Allow, authz2.NodeWrite("foo", nil))
|
||||
})
|
||||
|
||||
t.Run("Async-Cache", func(t *testing.T) {
|
||||
|
@ -1132,7 +1132,7 @@ func TestACLResolver_DownPolicy(t *testing.T) {
|
|||
authz, err := r.ResolveToken("found")
|
||||
require.NoError(t, err)
|
||||
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")
|
||||
|
||||
|
@ -1142,7 +1142,7 @@ func TestACLResolver_DownPolicy(t *testing.T) {
|
|||
require.NotNil(t, authz2)
|
||||
// testing pointer equality - these will be the same object because it is cached.
|
||||
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")
|
||||
|
||||
|
@ -1205,7 +1205,7 @@ func TestACLResolver_DownPolicy(t *testing.T) {
|
|||
authz, err := r.ResolveToken(secretID)
|
||||
require.NoError(t, err)
|
||||
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.
|
||||
requireIdentityCached(t, r, secretID, true, "cached")
|
||||
|
@ -1267,7 +1267,7 @@ func TestACLResolver_DownPolicy(t *testing.T) {
|
|||
authz, err := r.ResolveToken(secretID)
|
||||
require.NoError(t, err)
|
||||
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.
|
||||
requireIdentityCached(t, r, secretID, true, "cached")
|
||||
|
@ -1304,9 +1304,9 @@ func TestACLResolver_DatacenterScoping(t *testing.T) {
|
|||
authz, err := r.ResolveToken("found")
|
||||
require.NotNil(t, authz)
|
||||
require.NoError(t, err)
|
||||
require.False(t, authz.ACLRead())
|
||||
require.True(t, authz.NodeWrite("foo", nil))
|
||||
require.False(t, authz.KeyWrite("foo", nil))
|
||||
require.Equal(t, acl.Deny, authz.ACLRead(nil))
|
||||
require.Equal(t, acl.Allow, authz.NodeWrite("foo", nil))
|
||||
require.Equal(t, acl.Deny, authz.KeyWrite("foo", nil))
|
||||
})
|
||||
|
||||
t.Run("dc2", func(t *testing.T) {
|
||||
|
@ -1326,9 +1326,9 @@ func TestACLResolver_DatacenterScoping(t *testing.T) {
|
|||
authz, err := r.ResolveToken("found")
|
||||
require.NotNil(t, authz)
|
||||
require.NoError(t, err)
|
||||
require.False(t, authz.ACLRead())
|
||||
require.False(t, authz.NodeWrite("foo", nil))
|
||||
require.True(t, authz.KeyWrite("foo", nil))
|
||||
require.Equal(t, acl.Deny, authz.ACLRead(nil))
|
||||
require.Equal(t, acl.Deny, authz.NodeWrite("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")
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, authz)
|
||||
require.True(t, authz.NodeWrite("foo", nil))
|
||||
require.False(t, authz.ACLRead())
|
||||
require.Equal(t, acl.Allow, authz.NodeWrite("foo", nil))
|
||||
require.Equal(t, acl.Deny, authz.ACLRead(nil))
|
||||
require.True(t, modified)
|
||||
require.True(t, deleted)
|
||||
require.Equal(t, int32(2), tokenReads)
|
||||
|
@ -1577,49 +1577,49 @@ func testACLResolver_variousTokens(t *testing.T, delegate *ACLResolverTestDelega
|
|||
authz, err := r.ResolveToken("missing-policy")
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, authz)
|
||||
require.True(t, authz.ACLRead())
|
||||
require.False(t, authz.NodeWrite("foo", nil))
|
||||
require.Equal(t, acl.Allow, authz.ACLRead(nil))
|
||||
require.Equal(t, acl.Deny, authz.NodeWrite("foo", nil))
|
||||
})
|
||||
|
||||
runTwiceAndReset("Missing Role", func(t *testing.T) {
|
||||
authz, err := r.ResolveToken("missing-role")
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, authz)
|
||||
require.True(t, authz.ACLRead())
|
||||
require.False(t, authz.NodeWrite("foo", nil))
|
||||
require.Equal(t, acl.Allow, authz.ACLRead(nil))
|
||||
require.Equal(t, acl.Deny, authz.NodeWrite("foo", nil))
|
||||
})
|
||||
|
||||
runTwiceAndReset("Missing Policy on Role", func(t *testing.T) {
|
||||
authz, err := r.ResolveToken("missing-policy-on-role")
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, authz)
|
||||
require.True(t, authz.ACLRead())
|
||||
require.False(t, authz.NodeWrite("foo", nil))
|
||||
require.Equal(t, acl.Allow, authz.ACLRead(nil))
|
||||
require.Equal(t, acl.Deny, authz.NodeWrite("foo", nil))
|
||||
})
|
||||
|
||||
runTwiceAndReset("Normal with Policy", func(t *testing.T) {
|
||||
authz, err := r.ResolveToken("found")
|
||||
require.NotNil(t, authz)
|
||||
require.NoError(t, err)
|
||||
require.False(t, authz.ACLRead())
|
||||
require.True(t, authz.NodeWrite("foo", nil))
|
||||
require.Equal(t, acl.Deny, authz.ACLRead(nil))
|
||||
require.Equal(t, acl.Allow, authz.NodeWrite("foo", nil))
|
||||
})
|
||||
|
||||
runTwiceAndReset("Normal with Role", func(t *testing.T) {
|
||||
authz, err := r.ResolveToken("found-role")
|
||||
require.NotNil(t, authz)
|
||||
require.NoError(t, err)
|
||||
require.False(t, authz.ACLRead())
|
||||
require.True(t, authz.NodeWrite("foo", nil))
|
||||
require.Equal(t, acl.Deny, authz.ACLRead(nil))
|
||||
require.Equal(t, acl.Allow, authz.NodeWrite("foo", nil))
|
||||
})
|
||||
|
||||
runTwiceAndReset("Normal with Policy and Role", func(t *testing.T) {
|
||||
authz, err := r.ResolveToken("found-policy-and-role")
|
||||
require.NotNil(t, authz)
|
||||
require.NoError(t, err)
|
||||
require.False(t, authz.ACLRead())
|
||||
require.True(t, authz.NodeWrite("foo", nil))
|
||||
require.True(t, authz.ServiceRead("bar"))
|
||||
require.Equal(t, acl.Deny, authz.ACLRead(nil))
|
||||
require.Equal(t, acl.Allow, authz.NodeWrite("foo", nil))
|
||||
require.Equal(t, acl.Allow, authz.ServiceRead("bar", nil))
|
||||
})
|
||||
|
||||
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.NoError(t, err)
|
||||
// spot check some random perms
|
||||
require.False(t, authz.ACLRead())
|
||||
require.False(t, authz.NodeWrite("foo", nil))
|
||||
require.Equal(t, acl.Deny, authz.ACLRead(nil))
|
||||
require.Equal(t, acl.Deny, authz.NodeWrite("foo", nil))
|
||||
// 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
|
||||
require.True(t, authz.ServiceWrite("service1", nil))
|
||||
require.True(t, authz.ServiceRead("literally-anything"))
|
||||
require.True(t, authz.NodeRead("any-node"))
|
||||
require.Equal(t, acl.Allow, authz.ServiceWrite("service1", nil))
|
||||
require.Equal(t, acl.Allow, authz.ServiceRead("literally-anything", nil))
|
||||
require.Equal(t, acl.Allow, authz.NodeRead("any-node", nil))
|
||||
}
|
||||
{
|
||||
authz, err := r.ResolveToken("found-synthetic-policy-2")
|
||||
require.NotNil(t, authz)
|
||||
require.NoError(t, err)
|
||||
// spot check some random perms
|
||||
require.False(t, authz.ACLRead())
|
||||
require.False(t, authz.NodeWrite("foo", nil))
|
||||
require.Equal(t, acl.Deny, authz.ACLRead(nil))
|
||||
require.Equal(t, acl.Deny, authz.NodeWrite("foo", nil))
|
||||
// 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
|
||||
require.True(t, authz.ServiceWrite("service2", nil))
|
||||
require.True(t, authz.ServiceRead("literally-anything"))
|
||||
require.True(t, authz.NodeRead("any-node"))
|
||||
require.Equal(t, acl.Allow, authz.ServiceWrite("service2", nil))
|
||||
require.Equal(t, acl.Allow, authz.ServiceRead("literally-anything", nil))
|
||||
require.Equal(t, acl.Allow, authz.NodeRead("any-node", nil))
|
||||
}
|
||||
})
|
||||
|
||||
|
@ -1660,24 +1660,24 @@ func testACLResolver_variousTokens(t *testing.T, delegate *ACLResolverTestDelega
|
|||
authz, err := r.ResolveToken("")
|
||||
require.NotNil(t, authz)
|
||||
require.NoError(t, err)
|
||||
require.False(t, authz.ACLRead())
|
||||
require.True(t, authz.NodeWrite("foo", nil))
|
||||
require.Equal(t, acl.Deny, authz.ACLRead(nil))
|
||||
require.Equal(t, acl.Allow, authz.NodeWrite("foo", nil))
|
||||
})
|
||||
|
||||
runTwiceAndReset("legacy-management", func(t *testing.T) {
|
||||
authz, err := r.ResolveToken("legacy-management")
|
||||
require.NotNil(t, authz)
|
||||
require.NoError(t, err)
|
||||
require.True(t, authz.ACLWrite())
|
||||
require.True(t, authz.KeyRead("foo"))
|
||||
require.Equal(t, acl.Allow, authz.ACLWrite(nil))
|
||||
require.Equal(t, acl.Allow, authz.KeyRead("foo", nil))
|
||||
})
|
||||
|
||||
runTwiceAndReset("legacy-client", func(t *testing.T) {
|
||||
authz, err := r.ResolveToken("legacy-client")
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, authz)
|
||||
require.False(t, authz.OperatorRead())
|
||||
require.True(t, authz.ServiceRead("foo"))
|
||||
require.Equal(t, acl.Deny, authz.OperatorRead(nil))
|
||||
require.Equal(t, acl.Allow, authz.ServiceRead("foo", nil))
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -1700,10 +1700,12 @@ func TestACLResolver_Legacy(t *testing.T) {
|
|||
reply.ETag = "nothing"
|
||||
reply.Policy = &acl.Policy{
|
||||
ID: "not-needed",
|
||||
Nodes: []*acl.NodePolicy{
|
||||
&acl.NodePolicy{
|
||||
Name: "foo",
|
||||
Policy: acl.PolicyWrite,
|
||||
PolicyRules: acl.PolicyRules{
|
||||
Nodes: []*acl.NodeRule{
|
||||
&acl.NodeRule{
|
||||
Name: "foo",
|
||||
Policy: acl.PolicyWrite,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
@ -1719,18 +1721,18 @@ func TestACLResolver_Legacy(t *testing.T) {
|
|||
require.NoError(t, err)
|
||||
require.NotNil(t, authz)
|
||||
// there is a bit of translation that happens
|
||||
require.True(t, authz.NodeWrite("foo", nil))
|
||||
require.True(t, authz.NodeWrite("foo/bar", nil))
|
||||
require.False(t, authz.NodeWrite("fo", nil))
|
||||
require.Equal(t, acl.Allow, authz.NodeWrite("foo", nil))
|
||||
require.Equal(t, acl.Allow, authz.NodeWrite("foo/bar", nil))
|
||||
require.Equal(t, acl.Deny, authz.NodeWrite("fo", nil))
|
||||
|
||||
// this should be from the cache
|
||||
authz, err = r.ResolveToken("foo")
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, authz)
|
||||
// there is a bit of translation that happens
|
||||
require.True(t, authz.NodeWrite("foo", nil))
|
||||
require.True(t, authz.NodeWrite("foo/bar", nil))
|
||||
require.False(t, authz.NodeWrite("fo", nil))
|
||||
require.Equal(t, acl.Allow, authz.NodeWrite("foo", nil))
|
||||
require.Equal(t, acl.Allow, authz.NodeWrite("foo/bar", nil))
|
||||
require.Equal(t, acl.Deny, authz.NodeWrite("fo", nil))
|
||||
})
|
||||
|
||||
t.Run("Cache-Expiry-Extend", func(t *testing.T) {
|
||||
|
@ -1749,10 +1751,12 @@ func TestACLResolver_Legacy(t *testing.T) {
|
|||
reply.ETag = "nothing"
|
||||
reply.Policy = &acl.Policy{
|
||||
ID: "not-needed",
|
||||
Nodes: []*acl.NodePolicy{
|
||||
&acl.NodePolicy{
|
||||
Name: "foo",
|
||||
Policy: acl.PolicyWrite,
|
||||
PolicyRules: acl.PolicyRules{
|
||||
Nodes: []*acl.NodeRule{
|
||||
&acl.NodeRule{
|
||||
Name: "foo",
|
||||
Policy: acl.PolicyWrite,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
@ -1770,18 +1774,18 @@ func TestACLResolver_Legacy(t *testing.T) {
|
|||
require.NoError(t, err)
|
||||
require.NotNil(t, authz)
|
||||
// there is a bit of translation that happens
|
||||
require.True(t, authz.NodeWrite("foo", nil))
|
||||
require.True(t, authz.NodeWrite("foo/bar", nil))
|
||||
require.False(t, authz.NodeWrite("fo", nil))
|
||||
require.Equal(t, acl.Allow, authz.NodeWrite("foo", nil))
|
||||
require.Equal(t, acl.Allow, authz.NodeWrite("foo/bar", nil))
|
||||
require.Equal(t, acl.Deny, authz.NodeWrite("fo", nil))
|
||||
|
||||
// this should be from the cache
|
||||
authz, err = r.ResolveToken("foo")
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, authz)
|
||||
// there is a bit of translation that happens
|
||||
require.True(t, authz.NodeWrite("foo", nil))
|
||||
require.True(t, authz.NodeWrite("foo/bar", nil))
|
||||
require.False(t, authz.NodeWrite("fo", nil))
|
||||
require.Equal(t, acl.Allow, authz.NodeWrite("foo", nil))
|
||||
require.Equal(t, acl.Allow, authz.NodeWrite("foo/bar", nil))
|
||||
require.Equal(t, acl.Deny, authz.NodeWrite("fo", nil))
|
||||
})
|
||||
|
||||
t.Run("Cache-Expiry-Allow", func(t *testing.T) {
|
||||
|
@ -1800,10 +1804,12 @@ func TestACLResolver_Legacy(t *testing.T) {
|
|||
reply.ETag = "nothing"
|
||||
reply.Policy = &acl.Policy{
|
||||
ID: "not-needed",
|
||||
Nodes: []*acl.NodePolicy{
|
||||
&acl.NodePolicy{
|
||||
Name: "foo",
|
||||
Policy: acl.PolicyWrite,
|
||||
PolicyRules: acl.PolicyRules{
|
||||
Nodes: []*acl.NodeRule{
|
||||
&acl.NodeRule{
|
||||
Name: "foo",
|
||||
Policy: acl.PolicyWrite,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
@ -1822,18 +1828,18 @@ func TestACLResolver_Legacy(t *testing.T) {
|
|||
require.NoError(t, err)
|
||||
require.NotNil(t, authz)
|
||||
// there is a bit of translation that happens
|
||||
require.True(t, authz.NodeWrite("foo", nil))
|
||||
require.True(t, authz.NodeWrite("foo/bar", nil))
|
||||
require.False(t, authz.NodeWrite("fo", nil))
|
||||
require.Equal(t, acl.Allow, authz.NodeWrite("foo", nil))
|
||||
require.Equal(t, acl.Allow, authz.NodeWrite("foo/bar", nil))
|
||||
require.Equal(t, acl.Deny, authz.NodeWrite("fo", nil))
|
||||
|
||||
// this should be from the cache
|
||||
authz, err = r.ResolveToken("foo")
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, authz)
|
||||
// there is a bit of translation that happens
|
||||
require.True(t, authz.NodeWrite("foo", nil))
|
||||
require.True(t, authz.NodeWrite("foo/bar", nil))
|
||||
require.True(t, authz.NodeWrite("fo", nil))
|
||||
require.Equal(t, acl.Allow, authz.NodeWrite("foo", nil))
|
||||
require.Equal(t, acl.Allow, authz.NodeWrite("foo/bar", nil))
|
||||
require.Equal(t, acl.Allow, authz.NodeWrite("fo", nil))
|
||||
})
|
||||
|
||||
t.Run("Cache-Expiry-Deny", func(t *testing.T) {
|
||||
|
@ -1852,10 +1858,12 @@ func TestACLResolver_Legacy(t *testing.T) {
|
|||
reply.ETag = "nothing"
|
||||
reply.Policy = &acl.Policy{
|
||||
ID: "not-needed",
|
||||
Nodes: []*acl.NodePolicy{
|
||||
&acl.NodePolicy{
|
||||
Name: "foo",
|
||||
Policy: acl.PolicyWrite,
|
||||
PolicyRules: acl.PolicyRules{
|
||||
Nodes: []*acl.NodeRule{
|
||||
&acl.NodeRule{
|
||||
Name: "foo",
|
||||
Policy: acl.PolicyWrite,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
@ -1874,18 +1882,18 @@ func TestACLResolver_Legacy(t *testing.T) {
|
|||
require.NoError(t, err)
|
||||
require.NotNil(t, authz)
|
||||
// there is a bit of translation that happens
|
||||
require.True(t, authz.NodeWrite("foo", nil))
|
||||
require.True(t, authz.NodeWrite("foo/bar", nil))
|
||||
require.False(t, authz.NodeWrite("fo", nil))
|
||||
require.Equal(t, acl.Allow, authz.NodeWrite("foo", nil))
|
||||
require.Equal(t, acl.Allow, authz.NodeWrite("foo/bar", nil))
|
||||
require.Equal(t, acl.Deny, authz.NodeWrite("fo", nil))
|
||||
|
||||
// this should be from the cache
|
||||
authz, err = r.ResolveToken("foo")
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, authz)
|
||||
// there is a bit of translation that happens
|
||||
require.False(t, authz.NodeWrite("foo", nil))
|
||||
require.False(t, authz.NodeWrite("foo/bar", nil))
|
||||
require.False(t, authz.NodeWrite("fo", nil))
|
||||
require.Equal(t, acl.Deny, authz.NodeWrite("foo", nil))
|
||||
require.Equal(t, acl.Deny, authz.NodeWrite("foo/bar", nil))
|
||||
require.Equal(t, acl.Deny, authz.NodeWrite("fo", nil))
|
||||
})
|
||||
|
||||
t.Run("Cache-Expiry-Async-Cache", func(t *testing.T) {
|
||||
|
@ -1904,10 +1912,12 @@ func TestACLResolver_Legacy(t *testing.T) {
|
|||
reply.ETag = "nothing"
|
||||
reply.Policy = &acl.Policy{
|
||||
ID: "not-needed",
|
||||
Nodes: []*acl.NodePolicy{
|
||||
&acl.NodePolicy{
|
||||
Name: "foo",
|
||||
Policy: acl.PolicyWrite,
|
||||
PolicyRules: acl.PolicyRules{
|
||||
Nodes: []*acl.NodeRule{
|
||||
&acl.NodeRule{
|
||||
Name: "foo",
|
||||
Policy: acl.PolicyWrite,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
@ -1926,9 +1936,9 @@ func TestACLResolver_Legacy(t *testing.T) {
|
|||
require.NoError(t, err)
|
||||
require.NotNil(t, authz)
|
||||
// there is a bit of translation that happens
|
||||
require.True(t, authz.NodeWrite("foo", nil))
|
||||
require.True(t, authz.NodeWrite("foo/bar", nil))
|
||||
require.False(t, authz.NodeWrite("fo", nil))
|
||||
require.Equal(t, acl.Allow, authz.NodeWrite("foo", nil))
|
||||
require.Equal(t, acl.Allow, authz.NodeWrite("foo/bar", nil))
|
||||
require.Equal(t, acl.Deny, authz.NodeWrite("fo", nil))
|
||||
|
||||
// delivered from the cache
|
||||
authz2, err := r.ResolveToken("foo")
|
||||
|
@ -2170,7 +2180,7 @@ service "foo" {
|
|||
if err != nil {
|
||||
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 {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
|
@ -2204,7 +2214,7 @@ node "node1" {
|
|||
if err != nil {
|
||||
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 {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
|
@ -2260,7 +2270,7 @@ service "foo" {
|
|||
}
|
||||
`, acl.SyntaxLegacy, nil)
|
||||
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)
|
||||
|
||||
// Filter
|
||||
|
@ -2347,7 +2357,7 @@ service "foo" {
|
|||
if err != nil {
|
||||
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 {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
|
@ -2381,7 +2391,7 @@ node "node1" {
|
|||
if err != nil {
|
||||
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 {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
|
@ -2453,7 +2463,7 @@ service "foo" {
|
|||
if err != nil {
|
||||
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 {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
|
@ -2487,7 +2497,7 @@ node "node1" {
|
|||
if err != nil {
|
||||
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 {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
|
@ -2559,7 +2569,7 @@ service "foo" {
|
|||
if err != nil {
|
||||
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 {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
|
@ -2596,7 +2606,7 @@ node "node1" {
|
|||
if err != nil {
|
||||
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 {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
|
@ -2750,7 +2760,7 @@ service "foo" {
|
|||
if err != nil {
|
||||
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 {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
|
@ -2790,7 +2800,7 @@ node "node1" {
|
|||
if err != nil {
|
||||
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 {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
|
@ -3046,7 +3056,7 @@ node "node" {
|
|||
if err != nil {
|
||||
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 {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
|
@ -3091,7 +3101,7 @@ service "service" {
|
|||
if err != nil {
|
||||
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 {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
|
@ -3121,7 +3131,7 @@ service "other" {
|
|||
if err != nil {
|
||||
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 {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
|
@ -3195,7 +3205,7 @@ service "other" {
|
|||
if err != nil {
|
||||
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 {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
|
@ -3225,7 +3235,7 @@ node "node" {
|
|||
if err != nil {
|
||||
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 {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
|
@ -3272,7 +3282,7 @@ node "node" {
|
|||
if err != nil {
|
||||
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 {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
|
@ -3285,7 +3295,7 @@ node "node" {
|
|||
if err != nil {
|
||||
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 {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
|
@ -3294,7 +3304,7 @@ node "node" {
|
|||
DeregisterRequest structs.DeregisterRequest
|
||||
Service *structs.NodeService
|
||||
Check *structs.HealthCheck
|
||||
Perms *acl.PolicyAuthorizer
|
||||
Perms acl.Authorizer
|
||||
Expected bool
|
||||
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
|
||||
// delete this and do all the ACL checks down there.
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
// Proxies must have write permission on their destination
|
||||
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
|
||||
}
|
||||
}
|
||||
|
@ -334,7 +336,8 @@ func (c *Catalog) ServiceNodes(args *structs.ServiceSpecificRequest, reply *stru
|
|||
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)
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -154,12 +154,12 @@ func NewClientLogger(config *Config, logger *log.Logger, tlsConfigurator *tlsuti
|
|||
|
||||
c.useNewACLs = 0
|
||||
aclConfig := ACLResolverConfig{
|
||||
Config: config,
|
||||
Delegate: c,
|
||||
Logger: logger,
|
||||
AutoDisable: true,
|
||||
CacheConfig: clientACLCacheConfig,
|
||||
Sentinel: nil,
|
||||
Config: config,
|
||||
Delegate: c,
|
||||
Logger: logger,
|
||||
AutoDisable: true,
|
||||
CacheConfig: clientACLCacheConfig,
|
||||
EnterpriseConfig: newEnterpriseACLConfig(logger),
|
||||
}
|
||||
var err error
|
||||
if c.acls, err = NewACLResolver(&aclConfig); err != nil {
|
||||
|
|
|
@ -231,7 +231,8 @@ func (c *ConfigEntry) ResolveServiceConfig(args *structs.ServiceConfigRequest, r
|
|||
if err != nil {
|
||||
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
|
||||
}
|
||||
|
||||
|
|
|
@ -119,7 +119,7 @@ func (s *ConnectCA) ConfigurationGet(
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if rule != nil && !rule.OperatorRead() {
|
||||
if rule != nil && rule.OperatorRead(nil) != acl.Allow {
|
||||
return acl.ErrPermissionDenied
|
||||
}
|
||||
|
||||
|
@ -151,7 +151,7 @@ func (s *ConnectCA) ConfigurationSet(
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if rule != nil && !rule.OperatorWrite() {
|
||||
if rule != nil && rule.OperatorWrite(nil) != acl.Allow {
|
||||
return acl.ErrPermissionDenied
|
||||
}
|
||||
|
||||
|
@ -431,7 +431,8 @@ func (s *ConnectCA) Sign(
|
|||
return err
|
||||
}
|
||||
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
|
||||
}
|
||||
|
||||
|
@ -442,7 +443,8 @@ func (s *ConnectCA) Sign(
|
|||
"we are %s", serviceID.Datacenter, s.srv.config.Datacenter)
|
||||
}
|
||||
} 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
|
||||
}
|
||||
}
|
||||
|
@ -569,7 +571,7 @@ func (s *ConnectCA) SignIntermediate(
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if rule != nil && !rule.OperatorWrite() {
|
||||
if rule != nil && rule.OperatorWrite(nil) != acl.Allow {
|
||||
return acl.ErrPermissionDenied
|
||||
}
|
||||
|
||||
|
|
|
@ -139,7 +139,8 @@ func (c *Coordinate) Update(args *structs.CoordinateUpdateRequest, reply *struct
|
|||
return err
|
||||
}
|
||||
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
|
||||
}
|
||||
}
|
||||
|
@ -210,7 +211,8 @@ func (c *Coordinate) Node(args *structs.NodeSpecificRequest, reply *structs.Inde
|
|||
return err
|
||||
}
|
||||
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
|
||||
}
|
||||
}
|
||||
|
|
|
@ -34,7 +34,8 @@ func (c *DiscoveryChain) Get(args *structs.DiscoveryChainRequest, reply *structs
|
|||
if err != nil {
|
||||
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
|
||||
}
|
||||
|
||||
|
|
|
@ -14,7 +14,7 @@ func (d *dirEntFilter) Len() int {
|
|||
return len(d.ent)
|
||||
}
|
||||
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) {
|
||||
copy(d.ent[dst:dst+span], d.ent[src:src+span])
|
||||
|
@ -36,7 +36,8 @@ func (k *keyFilter) Len() int {
|
|||
return len(k.keys)
|
||||
}
|
||||
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) {
|
||||
|
@ -60,19 +61,20 @@ func (t *txnResultsFilter) Len() int {
|
|||
}
|
||||
|
||||
func (t *txnResultsFilter) Filter(i int) bool {
|
||||
// TODO (namespaces) use a real ent authz context for most of these checks
|
||||
result := t.results[i]
|
||||
switch {
|
||||
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:
|
||||
return !t.authorizer.NodeRead(result.Node.Node)
|
||||
return t.authorizer.NodeRead(result.Node.Node, nil) != acl.Allow
|
||||
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:
|
||||
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
|
||||
}
|
||||
|
|
|
@ -11,7 +11,7 @@ import (
|
|||
func TestFilter_DirEnt(t *testing.T) {
|
||||
t.Parallel()
|
||||
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 {
|
||||
in []string
|
||||
|
@ -53,7 +53,7 @@ func TestFilter_DirEnt(t *testing.T) {
|
|||
func TestFilter_Keys(t *testing.T) {
|
||||
t.Parallel()
|
||||
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 {
|
||||
in []string
|
||||
|
@ -85,7 +85,7 @@ func TestFilter_Keys(t *testing.T) {
|
|||
func TestFilter_TxnResults(t *testing.T) {
|
||||
t.Parallel()
|
||||
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 {
|
||||
in []string
|
||||
|
|
|
@ -5,6 +5,7 @@ import (
|
|||
"sort"
|
||||
|
||||
"github.com/armon/go-metrics"
|
||||
"github.com/hashicorp/consul/acl"
|
||||
"github.com/hashicorp/consul/agent/consul/state"
|
||||
"github.com/hashicorp/consul/agent/structs"
|
||||
bexpr "github.com/hashicorp/go-bexpr"
|
||||
|
@ -171,7 +172,7 @@ func (h *Health) ServiceNodes(args *structs.ServiceSpecificRequest, reply *struc
|
|||
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)
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -87,7 +87,7 @@ func (s *Intention) Apply(
|
|||
|
||||
// Perform the ACL check
|
||||
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)
|
||||
return acl.ErrPermissionDenied
|
||||
}
|
||||
|
@ -107,7 +107,7 @@ func (s *Intention) Apply(
|
|||
// Perform the ACL check that we have write to the old prefix too,
|
||||
// which must be true to perform any rename.
|
||||
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)
|
||||
return acl.ErrPermissionDenied
|
||||
}
|
||||
|
@ -243,7 +243,7 @@ func (s *Intention) Match(
|
|||
// We go through each entry and test the destination to check if it
|
||||
// matches.
|
||||
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)
|
||||
return acl.ErrPermissionDenied
|
||||
}
|
||||
|
@ -309,7 +309,7 @@ func (s *Intention) Check(
|
|||
// NOT IntentionRead because the Check API only returns pass/fail and
|
||||
// returns no other information about the intentions used.
|
||||
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)
|
||||
return acl.ErrPermissionDenied
|
||||
}
|
||||
|
@ -360,7 +360,7 @@ func (s *Intention) Check(
|
|||
|
||||
reply.Allowed = true
|
||||
if rule != nil {
|
||||
reply.Allowed = rule.IntentionDefaultAllow()
|
||||
reply.Allowed = rule.IntentionDefaultAllow(nil) == acl.Allow
|
||||
}
|
||||
|
||||
return nil
|
||||
|
|
|
@ -125,7 +125,7 @@ func (m *Internal) EventFire(args *structs.EventFireRequest,
|
|||
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)
|
||||
return acl.ErrPermissionDenied
|
||||
}
|
||||
|
@ -162,7 +162,7 @@ func (m *Internal) KeyringOperation(
|
|||
if rule != nil {
|
||||
switch args.Operation {
|
||||
case structs.KeyringList:
|
||||
if !rule.KeyringRead() {
|
||||
if rule.KeyringRead(nil) != acl.Allow {
|
||||
return fmt.Errorf("Reading keyring denied by ACLs")
|
||||
}
|
||||
case structs.KeyringInstall:
|
||||
|
@ -170,7 +170,7 @@ func (m *Internal) KeyringOperation(
|
|||
case structs.KeyringUse:
|
||||
fallthrough
|
||||
case structs.KeyringRemove:
|
||||
if !rule.KeyringWrite() {
|
||||
if rule.KeyringWrite(nil) != acl.Allow {
|
||||
return fmt.Errorf("Modifying keyring denied due to ACLs")
|
||||
}
|
||||
default:
|
||||
|
|
|
@ -9,7 +9,6 @@ import (
|
|||
"github.com/hashicorp/consul/agent/consul/state"
|
||||
"github.com/hashicorp/consul/agent/structs"
|
||||
"github.com/hashicorp/consul/api"
|
||||
"github.com/hashicorp/consul/sentinel"
|
||||
"github.com/hashicorp/go-memdb"
|
||||
)
|
||||
|
||||
|
@ -32,7 +31,8 @@ func kvsPreApply(srv *Server, rule acl.Authorizer, op api.KVOp, dirEnt *structs.
|
|||
if rule != nil {
|
||||
switch op {
|
||||
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
|
||||
}
|
||||
|
||||
|
@ -43,15 +43,13 @@ func kvsPreApply(srv *Server, rule acl.Authorizer, op api.KVOp, dirEnt *structs.
|
|||
// These could reveal information based on the outcome
|
||||
// of the transaction, and they operate on individual
|
||||
// keys so we check them here.
|
||||
if !rule.KeyRead(dirEnt.Key) {
|
||||
if rule.KeyRead(dirEnt.Key, nil) != acl.Allow {
|
||||
return false, acl.ErrPermissionDenied
|
||||
}
|
||||
|
||||
default:
|
||||
scope := func() map[string]interface{} {
|
||||
return sentinel.ScopeKVUpsert(dirEnt.Key, dirEnt.Value, dirEnt.Flags)
|
||||
}
|
||||
if !rule.KeyWrite(dirEnt.Key, scope) {
|
||||
// TODO (namespaces) use actual ent authz context - ensure we set the Sentinel Scope
|
||||
if rule.KeyWrite(dirEnt.Key, nil) != acl.Allow {
|
||||
return false, acl.ErrPermissionDenied
|
||||
}
|
||||
}
|
||||
|
@ -132,7 +130,7 @@ func (k *KVS) Get(args *structs.KeyRequest, reply *structs.IndexedDirEntries) er
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if aclRule != nil && !aclRule.KeyRead(args.Key) {
|
||||
if aclRule != nil && aclRule.KeyRead(args.Key, nil) != acl.Allow {
|
||||
return acl.ErrPermissionDenied
|
||||
}
|
||||
|
||||
|
@ -164,7 +162,7 @@ func (k *KVS) List(args *structs.KeyRequest, reply *structs.IndexedDirEntries) e
|
|||
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
|
||||
}
|
||||
|
||||
|
@ -208,7 +206,7 @@ func (k *KVS) ListKeys(args *structs.KeyListRequest, reply *structs.IndexedKeyLi
|
|||
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
|
||||
}
|
||||
|
||||
|
|
|
@ -498,23 +498,28 @@ func (s *Server) initializeACLs(upgrade bool) error {
|
|||
|
||||
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)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get the builtin global-management policy")
|
||||
}
|
||||
if policy == nil {
|
||||
policy := structs.ACLPolicy{
|
||||
if policy == nil || policy.Rules != structs.ACLPolicyGlobalManagement {
|
||||
newPolicy := structs.ACLPolicy{
|
||||
ID: structs.ACLPolicyGlobalManagementID,
|
||||
Name: "global-management",
|
||||
Description: "Builtin Policy that grants unlimited access",
|
||||
Rules: structs.ACLPolicyGlobalManagement,
|
||||
Syntax: acl.SyntaxCurrent,
|
||||
}
|
||||
policy.SetHash(true)
|
||||
if policy != nil {
|
||||
newPolicy.Name = policy.Name
|
||||
newPolicy.Description = policy.Description
|
||||
}
|
||||
|
||||
newPolicy.SetHash(true)
|
||||
|
||||
req := structs.ACLPolicyBatchSetRequest{
|
||||
Policies: structs.ACLPolicies{&policy},
|
||||
Policies: structs.ACLPolicies{&newPolicy},
|
||||
}
|
||||
_, err := s.raftApply(structs.ACLPolicySetRequestType, &req)
|
||||
if err != nil {
|
||||
|
|
|
@ -19,7 +19,7 @@ func (op *Operator) AutopilotGetConfiguration(args *structs.DCSpecificRequest, r
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if rule != nil && !rule.OperatorRead() {
|
||||
if rule != nil && rule.OperatorRead(nil) != acl.Allow {
|
||||
return acl.ErrPermissionDenied
|
||||
}
|
||||
|
||||
|
@ -48,7 +48,7 @@ func (op *Operator) AutopilotSetConfiguration(args *structs.AutopilotSetConfigRe
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if rule != nil && !rule.OperatorWrite() {
|
||||
if rule != nil && rule.OperatorWrite(nil) != acl.Allow {
|
||||
return acl.ErrPermissionDenied
|
||||
}
|
||||
|
||||
|
@ -84,7 +84,7 @@ func (op *Operator) ServerHealth(args *structs.DCSpecificRequest, reply *autopil
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if rule != nil && !rule.OperatorRead() {
|
||||
if rule != nil && rule.OperatorRead(nil) != acl.Allow {
|
||||
return acl.ErrPermissionDenied
|
||||
}
|
||||
|
||||
|
|
|
@ -22,7 +22,7 @@ func (op *Operator) RaftGetConfiguration(args *structs.DCSpecificRequest, reply
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if rule != nil && !rule.OperatorRead() {
|
||||
if rule != nil && rule.OperatorRead(nil) != acl.Allow {
|
||||
return acl.ErrPermissionDenied
|
||||
}
|
||||
|
||||
|
@ -84,7 +84,7 @@ func (op *Operator) RaftRemovePeerByAddress(args *structs.RaftRemovePeerRequest,
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if rule != nil && !rule.OperatorWrite() {
|
||||
if rule != nil && rule.OperatorWrite(nil) != acl.Allow {
|
||||
return acl.ErrPermissionDenied
|
||||
}
|
||||
|
||||
|
@ -151,7 +151,7 @@ func (op *Operator) RaftRemovePeerByID(args *structs.RaftRemovePeerRequest, repl
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if rule != nil && !rule.OperatorWrite() {
|
||||
if rule != nil && rule.OperatorWrite(nil) != acl.Allow {
|
||||
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
|
||||
// proposing.
|
||||
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)
|
||||
return acl.ErrPermissionDenied
|
||||
}
|
||||
|
@ -89,7 +89,7 @@ func (p *PreparedQuery) Apply(args *structs.PreparedQueryRequest, reply *string)
|
|||
}
|
||||
|
||||
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)
|
||||
return acl.ErrPermissionDenied
|
||||
}
|
||||
|
|
|
@ -18,6 +18,7 @@ import (
|
|||
"time"
|
||||
|
||||
metrics "github.com/armon/go-metrics"
|
||||
"github.com/hashicorp/consul/acl"
|
||||
ca "github.com/hashicorp/consul/agent/connect/ca"
|
||||
"github.com/hashicorp/consul/agent/consul/autopilot"
|
||||
"github.com/hashicorp/consul/agent/consul/fsm"
|
||||
|
@ -28,7 +29,6 @@ import (
|
|||
"github.com/hashicorp/consul/agent/structs"
|
||||
"github.com/hashicorp/consul/agent/token"
|
||||
"github.com/hashicorp/consul/lib"
|
||||
"github.com/hashicorp/consul/sentinel"
|
||||
"github.com/hashicorp/consul/tlsutil"
|
||||
"github.com/hashicorp/consul/types"
|
||||
"github.com/hashicorp/go-hclog"
|
||||
|
@ -107,8 +107,9 @@ var (
|
|||
// Server is Consul server which manages the service discovery,
|
||||
// health checking, DC forwarding, Raft, and multiple Serf pools.
|
||||
type Server struct {
|
||||
// sentinel is the Sentinel code engine (can be nil).
|
||||
sentinel sentinel.Evaluator
|
||||
// enterpriseACLConfig is the Consul Enterprise specific items
|
||||
// necessary for ACLs
|
||||
enterpriseACLConfig *acl.EnterpriseACLConfig
|
||||
|
||||
// acls is used to resolve tokens to effective policies
|
||||
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.
|
||||
s.statsFetcher = NewStatsFetcher(logger, s.connPool, s.config.Datacenter)
|
||||
|
||||
s.sentinel = sentinel.New(logger)
|
||||
s.enterpriseACLConfig = newEnterpriseACLConfig(logger)
|
||||
s.useNewACLs = 0
|
||||
aclConfig := ACLResolverConfig{
|
||||
Config: config,
|
||||
Delegate: s,
|
||||
CacheConfig: serverACLCacheConfig,
|
||||
AutoDisable: false,
|
||||
Logger: logger,
|
||||
Sentinel: s.sentinel,
|
||||
Config: config,
|
||||
Delegate: s,
|
||||
CacheConfig: serverACLCacheConfig,
|
||||
AutoDisable: false,
|
||||
Logger: logger,
|
||||
EnterpriseConfig: s.enterpriseACLConfig,
|
||||
}
|
||||
// Initialize the ACL resolver.
|
||||
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 {
|
||||
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
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
|
@ -241,7 +243,8 @@ func (s *Session) Renew(args *structs.SessionSpecificRequest,
|
|||
return err
|
||||
}
|
||||
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
|
||||
}
|
||||
}
|
||||
|
|
|
@ -61,7 +61,7 @@ func (s *Server) dispatchSnapshotRequest(args *structs.SnapshotRequest, in io.Re
|
|||
// all the ACLs and you could escalate from there.
|
||||
if rule, err := s.ResolveToken(args.Token); err != nil {
|
||||
return nil, err
|
||||
} else if rule != nil && !rule.Snapshot() {
|
||||
} else if rule != nil && rule.Snapshot(nil) != acl.Allow {
|
||||
return nil, acl.ErrPermissionDenied
|
||||
}
|
||||
|
||||
|
|
|
@ -1373,11 +1373,13 @@ func (s *Store) aclPolicySetTxn(tx *memdb.Txn, idx uint64, policy *structs.ACLPo
|
|||
}
|
||||
|
||||
if existing != nil {
|
||||
policyMatch := existing.(*structs.ACLPolicy)
|
||||
|
||||
if policy.ID == structs.ACLPolicyGlobalManagementID {
|
||||
// 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")
|
||||
}
|
||||
|
||||
|
|
|
@ -78,7 +78,7 @@ func (s *HTTPServer) EventList(resp http.ResponseWriter, req *http.Request) (int
|
|||
// Fetch the ACL token, if any.
|
||||
var token string
|
||||
s.parseToken(req, &token)
|
||||
acl, err := s.agent.resolveToken(token)
|
||||
authz, err := s.agent.resolveToken(token)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -128,10 +128,10 @@ RUN_QUERY:
|
|||
events := s.agent.UserEvents()
|
||||
|
||||
// Filter the events using the ACL, if present
|
||||
if acl != nil {
|
||||
if authz != nil {
|
||||
for i := 0; i < len(events); i++ {
|
||||
name := events[i].Name
|
||||
if acl.EventRead(name) {
|
||||
if authz.EventRead(name, nil) == acl.Allow {
|
||||
continue
|
||||
}
|
||||
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,
|
||||
// write a forbidden response
|
||||
if rule != nil && !rule.OperatorRead() {
|
||||
if rule != nil && rule.OperatorRead(nil) != acl.Allow {
|
||||
resp.WriteHeader(http.StatusForbidden)
|
||||
return
|
||||
}
|
||||
|
|
|
@ -11,7 +11,6 @@ import (
|
|||
"time"
|
||||
|
||||
"github.com/hashicorp/consul/acl"
|
||||
"github.com/hashicorp/consul/sentinel"
|
||||
"golang.org/x/crypto/blake2b"
|
||||
)
|
||||
|
||||
|
@ -80,7 +79,7 @@ service_prefix "" {
|
|||
}
|
||||
session_prefix "" {
|
||||
policy = "write"
|
||||
}`
|
||||
}` + EnterpriseACLPolicyGlobalManagement
|
||||
|
||||
// This is the policy ID for anonymous access. This is configurable by the
|
||||
// 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
|
||||
parsed := make([]*acl.Policy, 0, len(policies))
|
||||
for _, policy := range policies {
|
||||
|
@ -658,7 +657,7 @@ func (policies ACLPolicies) resolveWithCache(cache *ACLCaches, sentinel sentinel
|
|||
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 {
|
||||
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
|
||||
}
|
||||
|
||||
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
|
||||
cacheKey := policies.HashKey()
|
||||
entry := cache.GetAuthorizer(cacheKey)
|
||||
|
@ -679,13 +678,13 @@ func (policies ACLPolicies) Compile(parent acl.Authorizer, cache *ACLCaches, sen
|
|||
return entry.Authorizer, nil
|
||||
}
|
||||
|
||||
parsed, err := policies.resolveWithCache(cache, sentinel)
|
||||
parsed, err := policies.resolveWithCache(cache, entConf)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to parse the ACL policies: %v", err)
|
||||
}
|
||||
|
||||
// Create the ACL object
|
||||
authorizer, err := acl.NewPolicyAuthorizer(parent, parsed, sentinel)
|
||||
authorizer, err := acl.NewPolicyAuthorizerWithDefaults(parent, parsed, entConf)
|
||||
if err != nil {
|
||||
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
|
||||
}
|
||||
|
||||
func (policies ACLPolicies) Merge(cache *ACLCaches, sentinel sentinel.Evaluator) (*acl.Policy, error) {
|
||||
parsed, err := policies.resolveWithCache(cache, sentinel)
|
||||
func (policies ACLPolicies) Merge(cache *ACLCaches, entConf *acl.EnterpriseACLConfig) (*acl.Policy, error) {
|
||||
parsed, err := policies.resolveWithCache(cache, entConf)
|
||||
if err != nil {
|
||||
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.NotNil(t, authz)
|
||||
|
||||
require.True(t, authz.NodeRead("foo"))
|
||||
require.True(t, authz.AgentRead("foo"))
|
||||
require.True(t, authz.KeyRead("foo"))
|
||||
require.True(t, authz.ServiceRead("foo"))
|
||||
require.False(t, authz.ACLRead())
|
||||
require.Equal(t, acl.Allow, authz.NodeRead("foo", nil))
|
||||
require.Equal(t, acl.Allow, authz.AgentRead("foo", nil))
|
||||
require.Equal(t, acl.Allow, authz.KeyRead("foo", nil))
|
||||
require.Equal(t, acl.Allow, authz.ServiceRead("foo", nil))
|
||||
require.Equal(t, acl.Deny, authz.ACLRead(nil))
|
||||
})
|
||||
|
||||
t.Run("Check Cache", func(t *testing.T) {
|
||||
|
@ -678,11 +678,11 @@ func TestStructs_ACLPolicies_Compile(t *testing.T) {
|
|||
authz := entry.Authorizer
|
||||
require.NotNil(t, authz)
|
||||
|
||||
require.True(t, authz.NodeRead("foo"))
|
||||
require.True(t, authz.AgentRead("foo"))
|
||||
require.True(t, authz.KeyRead("foo"))
|
||||
require.True(t, authz.ServiceRead("foo"))
|
||||
require.False(t, authz.ACLRead())
|
||||
require.Equal(t, acl.Allow, authz.NodeRead("foo", nil))
|
||||
require.Equal(t, acl.Allow, authz.AgentRead("foo", nil))
|
||||
require.Equal(t, acl.Allow, authz.KeyRead("foo", nil))
|
||||
require.Equal(t, acl.Allow, authz.ServiceRead("foo", nil))
|
||||
require.Equal(t, acl.Deny, authz.ACLRead(nil))
|
||||
|
||||
// setup the cache for the next test
|
||||
cache.PutAuthorizer(testPolicies.HashKey(), acl.DenyAll())
|
||||
|
@ -694,10 +694,10 @@ func TestStructs_ACLPolicies_Compile(t *testing.T) {
|
|||
require.NotNil(t, authz)
|
||||
|
||||
// we reset the Authorizer in the cache so now everything should be denied
|
||||
require.False(t, authz.NodeRead("foo"))
|
||||
require.False(t, authz.AgentRead("foo"))
|
||||
require.False(t, authz.KeyRead("foo"))
|
||||
require.False(t, authz.ServiceRead("foo"))
|
||||
require.False(t, authz.ACLRead())
|
||||
require.Equal(t, acl.Deny, authz.NodeRead("foo", nil))
|
||||
require.Equal(t, acl.Deny, authz.AgentRead("foo", nil))
|
||||
require.Equal(t, acl.Deny, authz.KeyRead("foo", nil))
|
||||
require.Equal(t, acl.Deny, authz.ServiceRead("foo", nil))
|
||||
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 {
|
||||
return rule.ServiceRead(e.Name)
|
||||
return rule.ServiceRead(e.Name, nil) == acl.Allow
|
||||
}
|
||||
|
||||
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 {
|
||||
|
@ -162,7 +162,7 @@ func (e *ProxyConfigEntry) CanRead(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 {
|
||||
|
|
|
@ -892,13 +892,13 @@ type discoveryChainConfigEntry interface {
|
|||
}
|
||||
|
||||
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 {
|
||||
name := entry.GetName()
|
||||
|
||||
if !rule.ServiceWrite(name, nil) {
|
||||
if rule.ServiceWrite(name, nil) != acl.Allow {
|
||||
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
|
||||
// your own service.
|
||||
if !rule.ServiceRead(svc) {
|
||||
if rule.ServiceRead(svc, nil) != acl.Allow {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
|
|
@ -27,7 +27,7 @@ func TestConfigEntries_ListRelatedServices_AndACLs(t *testing.T) {
|
|||
policy, err := acl.NewPolicyFromSource("", 0, buf.String(), acl.SyntaxCurrent, nil)
|
||||
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)
|
||||
return authorizer
|
||||
}
|
||||
|
|
|
@ -251,12 +251,13 @@ func (s *Server) process(stream ADSStream, reqCh <-chan *envoy.DiscoveryRequest)
|
|||
|
||||
switch cfgSnap.Kind {
|
||||
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")
|
||||
}
|
||||
case structs.ServiceKindMeshGateway:
|
||||
// TODO (mesh-gateway) - figure out what ACLs to check for the Gateways
|
||||
if rule != nil && !rule.ServiceWrite(cfgSnap.Service, nil) {
|
||||
// TODO (namespaces) - pass through a real ent authz ctx
|
||||
if rule != nil && rule.ServiceWrite(cfgSnap.Service, nil) != acl.Allow {
|
||||
return status.Errorf(codes.PermissionDenied, "permission denied")
|
||||
}
|
||||
default:
|
||||
|
|
|
@ -450,7 +450,7 @@ func TestServer_StreamAggregatedResources_ACLEnforcement(t *testing.T) {
|
|||
// Parse the ACL and enforce it
|
||||
policy, err := acl.NewPolicyFromSource("", 0, tt.acl, acl.SyntaxLegacy, nil)
|
||||
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)
|
||||
defer envoy.Close()
|
||||
|
@ -521,7 +521,7 @@ func TestServer_StreamAggregatedResources_ACLTokenDeleted_StreamTerminatedDuring
|
|||
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)
|
||||
defer envoy.Close()
|
||||
|
@ -612,7 +612,7 @@ func TestServer_StreamAggregatedResources_ACLTokenDeleted_StreamTerminatedInBack
|
|||
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)
|
||||
defer envoy.Close()
|
||||
|
|
Loading…
Reference in New Issue