diff --git a/agent/acl_endpoint.go b/agent/acl_endpoint.go index ac882acbb..f997530a9 100644 --- a/agent/acl_endpoint.go +++ b/agent/acl_endpoint.go @@ -110,7 +110,6 @@ 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 - // TODO (namespaces) - pass through a real ent authz ctx if rule != nil && rule.ACLRead(nil) != acl.Allow { return nil, acl.ErrPermissionDenied } diff --git a/agent/agent_endpoint.go b/agent/agent_endpoint.go index 7adcbe81d..3e47bbfb9 100644 --- a/agent/agent_endpoint.go +++ b/agent/agent_endpoint.go @@ -1107,7 +1107,6 @@ func (s *HTTPServer) AgentNodeMaintenance(resp http.ResponseWriter, req *http.Re if err != nil { return nil, err } - // 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 } @@ -1325,12 +1324,15 @@ func (s *HTTPServer) AgentConnectCALeafCert(resp http.ResponseWriter, req *http. // not the ID of the service instance. serviceName := strings.TrimPrefix(req.URL.Path, "/v1/agent/connect/ca/leaf/") - // TODO (namespaces) add namespacing to connect leaf cert generation request args := cachetype.ConnectCALeafRequest{ Service: serviceName, // Need name not ID } var qOpts structs.QueryOptions + if err := s.parseEntMetaNoWildcard(req, &args.EnterpriseMeta); err != nil { + return nil, err + } + // Store DC in the ConnectCALeafRequest but query opts separately if done := s.parse(resp, req, &args.Datacenter, &qOpts); done { return nil, nil diff --git a/agent/catalog_endpoint.go b/agent/catalog_endpoint.go index 74c3a7810..ff0cda3de 100644 --- a/agent/catalog_endpoint.go +++ b/agent/catalog_endpoint.go @@ -226,7 +226,7 @@ func (s *HTTPServer) catalogServiceNodes(resp http.ResponseWriter, req *http.Req // Set default DC args := structs.ServiceSpecificRequest{Connect: connect} - if err := s.parseEntMeta(req, &args.EnterpriseMeta); err != nil { + if err := s.parseEntMetaNoWildcard(req, &args.EnterpriseMeta); err != nil { return nil, err } diff --git a/agent/consul/connect_ca_endpoint.go b/agent/consul/connect_ca_endpoint.go index 6b6d54cde..3e07cd67e 100644 --- a/agent/consul/connect_ca_endpoint.go +++ b/agent/consul/connect_ca_endpoint.go @@ -487,9 +487,12 @@ func (s *ConnectCA) Sign( if err != nil { return err } + var authzContext acl.AuthorizerContext + var entMeta structs.EnterpriseMeta if isService { - // TODO (namespaces) use actual ent authz context - if rule != nil && rule.ServiceWrite(serviceID.Service, nil) != acl.Allow { + entMeta.Merge(serviceID.GetEnterpriseMeta()) + entMeta.FillAuthzContext(&authzContext) + if rule != nil && rule.ServiceWrite(serviceID.Service, &authzContext) != acl.Allow { return acl.ErrPermissionDenied } @@ -500,8 +503,8 @@ func (s *ConnectCA) Sign( "we are %s", serviceID.Datacenter, s.srv.config.Datacenter) } } else if isAgent { - // TODO (namespaces) use actual ent authz context - if rule != nil && rule.NodeWrite(agentID.Agent, nil) != acl.Allow { + structs.DefaultEnterpriseMeta().FillAuthzContext(&authzContext) + if rule != nil && rule.NodeWrite(agentID.Agent, &authzContext) != acl.Allow { return acl.ErrPermissionDenied } } @@ -588,10 +591,11 @@ func (s *ConnectCA) Sign( // Set the response *reply = structs.IssuedCert{ - SerialNumber: connect.EncodeSerialNumber(cert.SerialNumber), - CertPEM: pem, - ValidAfter: cert.NotBefore, - ValidBefore: cert.NotAfter, + SerialNumber: connect.EncodeSerialNumber(cert.SerialNumber), + CertPEM: pem, + ValidAfter: cert.NotBefore, + ValidBefore: cert.NotAfter, + EnterpriseMeta: entMeta, RaftIndex: structs.RaftIndex{ ModifyIndex: modIdx, CreateIndex: modIdx, diff --git a/agent/consul/coordinate_endpoint.go b/agent/consul/coordinate_endpoint.go index 87cddd6c0..fb381810d 100644 --- a/agent/consul/coordinate_endpoint.go +++ b/agent/consul/coordinate_endpoint.go @@ -139,13 +139,14 @@ func (c *Coordinate) Update(args *structs.CoordinateUpdateRequest, reply *struct } // Fetch the ACL token, if any, and enforce the node policy if enabled. - rule, err := c.srv.ResolveToken(args.Token) + authz, err := c.srv.ResolveToken(args.Token) if err != nil { return err } - if rule != nil && c.srv.config.ACLEnforceVersion8 { - // TODO (namespaces) use actual ent authz context - if rule.NodeWrite(args.Node, nil) != acl.Allow { + if authz != nil && c.srv.config.ACLEnforceVersion8 { + var authzContext acl.AuthorizerContext + structs.DefaultEnterpriseMeta().FillAuthzContext(&authzContext) + if authz.NodeWrite(args.Node, &authzContext) != acl.Allow { return acl.ErrPermissionDenied } } @@ -211,13 +212,15 @@ func (c *Coordinate) Node(args *structs.NodeSpecificRequest, reply *structs.Inde } // Fetch the ACL token, if any, and enforce the node policy if enabled. - rule, err := c.srv.ResolveToken(args.Token) + + authz, err := c.srv.ResolveToken(args.Token) if err != nil { return err } - if rule != nil && c.srv.config.ACLEnforceVersion8 { - // TODO (namespaces) use actual ent authz context - if rule.NodeRead(args.Node, nil) != acl.Allow { + if authz != nil && c.srv.config.ACLEnforceVersion8 { + var authzContext acl.AuthorizerContext + structs.WildcardEnterpriseMeta().FillAuthzContext(&authzContext) + if authz.NodeRead(args.Node, &authzContext) != acl.Allow { return acl.ErrPermissionDenied } } diff --git a/agent/consul/internal_endpoint.go b/agent/consul/internal_endpoint.go index 474a823d2..ff3e0d52a 100644 --- a/agent/consul/internal_endpoint.go +++ b/agent/consul/internal_endpoint.go @@ -28,6 +28,11 @@ func (m *Internal) NodeInfo(args *structs.NodeSpecificRequest, return err } + _, err := m.srv.ResolveTokenAndDefaultMeta(args.Token, &args.EnterpriseMeta, nil) + if err != nil { + return err + } + return m.srv.blockingQuery( &args.QueryOptions, &reply.QueryMeta, @@ -49,6 +54,11 @@ func (m *Internal) NodeDump(args *structs.DCSpecificRequest, return err } + _, err := m.srv.ResolveTokenAndDefaultMeta(args.Token, &args.EnterpriseMeta, nil) + if err != nil { + return err + } + filter, err := bexpr.CreateFilter(args.Filter, nil, reply.Dump) if err != nil { return err @@ -83,6 +93,11 @@ func (m *Internal) ServiceDump(args *structs.ServiceDumpRequest, reply *structs. return err } + _, err := m.srv.ResolveTokenAndDefaultMeta(args.Token, &args.EnterpriseMeta, nil) + if err != nil { + return err + } + filter, err := bexpr.CreateFilter(args.Filter, nil, reply.Nodes) if err != nil { return err diff --git a/agent/consul/prepared_query_endpoint.go b/agent/consul/prepared_query_endpoint.go index 4dc0fdbbb..7dd262b8d 100644 --- a/agent/consul/prepared_query_endpoint.go +++ b/agent/consul/prepared_query_endpoint.go @@ -548,6 +548,7 @@ func (p *PreparedQuery) execute(query *structs.PreparedQuery, // Capture the nodes and pass the DNS information through to the reply. reply.Service = query.Service.Service + reply.EnterpriseMeta = query.Service.EnterpriseMeta reply.Nodes = nodes reply.DNS = query.DNS diff --git a/agent/consul/server_test.go b/agent/consul/server_test.go index c72c33df5..5a224895e 100644 --- a/agent/consul/server_test.go +++ b/agent/consul/server_test.go @@ -43,6 +43,7 @@ func testServerACLConfig(cb func(*Config)) func(*Config) { c.ACLsEnabled = true c.ACLMasterToken = TestDefaultMasterToken c.ACLDefaultPolicy = "deny" + c.ACLEnforceVersion8 = true if cb != nil { cb(c) diff --git a/agent/consul/state/acl.go b/agent/consul/state/acl.go index b41a0c852..c39f67b51 100644 --- a/agent/consul/state/acl.go +++ b/agent/consul/state/acl.go @@ -1206,7 +1206,7 @@ func (s *Store) getPolicyWithTxn(tx *memdb.Txn, ws memdb.WatchSet, value string, } ws.Add(watchCh) - if err != nil || policy == nil { + if policy == nil { return nil, err } diff --git a/agent/consul/state/catalog.go b/agent/consul/state/catalog.go index bd19f9c6e..e365dbb3d 100644 --- a/agent/consul/state/catalog.go +++ b/agent/consul/state/catalog.go @@ -528,7 +528,6 @@ func (s *Store) deleteNodeCASTxn(tx *memdb.Txn, idx, cidx uint64, nodeName strin // deleteNodeTxn is the inner method used for removing a node from // the store within a given transaction. -// TODO (namespaces) (catalog) access to catalog tables needs to become namespace aware for services/checks func (s *Store) deleteNodeTxn(tx *memdb.Txn, idx uint64, nodeName string) error { // Look up the node. node, err := tx.First("nodes", "id", nodeName) diff --git a/agent/discovery_chain_endpoint_test.go b/agent/discovery_chain_endpoint_test.go index 4001cdae8..b55c8fcba 100644 --- a/agent/discovery_chain_endpoint_test.go +++ b/agent/discovery_chain_endpoint_test.go @@ -256,8 +256,6 @@ func TestDiscoveryChainRead(t *testing.T) { }) })) - // TODO(namespaces): add a test - expectTarget_DC2 := newTarget("web", "", "default", "dc2") expectTarget_DC2.MeshGateway = structs.MeshGatewayConfig{ Mode: structs.MeshGatewayModeLocal, diff --git a/agent/health_endpoint.go b/agent/health_endpoint.go index 3ab1a2cb9..379919de0 100644 --- a/agent/health_endpoint.go +++ b/agent/health_endpoint.go @@ -108,7 +108,7 @@ RETRY_ONCE: func (s *HTTPServer) HealthServiceChecks(resp http.ResponseWriter, req *http.Request) (interface{}, error) { // Set default DC args := structs.ServiceSpecificRequest{} - if err := s.parseEntMeta(req, &args.EnterpriseMeta); err != nil { + if err := s.parseEntMetaNoWildcard(req, &args.EnterpriseMeta); err != nil { return nil, err } s.parseSource(req, &args.Source) @@ -164,7 +164,7 @@ func (s *HTTPServer) HealthServiceNodes(resp http.ResponseWriter, req *http.Requ func (s *HTTPServer) healthServiceNodes(resp http.ResponseWriter, req *http.Request, connect bool) (interface{}, error) { // Set default DC args := structs.ServiceSpecificRequest{Connect: connect} - if err := s.parseEntMeta(req, &args.EnterpriseMeta); err != nil { + if err := s.parseEntMetaNoWildcard(req, &args.EnterpriseMeta); err != nil { return nil, err } s.parseSource(req, &args.Source) diff --git a/agent/structs/prepared_query.go b/agent/structs/prepared_query.go index 160643b11..0f795891c 100644 --- a/agent/structs/prepared_query.go +++ b/agent/structs/prepared_query.go @@ -305,6 +305,9 @@ type PreparedQueryExecuteResponse struct { // Service is the service that was queried. Service string + // EnterpriseMeta of the service that was queried. + EnterpriseMeta + // Nodes has the nodes that were output by the query. Nodes CheckServiceNodes diff --git a/api/discovery_chain.go b/api/discovery_chain.go index 407a3b08e..75fdbaee2 100644 --- a/api/discovery_chain.go +++ b/api/discovery_chain.go @@ -33,7 +33,6 @@ func (d *DiscoveryChain) Get(name string, opts *DiscoveryChainOptions, q *QueryO if opts.EvaluateInDatacenter != "" { r.params.Set("compile-dc", opts.EvaluateInDatacenter) } - // TODO(namespaces): handle possible EvaluateInNamespace here } if method == "POST" { diff --git a/api/prepared_query.go b/api/prepared_query.go index 020458116..5ac2535c7 100644 --- a/api/prepared_query.go +++ b/api/prepared_query.go @@ -25,6 +25,9 @@ type ServiceQuery struct { // Service is the service to query. Service string + // Namespace of the service to query + Namespace string `json:",omitempty"` + // Near allows baking in the name of a node to automatically distance- // sort from. The magic "_agent" value is supported, which sorts near // the agent which initiated the request by default. @@ -119,6 +122,9 @@ type PreparedQueryExecuteResponse struct { // Service is the service that was queried. Service string + // Namespace of the service that was queried + Namespace string `json:",omitempty"` + // Nodes has the nodes that were output by the query. Nodes []ServiceEntry