updated Allocation.List to properly handle ACL checking for namespace=*
This commit is contained in:
parent
6743803e5c
commit
be32fb7d3c
|
@ -38,6 +38,7 @@ __BACKWARDS INCOMPATIBILITIES:__
|
|||
BUG FIXES:
|
||||
|
||||
* core: Fixed a bug where blocking queries would not include the query's maximum wait time when calculating whether it was safe to retry. [[GH-8921](https://github.com/hashicorp/nomad/issues/8921)]
|
||||
* core: Fixed a bug where ACL handling prevented cross-namespace allocation listing [[GH-9278](https://github.com/hashicorp/nomad/issues/9278)]
|
||||
* config (Enterprise): Fixed default enterprise config merging. [[GH-9083](https://github.com/hashicorp/nomad/pull/9083)]
|
||||
* client: Fixed an issue with the Java fingerprinter on macOS causing pop-up notifications when no JVM installed. [[GH-9225](https://github.com/hashicorp/nomad/pull/9225)]
|
||||
* consul: Fixed a bug to correctly validate task when using script-checks in group-level services [[GH-8952](https://github.com/hashicorp/nomad/issues/8952)]
|
||||
|
|
|
@ -29,6 +29,10 @@ func (a *Alloc) List(args *structs.AllocListRequest, reply *structs.AllocListRes
|
|||
}
|
||||
defer metrics.MeasureSince([]string{"nomad", "alloc", "list"}, time.Now())
|
||||
|
||||
if args.RequestNamespace() == structs.AllNamespacesSentinel {
|
||||
return a.listAllNamespaces(args, reply)
|
||||
}
|
||||
|
||||
// Check namespace read-job permissions
|
||||
aclObj, err := a.srv.ResolveToken(args.AuthToken)
|
||||
if err != nil {
|
||||
|
@ -37,10 +41,6 @@ func (a *Alloc) List(args *structs.AllocListRequest, reply *structs.AllocListRes
|
|||
return structs.ErrPermissionDenied
|
||||
}
|
||||
|
||||
allow := func(ns string) bool {
|
||||
return aclObj.AllowNsOp(ns, acl.NamespaceCapabilityListJobs)
|
||||
}
|
||||
|
||||
// Setup the blocking query
|
||||
opts := blockingOptions{
|
||||
queryOpts: &args.QueryOptions,
|
||||
|
@ -51,16 +51,7 @@ func (a *Alloc) List(args *structs.AllocListRequest, reply *structs.AllocListRes
|
|||
var iter memdb.ResultIterator
|
||||
|
||||
prefix := args.QueryOptions.Prefix
|
||||
if args.RequestNamespace() == structs.AllNamespacesSentinel {
|
||||
allowedNSes, err := allowedNSes(aclObj, state, allow)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
iter, err = state.AllocsByIDPrefixInNSes(ws, allowedNSes, prefix)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else if prefix != "" {
|
||||
if prefix != "" {
|
||||
iter, err = state.AllocsByIDPrefix(ws, args.RequestNamespace(), prefix)
|
||||
} else {
|
||||
iter, err = state.AllocsByNamespace(ws, args.RequestNamespace())
|
||||
|
@ -94,6 +85,68 @@ func (a *Alloc) List(args *structs.AllocListRequest, reply *structs.AllocListRes
|
|||
return a.srv.blockingRPC(&opts)
|
||||
}
|
||||
|
||||
// listAllNamespaces lists all allocations across all namespaces
|
||||
func (a *Alloc) listAllNamespaces(args *structs.AllocListRequest, reply *structs.AllocListResponse) error {
|
||||
// Check for read-job permissions
|
||||
aclObj, err := a.srv.ResolveToken(args.AuthToken)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
prefix := args.QueryOptions.Prefix
|
||||
allow := func(ns string) bool {
|
||||
return aclObj.AllowNsOp(ns, acl.NamespaceCapabilityReadJob)
|
||||
}
|
||||
|
||||
// Setup the blocking query
|
||||
opts := blockingOptions{
|
||||
queryOpts: &args.QueryOptions,
|
||||
queryMeta: &reply.QueryMeta,
|
||||
run: func(ws memdb.WatchSet, state *state.StateStore) error {
|
||||
// get list of accessible namespaces
|
||||
allowedNSes, err := allowedNSes(aclObj, state, allow)
|
||||
if err == structs.ErrPermissionDenied {
|
||||
// return empty allocations if token isn't authorized for any
|
||||
// namespace, matching other endpoints
|
||||
reply.Allocations = []*structs.AllocListStub{}
|
||||
} else if err != nil {
|
||||
return err
|
||||
} else {
|
||||
var iter memdb.ResultIterator
|
||||
var err error
|
||||
if prefix != "" {
|
||||
iter, err = state.AllocsByIDPrefixAllNSs(ws, prefix)
|
||||
} else {
|
||||
iter, err = state.Allocs(ws)
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var allocs []*structs.AllocListStub
|
||||
for raw := iter.Next(); raw != nil; raw = iter.Next() {
|
||||
alloc := raw.(*structs.Allocation)
|
||||
if allowedNSes != nil && !allowedNSes[alloc.Namespace] {
|
||||
continue
|
||||
}
|
||||
allocs = append(allocs, alloc.Stub(args.Fields))
|
||||
}
|
||||
reply.Allocations = allocs
|
||||
}
|
||||
|
||||
// Use the last index that affected the jobs table
|
||||
index, err := state.Index("allocs")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
reply.Index = index
|
||||
|
||||
// Set the query response
|
||||
a.srv.setQueryMeta(&reply.QueryMeta)
|
||||
return nil
|
||||
}}
|
||||
return a.srv.blockingRPC(&opts)
|
||||
}
|
||||
|
||||
// GetAlloc is used to lookup a particular allocation
|
||||
func (a *Alloc) GetAlloc(args *structs.AllocSpecificRequest,
|
||||
reply *structs.SingleAllocResponse) error {
|
||||
|
|
|
@ -3329,35 +3329,17 @@ func allocNamespaceFilter(namespace string) func(interface{}) bool {
|
|||
}
|
||||
|
||||
// AllocsByIDPrefix is used to lookup allocs by prefix
|
||||
func (s *StateStore) AllocsByIDPrefixInNSes(ws memdb.WatchSet, namespaces map[string]bool, prefix string) (memdb.ResultIterator, error) {
|
||||
func (s *StateStore) AllocsByIDPrefixAllNSs(ws memdb.WatchSet, prefix string) (memdb.ResultIterator, error) {
|
||||
txn := s.db.ReadTxn()
|
||||
|
||||
var iter memdb.ResultIterator
|
||||
var err error
|
||||
if prefix != "" {
|
||||
iter, err = txn.Get("allocs", "id_prefix", prefix)
|
||||
} else {
|
||||
iter, err = txn.Get("allocs", "id")
|
||||
|
||||
}
|
||||
iter, err := txn.Get("allocs", "id_prefix", prefix)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("alloc lookup failed: %v", err)
|
||||
}
|
||||
|
||||
ws.Add(iter.WatchCh())
|
||||
|
||||
// Wrap the iterator in a filter
|
||||
nsesFilter := func(raw interface{}) bool {
|
||||
alloc, ok := raw.(*structs.Allocation)
|
||||
if !ok {
|
||||
return true
|
||||
}
|
||||
|
||||
return namespaces[alloc.Namespace]
|
||||
}
|
||||
|
||||
wrap := memdb.NewFilterIterator(iter, nsesFilter)
|
||||
return wrap, nil
|
||||
return iter, nil
|
||||
}
|
||||
|
||||
// AllocsByNode returns all the allocations by node
|
||||
|
|
Loading…
Reference in New Issue