Catalog + Namespace OSS changes. (#7219)

* Various Prepared Query + Namespace things

* Last round of OSS changes for a namespaced catalog
This commit is contained in:
Matt Keeler 2020-02-10 10:40:44 -05:00 committed by GitHub
parent 20600403b2
commit d0cd092e3b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 57 additions and 27 deletions

View File

@ -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 // Should this require lesser permissions? Really the only reason to require authorization at all is
// to prevent external entities from DoS Consul with repeated rule translation requests // to prevent external entities from DoS Consul with repeated rule translation requests
// TODO (namespaces) - pass through a real ent authz ctx
if rule != nil && rule.ACLRead(nil) != acl.Allow { if rule != nil && rule.ACLRead(nil) != acl.Allow {
return nil, acl.ErrPermissionDenied return nil, acl.ErrPermissionDenied
} }

View File

@ -1107,7 +1107,6 @@ func (s *HTTPServer) AgentNodeMaintenance(resp http.ResponseWriter, req *http.Re
if err != nil { if err != nil {
return nil, err return nil, err
} }
// TODO (namespaces) - pass through a real ent authz ctx?
if rule != nil && rule.NodeWrite(s.agent.config.NodeName, nil) != acl.Allow { if rule != nil && rule.NodeWrite(s.agent.config.NodeName, nil) != acl.Allow {
return nil, acl.ErrPermissionDenied return nil, acl.ErrPermissionDenied
} }
@ -1325,12 +1324,15 @@ func (s *HTTPServer) AgentConnectCALeafCert(resp http.ResponseWriter, req *http.
// not the ID of the service instance. // not the ID of the service instance.
serviceName := strings.TrimPrefix(req.URL.Path, "/v1/agent/connect/ca/leaf/") serviceName := strings.TrimPrefix(req.URL.Path, "/v1/agent/connect/ca/leaf/")
// TODO (namespaces) add namespacing to connect leaf cert generation request
args := cachetype.ConnectCALeafRequest{ args := cachetype.ConnectCALeafRequest{
Service: serviceName, // Need name not ID Service: serviceName, // Need name not ID
} }
var qOpts structs.QueryOptions 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 // Store DC in the ConnectCALeafRequest but query opts separately
if done := s.parse(resp, req, &args.Datacenter, &qOpts); done { if done := s.parse(resp, req, &args.Datacenter, &qOpts); done {
return nil, nil return nil, nil

View File

@ -226,7 +226,7 @@ func (s *HTTPServer) catalogServiceNodes(resp http.ResponseWriter, req *http.Req
// Set default DC // Set default DC
args := structs.ServiceSpecificRequest{Connect: connect} 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 return nil, err
} }

View File

@ -487,9 +487,12 @@ func (s *ConnectCA) Sign(
if err != nil { if err != nil {
return err return err
} }
var authzContext acl.AuthorizerContext
var entMeta structs.EnterpriseMeta
if isService { if isService {
// TODO (namespaces) use actual ent authz context entMeta.Merge(serviceID.GetEnterpriseMeta())
if rule != nil && rule.ServiceWrite(serviceID.Service, nil) != acl.Allow { entMeta.FillAuthzContext(&authzContext)
if rule != nil && rule.ServiceWrite(serviceID.Service, &authzContext) != acl.Allow {
return acl.ErrPermissionDenied return acl.ErrPermissionDenied
} }
@ -500,8 +503,8 @@ func (s *ConnectCA) Sign(
"we are %s", serviceID.Datacenter, s.srv.config.Datacenter) "we are %s", serviceID.Datacenter, s.srv.config.Datacenter)
} }
} else if isAgent { } else if isAgent {
// TODO (namespaces) use actual ent authz context structs.DefaultEnterpriseMeta().FillAuthzContext(&authzContext)
if rule != nil && rule.NodeWrite(agentID.Agent, nil) != acl.Allow { if rule != nil && rule.NodeWrite(agentID.Agent, &authzContext) != acl.Allow {
return acl.ErrPermissionDenied return acl.ErrPermissionDenied
} }
} }
@ -588,10 +591,11 @@ func (s *ConnectCA) Sign(
// Set the response // Set the response
*reply = structs.IssuedCert{ *reply = structs.IssuedCert{
SerialNumber: connect.EncodeSerialNumber(cert.SerialNumber), SerialNumber: connect.EncodeSerialNumber(cert.SerialNumber),
CertPEM: pem, CertPEM: pem,
ValidAfter: cert.NotBefore, ValidAfter: cert.NotBefore,
ValidBefore: cert.NotAfter, ValidBefore: cert.NotAfter,
EnterpriseMeta: entMeta,
RaftIndex: structs.RaftIndex{ RaftIndex: structs.RaftIndex{
ModifyIndex: modIdx, ModifyIndex: modIdx,
CreateIndex: modIdx, CreateIndex: modIdx,

View File

@ -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. // 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 { if err != nil {
return err return err
} }
if rule != nil && c.srv.config.ACLEnforceVersion8 { if authz != nil && c.srv.config.ACLEnforceVersion8 {
// TODO (namespaces) use actual ent authz context var authzContext acl.AuthorizerContext
if rule.NodeWrite(args.Node, nil) != acl.Allow { structs.DefaultEnterpriseMeta().FillAuthzContext(&authzContext)
if authz.NodeWrite(args.Node, &authzContext) != acl.Allow {
return acl.ErrPermissionDenied 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. // 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 { if err != nil {
return err return err
} }
if rule != nil && c.srv.config.ACLEnforceVersion8 { if authz != nil && c.srv.config.ACLEnforceVersion8 {
// TODO (namespaces) use actual ent authz context var authzContext acl.AuthorizerContext
if rule.NodeRead(args.Node, nil) != acl.Allow { structs.WildcardEnterpriseMeta().FillAuthzContext(&authzContext)
if authz.NodeRead(args.Node, &authzContext) != acl.Allow {
return acl.ErrPermissionDenied return acl.ErrPermissionDenied
} }
} }

View File

@ -28,6 +28,11 @@ func (m *Internal) NodeInfo(args *structs.NodeSpecificRequest,
return err return err
} }
_, err := m.srv.ResolveTokenAndDefaultMeta(args.Token, &args.EnterpriseMeta, nil)
if err != nil {
return err
}
return m.srv.blockingQuery( return m.srv.blockingQuery(
&args.QueryOptions, &args.QueryOptions,
&reply.QueryMeta, &reply.QueryMeta,
@ -49,6 +54,11 @@ func (m *Internal) NodeDump(args *structs.DCSpecificRequest,
return err 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) filter, err := bexpr.CreateFilter(args.Filter, nil, reply.Dump)
if err != nil { if err != nil {
return err return err
@ -83,6 +93,11 @@ func (m *Internal) ServiceDump(args *structs.ServiceDumpRequest, reply *structs.
return err 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) filter, err := bexpr.CreateFilter(args.Filter, nil, reply.Nodes)
if err != nil { if err != nil {
return err return err

View File

@ -548,6 +548,7 @@ func (p *PreparedQuery) execute(query *structs.PreparedQuery,
// Capture the nodes and pass the DNS information through to the reply. // Capture the nodes and pass the DNS information through to the reply.
reply.Service = query.Service.Service reply.Service = query.Service.Service
reply.EnterpriseMeta = query.Service.EnterpriseMeta
reply.Nodes = nodes reply.Nodes = nodes
reply.DNS = query.DNS reply.DNS = query.DNS

View File

@ -43,6 +43,7 @@ func testServerACLConfig(cb func(*Config)) func(*Config) {
c.ACLsEnabled = true c.ACLsEnabled = true
c.ACLMasterToken = TestDefaultMasterToken c.ACLMasterToken = TestDefaultMasterToken
c.ACLDefaultPolicy = "deny" c.ACLDefaultPolicy = "deny"
c.ACLEnforceVersion8 = true
if cb != nil { if cb != nil {
cb(c) cb(c)

View File

@ -1206,7 +1206,7 @@ func (s *Store) getPolicyWithTxn(tx *memdb.Txn, ws memdb.WatchSet, value string,
} }
ws.Add(watchCh) ws.Add(watchCh)
if err != nil || policy == nil { if policy == nil {
return nil, err return nil, err
} }

View File

@ -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 // deleteNodeTxn is the inner method used for removing a node from
// the store within a given transaction. // 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 { func (s *Store) deleteNodeTxn(tx *memdb.Txn, idx uint64, nodeName string) error {
// Look up the node. // Look up the node.
node, err := tx.First("nodes", "id", nodeName) node, err := tx.First("nodes", "id", nodeName)

View File

@ -256,8 +256,6 @@ func TestDiscoveryChainRead(t *testing.T) {
}) })
})) }))
// TODO(namespaces): add a test
expectTarget_DC2 := newTarget("web", "", "default", "dc2") expectTarget_DC2 := newTarget("web", "", "default", "dc2")
expectTarget_DC2.MeshGateway = structs.MeshGatewayConfig{ expectTarget_DC2.MeshGateway = structs.MeshGatewayConfig{
Mode: structs.MeshGatewayModeLocal, Mode: structs.MeshGatewayModeLocal,

View File

@ -108,7 +108,7 @@ RETRY_ONCE:
func (s *HTTPServer) HealthServiceChecks(resp http.ResponseWriter, req *http.Request) (interface{}, error) { func (s *HTTPServer) HealthServiceChecks(resp http.ResponseWriter, req *http.Request) (interface{}, error) {
// Set default DC // Set default DC
args := structs.ServiceSpecificRequest{} args := structs.ServiceSpecificRequest{}
if err := s.parseEntMeta(req, &args.EnterpriseMeta); err != nil { if err := s.parseEntMetaNoWildcard(req, &args.EnterpriseMeta); err != nil {
return nil, err return nil, err
} }
s.parseSource(req, &args.Source) 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) { func (s *HTTPServer) healthServiceNodes(resp http.ResponseWriter, req *http.Request, connect bool) (interface{}, error) {
// Set default DC // Set default DC
args := structs.ServiceSpecificRequest{Connect: connect} 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 return nil, err
} }
s.parseSource(req, &args.Source) s.parseSource(req, &args.Source)

View File

@ -305,6 +305,9 @@ type PreparedQueryExecuteResponse struct {
// Service is the service that was queried. // Service is the service that was queried.
Service string Service string
// EnterpriseMeta of the service that was queried.
EnterpriseMeta
// Nodes has the nodes that were output by the query. // Nodes has the nodes that were output by the query.
Nodes CheckServiceNodes Nodes CheckServiceNodes

View File

@ -33,7 +33,6 @@ func (d *DiscoveryChain) Get(name string, opts *DiscoveryChainOptions, q *QueryO
if opts.EvaluateInDatacenter != "" { if opts.EvaluateInDatacenter != "" {
r.params.Set("compile-dc", opts.EvaluateInDatacenter) r.params.Set("compile-dc", opts.EvaluateInDatacenter)
} }
// TODO(namespaces): handle possible EvaluateInNamespace here
} }
if method == "POST" { if method == "POST" {

View File

@ -25,6 +25,9 @@ type ServiceQuery struct {
// Service is the service to query. // Service is the service to query.
Service string Service string
// Namespace of the service to query
Namespace string `json:",omitempty"`
// Near allows baking in the name of a node to automatically distance- // Near allows baking in the name of a node to automatically distance-
// sort from. The magic "_agent" value is supported, which sorts near // sort from. The magic "_agent" value is supported, which sorts near
// the agent which initiated the request by default. // the agent which initiated the request by default.
@ -119,6 +122,9 @@ type PreparedQueryExecuteResponse struct {
// Service is the service that was queried. // Service is the service that was queried.
Service string Service string
// Namespace of the service that was queried
Namespace string `json:",omitempty"`
// Nodes has the nodes that were output by the query. // Nodes has the nodes that were output by the query.
Nodes []ServiceEntry Nodes []ServiceEntry