consul: break acl filtering into a separate struct
This commit is contained in:
parent
404d4d653c
commit
1ff496d6dd
210
consul/acl.go
210
consul/acl.go
|
@ -2,6 +2,7 @@ package consul
|
|||
|
||||
import (
|
||||
"errors"
|
||||
"log"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
|
@ -193,10 +194,125 @@ func (s *Server) useACLPolicy(id, authDC string, cached *aclCacheEntry, p *struc
|
|||
return compiled, nil
|
||||
}
|
||||
|
||||
// applyDiscoveryACLs is used to filter results from our service catalog based
|
||||
// on the configured rules for the request ACL. Nodes or services which do
|
||||
// not match the ACL rules will be dropped from the result.
|
||||
func (s *Server) applyDiscoveryACLs(token string, subj interface{}) error {
|
||||
// aclFilter is used to filter results from our state store based on ACL rules
|
||||
// configured for the provided token.
|
||||
type aclFilter struct {
|
||||
acl acl.ACL
|
||||
logger *log.Logger
|
||||
}
|
||||
|
||||
// filterService is used to determine if a service is accessible for an ACL.
|
||||
func (f *aclFilter) filterService(service string) bool {
|
||||
if service == "" || service == ConsulServiceID {
|
||||
return true
|
||||
}
|
||||
return f.acl.ServiceRead(service)
|
||||
}
|
||||
|
||||
// filterHealthChecks is used to filter a set of health checks down based on
|
||||
// the configured ACL rules for a token.
|
||||
func (f *aclFilter) filterHealthChecks(checks *structs.HealthChecks) {
|
||||
hc := *checks
|
||||
for i := 0; i < len(hc); i++ {
|
||||
check := hc[i]
|
||||
if f.filterService(check.ServiceName) {
|
||||
continue
|
||||
}
|
||||
f.logger.Printf("[DEBUG] consul: dropping check %q from result due to ACLs", check.CheckID)
|
||||
hc = append(hc[:i], hc[i+1:]...)
|
||||
i--
|
||||
}
|
||||
*checks = hc
|
||||
}
|
||||
|
||||
// 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.filterService(svc) {
|
||||
continue
|
||||
}
|
||||
f.logger.Printf("[DEBUG] consul: dropping service %q from result due to ACLs", svc)
|
||||
delete(services, svc)
|
||||
}
|
||||
}
|
||||
|
||||
// filterServiceNodes is used to filter a set of nodes for a given service
|
||||
// based on the configured ACL rules.
|
||||
func (f *aclFilter) filterServiceNodes(nodes *structs.ServiceNodes) {
|
||||
sn := *nodes
|
||||
for i := 0; i < len(sn); i++ {
|
||||
node := sn[i]
|
||||
if f.filterService(node.ServiceName) {
|
||||
continue
|
||||
}
|
||||
f.logger.Printf("[DEBUG] consul: dropping node %q from result due to ACLs", node.Node)
|
||||
sn = append(sn[:i], sn[i+1:]...)
|
||||
i--
|
||||
}
|
||||
*nodes = sn
|
||||
}
|
||||
|
||||
// filterNodeServices is used to filter services on a given node base on ACLs.
|
||||
func (f *aclFilter) filterNodeServices(services *structs.NodeServices) {
|
||||
for svc, _ := range services.Services {
|
||||
if f.filterService(svc) {
|
||||
continue
|
||||
}
|
||||
f.logger.Printf("[DEBUG] consul: dropping service %q from result due to ACLs", svc)
|
||||
delete(services.Services, svc)
|
||||
}
|
||||
}
|
||||
|
||||
// filterCheckServiceNodes is used to filter nodes based on ACL rules.
|
||||
func (f *aclFilter) filterCheckServiceNodes(nodes *structs.CheckServiceNodes) {
|
||||
csn := *nodes
|
||||
for i := 0; i < len(csn); i++ {
|
||||
node := csn[i]
|
||||
if f.filterService(node.Service.Service) {
|
||||
continue
|
||||
}
|
||||
f.logger.Printf("[DEBUG] consul: dropping node %q from result due to ACLs", node.Node)
|
||||
csn = append(csn[:i], csn[i+1:]...)
|
||||
i--
|
||||
}
|
||||
*nodes = csn
|
||||
}
|
||||
|
||||
// filterNodeDump is used to filter through all parts of a node dump and
|
||||
// remove elements the provided ACL token cannot access.
|
||||
func (f *aclFilter) filterNodeDump(dump *structs.NodeDump) {
|
||||
nd := *dump
|
||||
for i := 0; i < len(nd); i++ {
|
||||
info := nd[i]
|
||||
|
||||
// Filter services
|
||||
for i := 0; i < len(info.Services); i++ {
|
||||
svc := info.Services[i].Service
|
||||
if f.filterService(svc) {
|
||||
continue
|
||||
}
|
||||
f.logger.Printf("[DEBUG] consul: dropping service %q from result due to ACLs", svc)
|
||||
info.Services = append(info.Services[:i], info.Services[i+1:]...)
|
||||
i--
|
||||
}
|
||||
|
||||
// Filter checks
|
||||
for i := 0; i < len(info.Checks); i++ {
|
||||
chk := info.Checks[i]
|
||||
if f.filterService(chk.ServiceName) {
|
||||
continue
|
||||
}
|
||||
f.logger.Printf("[DEBUG] consul: dropping check %q from result due to ACLs", chk.CheckID)
|
||||
info.Checks = append(info.Checks[:i], info.Checks[i+1:]...)
|
||||
i--
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// aclFilter is used to filter results from our service catalog based on the
|
||||
// rules configured for the provided token. The subject is scrubbed and
|
||||
// modified in-place, leaving only resources the token can access.
|
||||
func (s *Server) aclFilter(token string, subj interface{}) error {
|
||||
// Get the ACL from the token
|
||||
acl, err := s.resolveToken(token)
|
||||
if err != nil {
|
||||
|
@ -208,97 +324,27 @@ func (s *Server) applyDiscoveryACLs(token string, subj interface{}) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
filt := func(service string) bool {
|
||||
// Don't filter the "consul" service or empty service names
|
||||
if service == "" || service == ConsulServiceID {
|
||||
return true
|
||||
}
|
||||
|
||||
// Check the ACL
|
||||
if !acl.ServiceRead(service) {
|
||||
s.logger.Printf("[DEBUG] consul: reading service '%s' denied due to ACLs", service)
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
// Create the filter
|
||||
filt := &aclFilter{acl, s.logger}
|
||||
|
||||
switch v := subj.(type) {
|
||||
// Filter health checks
|
||||
case *structs.IndexedHealthChecks:
|
||||
for i := 0; i < len(v.HealthChecks); i++ {
|
||||
hc := v.HealthChecks[i]
|
||||
if filt(hc.ServiceName) {
|
||||
continue
|
||||
}
|
||||
v.HealthChecks = append(v.HealthChecks[:i], v.HealthChecks[i+1:]...)
|
||||
i--
|
||||
}
|
||||
filt.filterHealthChecks(&v.HealthChecks)
|
||||
|
||||
// Filter services
|
||||
case *structs.IndexedServices:
|
||||
for svc, _ := range v.Services {
|
||||
if filt(svc) {
|
||||
continue
|
||||
}
|
||||
delete(v.Services, svc)
|
||||
}
|
||||
filt.filterServices(v.Services)
|
||||
|
||||
// Filter service nodes
|
||||
case *structs.IndexedServiceNodes:
|
||||
for i := 0; i < len(v.ServiceNodes); i++ {
|
||||
node := v.ServiceNodes[i]
|
||||
if filt(node.ServiceName) {
|
||||
continue
|
||||
}
|
||||
v.ServiceNodes = append(v.ServiceNodes[:i], v.ServiceNodes[i+1:]...)
|
||||
i--
|
||||
}
|
||||
filt.filterServiceNodes(&v.ServiceNodes)
|
||||
|
||||
// Filter node services
|
||||
case *structs.IndexedNodeServices:
|
||||
for svc, _ := range v.NodeServices.Services {
|
||||
if filt(svc) {
|
||||
continue
|
||||
}
|
||||
delete(v.NodeServices.Services, svc)
|
||||
}
|
||||
filt.filterNodeServices(v.NodeServices)
|
||||
|
||||
// Filter check service nodes
|
||||
case *structs.IndexedCheckServiceNodes:
|
||||
for i := 0; i < len(v.Nodes); i++ {
|
||||
cs := v.Nodes[i]
|
||||
if filt(cs.Service.Service) {
|
||||
continue
|
||||
}
|
||||
v.Nodes = append(v.Nodes[:i], v.Nodes[i+1:]...)
|
||||
i--
|
||||
}
|
||||
filt.filterCheckServiceNodes(&v.Nodes)
|
||||
|
||||
// Filter node dumps
|
||||
case *structs.IndexedNodeDump:
|
||||
for i := 0; i < len(v.Dump); i++ {
|
||||
dump := v.Dump[i]
|
||||
|
||||
// Filter the services
|
||||
for i := 0; i < len(dump.Services); i++ {
|
||||
svc := dump.Services[i]
|
||||
if filt(svc.Service) {
|
||||
continue
|
||||
}
|
||||
dump.Services = append(dump.Services[:i], dump.Services[i+1:]...)
|
||||
i--
|
||||
}
|
||||
|
||||
// Filter the checks
|
||||
for i := 0; i < len(dump.Checks); i++ {
|
||||
chk := dump.Checks[i]
|
||||
if filt(chk.ServiceName) {
|
||||
continue
|
||||
}
|
||||
dump.Checks = append(dump.Checks[:i], dump.Checks[i+1:]...)
|
||||
i--
|
||||
}
|
||||
}
|
||||
filt.filterNodeDump(&v.Dump)
|
||||
}
|
||||
|
||||
return nil
|
||||
|
|
|
@ -126,7 +126,7 @@ func (c *Catalog) ListNodes(args *structs.DCSpecificRequest, reply *structs.Inde
|
|||
state.QueryTables("Nodes"),
|
||||
func() error {
|
||||
reply.Index, reply.Nodes = state.Nodes()
|
||||
return c.srv.applyDiscoveryACLs(args.Token, reply)
|
||||
return c.srv.aclFilter(args.Token, reply)
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -143,7 +143,7 @@ func (c *Catalog) ListServices(args *structs.DCSpecificRequest, reply *structs.I
|
|||
state.QueryTables("Services"),
|
||||
func() error {
|
||||
reply.Index, reply.Services = state.Services()
|
||||
return c.srv.applyDiscoveryACLs(args.Token, reply)
|
||||
return c.srv.aclFilter(args.Token, reply)
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -169,7 +169,7 @@ func (c *Catalog) ServiceNodes(args *structs.ServiceSpecificRequest, reply *stru
|
|||
} else {
|
||||
reply.Index, reply.ServiceNodes = state.ServiceNodes(args.ServiceName)
|
||||
}
|
||||
return c.srv.applyDiscoveryACLs(args.Token, reply)
|
||||
return c.srv.aclFilter(args.Token, reply)
|
||||
})
|
||||
|
||||
// Provide some metrics
|
||||
|
@ -203,6 +203,6 @@ func (c *Catalog) NodeServices(args *structs.NodeSpecificRequest, reply *structs
|
|||
state.QueryTables("NodeServices"),
|
||||
func() error {
|
||||
reply.Index, reply.NodeServices = state.NodeServices(args.Node)
|
||||
return c.srv.applyDiscoveryACLs(args.Token, reply)
|
||||
return c.srv.aclFilter(args.Token, reply)
|
||||
})
|
||||
}
|
||||
|
|
|
@ -43,7 +43,7 @@ func (h *Health) NodeChecks(args *structs.NodeSpecificRequest,
|
|||
state.QueryTables("NodeChecks"),
|
||||
func() error {
|
||||
reply.Index, reply.HealthChecks = state.NodeChecks(args.Node)
|
||||
return h.srv.applyDiscoveryACLs(args.Token, reply)
|
||||
return h.srv.aclFilter(args.Token, reply)
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -67,7 +67,7 @@ func (h *Health) ServiceChecks(args *structs.ServiceSpecificRequest,
|
|||
state.QueryTables("ServiceChecks"),
|
||||
func() error {
|
||||
reply.Index, reply.HealthChecks = state.ServiceChecks(args.ServiceName)
|
||||
return h.srv.applyDiscoveryACLs(args.Token, reply)
|
||||
return h.srv.aclFilter(args.Token, reply)
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -93,7 +93,7 @@ func (h *Health) ServiceNodes(args *structs.ServiceSpecificRequest, reply *struc
|
|||
} else {
|
||||
reply.Index, reply.Nodes = state.CheckServiceNodes(args.ServiceName)
|
||||
}
|
||||
return h.srv.applyDiscoveryACLs(args.Token, reply)
|
||||
return h.srv.aclFilter(args.Token, reply)
|
||||
})
|
||||
|
||||
// Provide some metrics
|
||||
|
|
|
@ -25,8 +25,12 @@ func (m *Internal) NodeInfo(args *structs.NodeSpecificRequest,
|
|||
&reply.QueryMeta,
|
||||
state.QueryTables("NodeInfo"),
|
||||
func() error {
|
||||
reply.Index, reply.Dump = state.NodeInfo(args.Node)
|
||||
return m.srv.applyDiscoveryACLs(args.Token, reply)
|
||||
index, dump := state.NodeInfo(args.Node)
|
||||
if err := m.srv.aclFilter(args.Token, &dump); err != nil {
|
||||
return err
|
||||
}
|
||||
reply.Index, reply.Dump = index, dump
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -43,8 +47,12 @@ func (m *Internal) NodeDump(args *structs.DCSpecificRequest,
|
|||
&reply.QueryMeta,
|
||||
state.QueryTables("NodeDump"),
|
||||
func() error {
|
||||
reply.Index, reply.Dump = state.NodeDump()
|
||||
return m.srv.applyDiscoveryACLs(args.Token, reply)
|
||||
index, dump := state.NodeDump()
|
||||
if err := m.srv.aclFilter(args.Token, &dump); err != nil {
|
||||
return err
|
||||
}
|
||||
reply.Index, reply.Dump = index, dump
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue