mirror of
https://github.com/status-im/consul.git
synced 2025-02-22 18:38:19 +00:00
acl: some acl authz refactors for nodes (#10909)
This commit is contained in:
parent
11b1dc1f97
commit
a6d22efb49
13
agent/acl.go
13
agent/acl.go
@ -62,6 +62,7 @@ func (a *Agent) vetServiceRegisterWithAuthorizer(authz acl.Authorizer, service *
|
||||
if service.Kind == structs.ServiceKindConnectProxy {
|
||||
service.FillAuthzContext(&authzContext)
|
||||
if authz.ServiceWrite(service.Proxy.DestinationServiceName, &authzContext) != acl.Allow {
|
||||
// TODO(partitions) fix this to include namespace and partition
|
||||
return acl.PermissionDenied("Missing service:write on %s", service.Proxy.DestinationServiceName)
|
||||
}
|
||||
}
|
||||
@ -98,7 +99,7 @@ func (a *Agent) vetCheckRegisterWithAuthorizer(authz acl.Authorizer, check *stru
|
||||
}
|
||||
} else {
|
||||
if authz.NodeWrite(a.config.NodeName, &authzContext) != acl.Allow {
|
||||
return acl.PermissionDenied("Missing node:write on %s", a.config.NodeName)
|
||||
return acl.PermissionDenied("Missing node:write on %s", structs.NodeNameString(a.config.NodeName, a.agentEnterpriseMeta()))
|
||||
}
|
||||
}
|
||||
|
||||
@ -110,7 +111,7 @@ func (a *Agent) vetCheckRegisterWithAuthorizer(authz acl.Authorizer, check *stru
|
||||
}
|
||||
} else {
|
||||
if authz.NodeWrite(a.config.NodeName, &authzContext) != acl.Allow {
|
||||
return acl.PermissionDenied("Missing node:write on %s", a.config.NodeName)
|
||||
return acl.PermissionDenied("Missing node:write on %s", structs.NodeNameString(a.config.NodeName, a.agentEnterpriseMeta()))
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -126,11 +127,11 @@ func (a *Agent) vetCheckUpdateWithAuthorizer(authz acl.Authorizer, checkID struc
|
||||
if existing := a.State.Check(checkID); existing != nil {
|
||||
if len(existing.ServiceName) > 0 {
|
||||
if authz.ServiceWrite(existing.ServiceName, &authzContext) != acl.Allow {
|
||||
return acl.PermissionDenied("Missing service:write on %s", existing.ServiceName)
|
||||
return acl.PermissionDenied("Missing service:write on %s", structs.ServiceIDString(existing.ServiceName, &existing.EnterpriseMeta))
|
||||
}
|
||||
} else {
|
||||
if authz.NodeWrite(a.config.NodeName, &authzContext) != acl.Allow {
|
||||
return acl.PermissionDenied("Missing node:write on %s", a.config.NodeName)
|
||||
return acl.PermissionDenied("Missing node:write on %s", structs.NodeNameString(a.config.NodeName, a.agentEnterpriseMeta()))
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@ -184,14 +185,12 @@ func (a *Agent) filterChecksWithAuthorizer(authz acl.Authorizer, checks *map[str
|
||||
var authzContext acl.AuthorizerContext
|
||||
// Filter out checks based on the node or service policy.
|
||||
for id, check := range *checks {
|
||||
check.FillAuthzContext(&authzContext)
|
||||
if len(check.ServiceName) > 0 {
|
||||
check.FillAuthzContext(&authzContext)
|
||||
if authz.ServiceRead(check.ServiceName, &authzContext) == acl.Allow {
|
||||
continue
|
||||
}
|
||||
} else {
|
||||
// TODO(partition): should this be a Default or Node flavored entmeta?
|
||||
check.NodeEnterpriseMetaForPartition().FillAuthzContext(&authzContext)
|
||||
if authz.NodeRead(a.config.NodeName, &authzContext) == acl.Allow {
|
||||
continue
|
||||
}
|
||||
|
@ -1247,7 +1247,10 @@ func (s *HTTPHandlers) AgentNodeMaintenance(resp http.ResponseWriter, req *http.
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if authz.NodeWrite(s.agent.config.NodeName, nil) != acl.Allow {
|
||||
|
||||
var authzContext acl.AuthorizerContext
|
||||
s.agent.agentEnterpriseMeta().FillAuthzContext(&authzContext)
|
||||
if authz.NodeWrite(s.agent.config.NodeName, &authzContext) != acl.Allow {
|
||||
return nil, acl.ErrPermissionDenied
|
||||
}
|
||||
|
||||
|
@ -5150,6 +5150,7 @@ func (tc testCase) run(format string, dataDir string) func(t *testing.T) {
|
||||
expected.ACLResolverSettings.Datacenter = expected.Datacenter
|
||||
expected.ACLResolverSettings.ACLsEnabled = expected.ACLsEnabled
|
||||
expected.ACLResolverSettings.NodeName = expected.NodeName
|
||||
expected.ACLResolverSettings.EnterpriseMeta = *structs.NodeEnterpriseMetaInPartition(expected.PartitionOrDefault())
|
||||
|
||||
assertDeepEqual(t, expected, actual, cmpopts.EquateEmpty())
|
||||
}
|
||||
@ -5189,6 +5190,7 @@ func TestLoad_FullConfig(t *testing.T) {
|
||||
}
|
||||
|
||||
defaultEntMeta := structs.DefaultEnterpriseMetaInDefaultPartition()
|
||||
nodeEntMeta := structs.NodeEnterpriseMetaInDefaultPartition()
|
||||
expected := &RuntimeConfig{
|
||||
// non-user configurable values
|
||||
AEInterval: time.Minute,
|
||||
@ -5241,6 +5243,7 @@ func TestLoad_FullConfig(t *testing.T) {
|
||||
ACLsEnabled: true,
|
||||
Datacenter: "rzo029wg",
|
||||
NodeName: "otlLxGaI",
|
||||
EnterpriseMeta: *nodeEntMeta,
|
||||
ACLDefaultPolicy: "72c2e7a0",
|
||||
ACLDownPolicy: "03eb2aee",
|
||||
ACLTokenTTL: 3321 * time.Second,
|
||||
|
@ -9,6 +9,7 @@
|
||||
"ACLTokenTTL": "0s",
|
||||
"ACLsEnabled": false,
|
||||
"Datacenter": "",
|
||||
"EnterpriseMeta": {},
|
||||
"NodeName": ""
|
||||
},
|
||||
"ACLTokenReplication": false,
|
||||
|
@ -217,9 +217,10 @@ const aclClientDisabledTTL = 30 * time.Second
|
||||
|
||||
// TODO: rename the fields to remove the ACL prefix
|
||||
type ACLResolverSettings struct {
|
||||
ACLsEnabled bool
|
||||
Datacenter string
|
||||
NodeName string
|
||||
ACLsEnabled bool
|
||||
Datacenter string
|
||||
NodeName string
|
||||
EnterpriseMeta structs.EnterpriseMeta
|
||||
|
||||
// ACLPolicyTTL is used to control the time-to-live of cached ACL policies. This has
|
||||
// a major impact on performance. By default, it is set to 30 seconds.
|
||||
@ -301,7 +302,11 @@ type ACLResolver struct {
|
||||
agentMasterAuthz acl.Authorizer
|
||||
}
|
||||
|
||||
func agentMasterAuthorizer(nodeName string) (acl.Authorizer, error) {
|
||||
func agentMasterAuthorizer(nodeName string, entMeta *structs.EnterpriseMeta) (acl.Authorizer, error) {
|
||||
// TODO(partitions,acls): this function likely needs split so that the generated policy can be partitioned appropriately
|
||||
|
||||
// TODO(partitions,acls): after this all works, write a test for this function when partitioned
|
||||
|
||||
// Build a policy for the agent master token.
|
||||
// The builtin agent master policy allows reading any node information
|
||||
// and allows writes to the agent with the node name of the running agent
|
||||
@ -355,7 +360,7 @@ func NewACLResolver(config *ACLResolverConfig) (*ACLResolver, error) {
|
||||
return nil, fmt.Errorf("invalid ACL down policy %q", config.Config.ACLDownPolicy)
|
||||
}
|
||||
|
||||
authz, err := agentMasterAuthorizer(config.Config.NodeName)
|
||||
authz, err := agentMasterAuthorizer(config.Config.NodeName, &config.Config.EnterpriseMeta)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to initialize the agent master authorizer")
|
||||
}
|
||||
@ -1443,7 +1448,7 @@ func (f *aclFilter) filterServiceNodes(nodes *structs.ServiceNodes) {
|
||||
if f.allowNode(node.Node, &authzContext) && f.allowService(node.ServiceName, &authzContext) {
|
||||
continue
|
||||
}
|
||||
f.logger.Debug("dropping node from result due to ACLs", "node", node.Node)
|
||||
f.logger.Debug("dropping node from result due to ACLs", "node", structs.NodeNameString(node.Node, &node.EnterpriseMeta))
|
||||
sn = append(sn[:i], sn[i+1:]...)
|
||||
i--
|
||||
}
|
||||
@ -1457,8 +1462,7 @@ func (f *aclFilter) filterNodeServices(services **structs.NodeServices) {
|
||||
}
|
||||
|
||||
var authzContext acl.AuthorizerContext
|
||||
// TODO(partitions): put partition into this wildcard?
|
||||
structs.WildcardEnterpriseMetaInDefaultPartition().FillAuthzContext(&authzContext)
|
||||
(*services).Node.FillAuthzContext(&authzContext)
|
||||
if !f.allowNode((*services).Node.Node, &authzContext) {
|
||||
*services = nil
|
||||
return
|
||||
@ -1482,8 +1486,7 @@ func (f *aclFilter) filterNodeServiceList(services **structs.NodeServiceList) {
|
||||
}
|
||||
|
||||
var authzContext acl.AuthorizerContext
|
||||
// TODO(partitions): put partition into this wildcard?
|
||||
structs.WildcardEnterpriseMetaInDefaultPartition().FillAuthzContext(&authzContext)
|
||||
(*services).Node.FillAuthzContext(&authzContext)
|
||||
if !f.allowNode((*services).Node.Node, &authzContext) {
|
||||
*services = nil
|
||||
return
|
||||
@ -1523,7 +1526,7 @@ func (f *aclFilter) filterCheckServiceNodes(nodes *structs.CheckServiceNodes) {
|
||||
if f.allowNode(node.Node.Node, &authzContext) && f.allowService(node.Service.Service, &authzContext) {
|
||||
continue
|
||||
}
|
||||
f.logger.Debug("dropping node from result due to ACLs", "node", node.Node.Node)
|
||||
f.logger.Debug("dropping node from result due to ACLs", "node", structs.NodeNameString(node.Node.Node, node.Node.GetEnterpriseMeta()))
|
||||
csn = append(csn[:i], csn[i+1:]...)
|
||||
i--
|
||||
}
|
||||
@ -1580,15 +1583,14 @@ func (f *aclFilter) filterSessions(sessions *structs.Sessions) {
|
||||
func (f *aclFilter) filterCoordinates(coords *structs.Coordinates) {
|
||||
c := *coords
|
||||
var authzContext acl.AuthorizerContext
|
||||
// TODO(partitions): put partition into this wildcard?
|
||||
structs.WildcardEnterpriseMetaInDefaultPartition().FillAuthzContext(&authzContext)
|
||||
|
||||
for i := 0; i < len(c); i++ {
|
||||
c[i].FillAuthzContext(&authzContext)
|
||||
node := c[i].Node
|
||||
if f.allowNode(node, &authzContext) {
|
||||
continue
|
||||
}
|
||||
f.logger.Debug("dropping node from result due to ACLs", "node", node)
|
||||
f.logger.Debug("dropping node from result due to ACLs", "node", structs.NodeNameString(node, c[i].GetEnterpriseMeta()))
|
||||
c = append(c[:i], c[i+1:]...)
|
||||
i--
|
||||
}
|
||||
@ -1622,10 +1624,9 @@ func (f *aclFilter) filterNodeDump(dump *structs.NodeDump) {
|
||||
info := nd[i]
|
||||
|
||||
// Filter nodes
|
||||
// TODO(partitions): put partition into this wildcard?
|
||||
structs.WildcardEnterpriseMetaInDefaultPartition().FillAuthzContext(&authzContext)
|
||||
info.FillAuthzContext(&authzContext)
|
||||
if node := info.Node; !f.allowNode(node, &authzContext) {
|
||||
f.logger.Debug("dropping node from result due to ACLs", "node", node)
|
||||
f.logger.Debug("dropping node from result due to ACLs", "node", structs.NodeNameString(node, info.GetEnterpriseMeta()))
|
||||
nd = append(nd[:i], nd[i+1:]...)
|
||||
i--
|
||||
continue
|
||||
@ -1691,15 +1692,14 @@ func (f *aclFilter) filterNodes(nodes *structs.Nodes) {
|
||||
n := *nodes
|
||||
|
||||
var authzContext acl.AuthorizerContext
|
||||
// TODO(partitions): put partition into this wildcard?
|
||||
structs.WildcardEnterpriseMetaInDefaultPartition().FillAuthzContext(&authzContext)
|
||||
|
||||
for i := 0; i < len(n); i++ {
|
||||
n[i].FillAuthzContext(&authzContext)
|
||||
node := n[i].Node
|
||||
if f.allowNode(node, &authzContext) {
|
||||
continue
|
||||
}
|
||||
f.logger.Debug("dropping node from result due to ACLs", "node", node)
|
||||
f.logger.Debug("dropping node from result due to ACLs", "node", structs.NodeNameString(node, n[i].GetEnterpriseMeta()))
|
||||
n = append(n[:i], n[i+1:]...)
|
||||
i--
|
||||
}
|
||||
|
@ -208,6 +208,7 @@ func (ac *AutoConfig) updateACLsInConfig(opts AutoConfigOptions, resp *pbautocon
|
||||
Datacenter: ac.config.Datacenter,
|
||||
},
|
||||
},
|
||||
// TODO(partitions): support auto-config in different partitions
|
||||
EnterpriseMeta: *structs.DefaultEnterpriseMetaInDefaultPartition(),
|
||||
}
|
||||
|
||||
|
@ -69,7 +69,7 @@ func (c *Client) setupSerf(conf *serf.Config, ch chan serf.Event, path string) (
|
||||
return nil, err
|
||||
}
|
||||
|
||||
addEnterpriseSerfTags(conf.Tags, c.config.agentEnterpriseMeta())
|
||||
addEnterpriseSerfTags(conf.Tags, c.config.AgentEnterpriseMeta())
|
||||
|
||||
conf.ReconnectTimeoutOverride = libserf.NewReconnectOverride(c.logger)
|
||||
|
||||
|
@ -73,6 +73,13 @@ func testClientWithConfigWithErr(t *testing.T, cb func(c *Config)) (string, *Cli
|
||||
cb(config)
|
||||
}
|
||||
|
||||
// Apply config to copied fields because many tests only set the old
|
||||
//values.
|
||||
config.ACLResolverSettings.ACLsEnabled = config.ACLsEnabled
|
||||
config.ACLResolverSettings.NodeName = config.NodeName
|
||||
config.ACLResolverSettings.Datacenter = config.Datacenter
|
||||
config.ACLResolverSettings.EnterpriseMeta = *config.AgentEnterpriseMeta()
|
||||
|
||||
client, err := NewClient(config, newDefaultDeps(t, config))
|
||||
return dir, client, err
|
||||
}
|
||||
|
@ -4,6 +4,6 @@ package consul
|
||||
|
||||
import "github.com/hashicorp/consul/agent/structs"
|
||||
|
||||
func (c *Config) agentEnterpriseMeta() *structs.EnterpriseMeta {
|
||||
func (c *Config) AgentEnterpriseMeta() *structs.EnterpriseMeta {
|
||||
return structs.NodeEnterpriseMetaInDefaultPartition()
|
||||
}
|
||||
|
@ -186,7 +186,7 @@ func (s *ConnectCA) Sign(
|
||||
"we are %s", serviceID.Datacenter, s.srv.config.Datacenter)
|
||||
}
|
||||
} else if isAgent {
|
||||
|
||||
// TODO(partitions): support auto-config in different partitions
|
||||
structs.DefaultEnterpriseMetaInDefaultPartition().FillAuthzContext(&authzContext)
|
||||
if authz.NodeWrite(agentID.Agent, &authzContext) != acl.Allow {
|
||||
return acl.ErrPermissionDenied
|
||||
|
@ -151,7 +151,7 @@ func (c *Coordinate) Update(args *structs.CoordinateUpdateRequest, reply *struct
|
||||
}
|
||||
|
||||
var authzContext acl.AuthorizerContext
|
||||
args.DefaultEnterpriseMetaForPartition().FillAuthzContext(&authzContext)
|
||||
args.FillAuthzContext(&authzContext)
|
||||
if authz.NodeWrite(args.Node, &authzContext) != acl.Allow {
|
||||
return acl.ErrPermissionDenied
|
||||
}
|
||||
@ -174,7 +174,7 @@ func (c *Coordinate) ListDatacenters(args *struct{}, reply *[]structs.Datacenter
|
||||
return err
|
||||
}
|
||||
|
||||
// TODO(partitions):
|
||||
// TODO(partitions): should we filter any of this out?
|
||||
|
||||
var out []structs.DatacenterMap
|
||||
|
||||
@ -248,7 +248,7 @@ func (c *Coordinate) Node(args *structs.NodeSpecificRequest, reply *structs.Inde
|
||||
}
|
||||
|
||||
var authzContext acl.AuthorizerContext
|
||||
args.WildcardEnterpriseMetaForPartition().FillAuthzContext(&authzContext)
|
||||
args.FillAuthzContext(&authzContext)
|
||||
if authz.NodeRead(args.Node, &authzContext) != acl.Allow {
|
||||
return acl.ErrPermissionDenied
|
||||
}
|
||||
|
@ -47,8 +47,7 @@ func (t *txnResultsFilter) Filter(i int) bool {
|
||||
result.KV.EnterpriseMeta.FillAuthzContext(&authzContext)
|
||||
return t.authorizer.KeyRead(result.KV.Key, &authzContext) != acl.Allow
|
||||
case result.Node != nil:
|
||||
// TODO(partitions): put partition into this wildcard?
|
||||
structs.WildcardEnterpriseMetaInDefaultPartition().FillAuthzContext(&authzContext)
|
||||
(*structs.Node)(result.Node).FillAuthzContext(&authzContext)
|
||||
return t.authorizer.NodeRead(result.Node.Node, &authzContext) != acl.Allow
|
||||
case result.Service != nil:
|
||||
result.Service.EnterpriseMeta.FillAuthzContext(&authzContext)
|
||||
|
@ -1438,6 +1438,7 @@ func (c *CAManager) SignCertificate(csr *x509.CertificateRequest, spiffeID conne
|
||||
|
||||
csr.URIs = uris
|
||||
}
|
||||
// TODO(partitions): support auto-config in different partitions
|
||||
entMeta.Merge(structs.DefaultEnterpriseMetaInDefaultPartition())
|
||||
}
|
||||
|
||||
|
@ -176,7 +176,7 @@ func (s *Server) setupSerf(conf *serf.Config, ch chan serf.Event, path string, w
|
||||
|
||||
conf.ReconnectTimeoutOverride = libserf.NewReconnectOverride(s.logger)
|
||||
|
||||
addEnterpriseSerfTags(conf.Tags, s.config.agentEnterpriseMeta())
|
||||
addEnterpriseSerfTags(conf.Tags, s.config.AgentEnterpriseMeta())
|
||||
|
||||
if s.config.OverrideInitialSerfTags != nil {
|
||||
s.config.OverrideInitialSerfTags(conf.Tags)
|
||||
|
@ -250,6 +250,7 @@ func testServerWithConfig(t *testing.T, cb func(*Config)) (string, *Server) {
|
||||
config.ACLResolverSettings.ACLsEnabled = config.ACLsEnabled
|
||||
config.ACLResolverSettings.NodeName = config.NodeName
|
||||
config.ACLResolverSettings.Datacenter = config.Datacenter
|
||||
config.ACLResolverSettings.EnterpriseMeta = *config.AgentEnterpriseMeta()
|
||||
|
||||
var err error
|
||||
srv, err = newServer(t, config)
|
||||
|
@ -11,9 +11,10 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"golang.org/x/crypto/blake2b"
|
||||
|
||||
"github.com/hashicorp/consul/acl"
|
||||
"github.com/hashicorp/consul/lib"
|
||||
"golang.org/x/crypto/blake2b"
|
||||
)
|
||||
|
||||
type ACLMode string
|
||||
@ -229,6 +230,7 @@ func (s *ACLNodeIdentity) SyntheticPolicy() *ACLPolicy {
|
||||
policy.Rules = rules
|
||||
policy.Syntax = acl.SyntaxCurrent
|
||||
policy.Datacenters = []string{s.Datacenter}
|
||||
// TODO(partitions,acls): this needs to be fed the correct partition
|
||||
policy.EnterpriseMeta = *DefaultEnterpriseMetaInDefaultPartition()
|
||||
policy.SetHash(true)
|
||||
return policy
|
||||
|
@ -1824,6 +1824,14 @@ type NodeInfo struct {
|
||||
Checks HealthChecks
|
||||
}
|
||||
|
||||
func (n *NodeInfo) GetEnterpriseMeta() *EnterpriseMeta {
|
||||
return NodeEnterpriseMetaInPartition(n.Partition)
|
||||
}
|
||||
|
||||
func (n *NodeInfo) PartitionOrDefault() string {
|
||||
return PartitionOrDefault(n.Partition)
|
||||
}
|
||||
|
||||
// NodeDump is used to dump all the nodes with all their
|
||||
// associated data. This is currently used for the UI only,
|
||||
// as it is rather expensive to generate.
|
||||
|
@ -128,6 +128,12 @@ func WildcardEnterpriseMetaInPartition(_ string) *EnterpriseMeta {
|
||||
// FillAuthzContext stub
|
||||
func (_ *EnterpriseMeta) FillAuthzContext(_ *acl.AuthorizerContext) {}
|
||||
|
||||
func (_ *Node) FillAuthzContext(_ *acl.AuthorizerContext) {}
|
||||
|
||||
func (_ *Coordinate) FillAuthzContext(_ *acl.AuthorizerContext) {}
|
||||
|
||||
func (_ *NodeInfo) FillAuthzContext(_ *acl.AuthorizerContext) {}
|
||||
|
||||
func (_ *EnterpriseMeta) Normalize() {}
|
||||
|
||||
// FillAuthzContext stub
|
||||
@ -149,6 +155,10 @@ func (_ *TxnServiceOp) FillAuthzContext(_ *acl.AuthorizerContext) {}
|
||||
// OSS Stub
|
||||
func (_ *TxnCheckOp) FillAuthzContext(_ *acl.AuthorizerContext) {}
|
||||
|
||||
func NodeNameString(node string, _ *EnterpriseMeta) string {
|
||||
return node
|
||||
}
|
||||
|
||||
func ServiceIDString(id string, _ *EnterpriseMeta) string {
|
||||
return id
|
||||
}
|
||||
|
@ -614,6 +614,8 @@ func (s *HTTPHandlers) UIMetricsProxy(resp http.ResponseWriter, req *http.Reques
|
||||
// This endpoint requires wildcard read on all services and all nodes.
|
||||
//
|
||||
// In enterprise it requires this _in all namespaces_ too.
|
||||
//
|
||||
// TODO(partitions,acls): need to revisit this
|
||||
var authzContext acl.AuthorizerContext
|
||||
entMeta.WildcardEnterpriseMetaForPartition().FillAuthzContext(&authzContext)
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user