2016-12-14 07:21:14 +00:00
|
|
|
package agent
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
|
2020-11-17 22:10:21 +00:00
|
|
|
"github.com/hashicorp/serf/serf"
|
|
|
|
|
2016-12-14 07:21:14 +00:00
|
|
|
"github.com/hashicorp/consul/acl"
|
2017-07-06 10:34:00 +00:00
|
|
|
"github.com/hashicorp/consul/agent/structs"
|
2021-12-03 20:36:28 +00:00
|
|
|
"github.com/hashicorp/consul/api"
|
|
|
|
"github.com/hashicorp/consul/types"
|
2016-12-14 07:21:14 +00:00
|
|
|
)
|
|
|
|
|
2020-01-27 19:54:32 +00:00
|
|
|
// aclAccessorID is used to convert an ACLToken's secretID to its accessorID for non-
|
|
|
|
// critical purposes, such as logging. Therefore we interpret all errors as empty-string
|
|
|
|
// so we can safely log it without handling non-critical errors at the usage site.
|
|
|
|
func (a *Agent) aclAccessorID(secretID string) string {
|
2022-01-22 19:47:59 +00:00
|
|
|
ident, err := a.delegate.ResolveTokenAndDefaultMeta(secretID, nil, nil)
|
2020-01-29 17:16:08 +00:00
|
|
|
if acl.IsErrNotFound(err) {
|
|
|
|
return ""
|
|
|
|
}
|
2020-01-27 19:54:32 +00:00
|
|
|
if err != nil {
|
2020-01-29 17:16:08 +00:00
|
|
|
a.logger.Debug("non-critical error resolving acl token accessor for logging", "error", err)
|
2020-01-27 19:54:32 +00:00
|
|
|
return ""
|
|
|
|
}
|
2022-01-22 19:47:59 +00:00
|
|
|
return ident.AccessorID()
|
2020-01-27 19:54:32 +00:00
|
|
|
}
|
|
|
|
|
2016-12-14 22:16:46 +00:00
|
|
|
// vetServiceRegister makes sure the service registration action is allowed by
|
|
|
|
// the given token.
|
|
|
|
func (a *Agent) vetServiceRegister(token string, service *structs.NodeService) error {
|
|
|
|
// Resolve the token and bail if ACLs aren't enabled.
|
2021-04-14 16:39:35 +00:00
|
|
|
authz, err := a.delegate.ResolveTokenAndDefaultMeta(token, nil, nil)
|
2016-12-14 22:16:46 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2019-12-18 18:46:53 +00:00
|
|
|
|
|
|
|
return a.vetServiceRegisterWithAuthorizer(authz, service)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (a *Agent) vetServiceRegisterWithAuthorizer(authz acl.Authorizer, service *structs.NodeService) error {
|
2019-12-18 18:43:24 +00:00
|
|
|
var authzContext acl.AuthorizerContext
|
2021-12-13 17:43:33 +00:00
|
|
|
|
2016-12-14 22:16:46 +00:00
|
|
|
// Vet the service itself.
|
2021-12-13 17:43:33 +00:00
|
|
|
service.FillAuthzContext(&authzContext)
|
2019-12-18 18:46:53 +00:00
|
|
|
if authz.ServiceWrite(service.Service, &authzContext) != acl.Allow {
|
2022-02-11 20:53:23 +00:00
|
|
|
return acl.PermissionDeniedByACL(authz, &authzContext, acl.ResourceService, acl.AccessWrite, service.Service)
|
2016-12-14 22:16:46 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Vet any service that might be getting overwritten.
|
2019-12-10 02:26:41 +00:00
|
|
|
if existing := a.State.Service(service.CompoundServiceID()); existing != nil {
|
|
|
|
existing.FillAuthzContext(&authzContext)
|
2019-12-18 18:46:53 +00:00
|
|
|
if authz.ServiceWrite(existing.Service, &authzContext) != acl.Allow {
|
2022-02-11 20:53:23 +00:00
|
|
|
return acl.PermissionDeniedByACL(authz, &authzContext, acl.ResourceService, acl.AccessWrite, existing.Service)
|
2016-12-14 22:16:46 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-09-27 13:33:12 +00:00
|
|
|
// If the service is a proxy, ensure that it has write on the destination too
|
|
|
|
// since it can be discovered as an instance of that service.
|
|
|
|
if service.Kind == structs.ServiceKindConnectProxy {
|
2019-12-10 02:26:41 +00:00
|
|
|
service.FillAuthzContext(&authzContext)
|
2019-12-18 18:46:53 +00:00
|
|
|
if authz.ServiceWrite(service.Proxy.DestinationServiceName, &authzContext) != acl.Allow {
|
2022-02-11 20:53:23 +00:00
|
|
|
return acl.PermissionDeniedByACL(authz, &authzContext, acl.ResourceService, acl.AccessWrite, service.Proxy.DestinationServiceName)
|
2018-09-27 13:33:12 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-12-14 22:16:46 +00:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2019-12-18 18:46:53 +00:00
|
|
|
func (a *Agent) vetServiceUpdateWithAuthorizer(authz acl.Authorizer, serviceID structs.ServiceID) error {
|
2019-12-18 18:43:24 +00:00
|
|
|
var authzContext acl.AuthorizerContext
|
2019-12-10 02:26:41 +00:00
|
|
|
|
2016-12-14 22:16:46 +00:00
|
|
|
// Vet any changes based on the existing services's info.
|
2019-12-10 02:26:41 +00:00
|
|
|
if existing := a.State.Service(serviceID); existing != nil {
|
|
|
|
existing.FillAuthzContext(&authzContext)
|
2019-12-18 18:46:53 +00:00
|
|
|
if authz.ServiceWrite(existing.Service, &authzContext) != acl.Allow {
|
2022-02-11 20:53:23 +00:00
|
|
|
return acl.PermissionDeniedByACL(authz, &authzContext, acl.ResourceService, acl.AccessWrite, existing.Service)
|
2016-12-14 22:16:46 +00:00
|
|
|
}
|
|
|
|
} else {
|
2021-08-21 02:03:24 +00:00
|
|
|
// Take care if modifying this error message.
|
|
|
|
// agent/local/state.go's deleteService assumes the Catalog.Deregister RPC call
|
|
|
|
// will include "Unknown service"in the error if deregistration fails due to a
|
|
|
|
// service with that ID not existing.
|
|
|
|
return NotFoundError{Reason: fmt.Sprintf(
|
|
|
|
"Unknown service ID %q. Ensure that the service ID is passed, not the service name.",
|
|
|
|
serviceID)}
|
2016-12-14 22:16:46 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2019-12-18 18:46:53 +00:00
|
|
|
func (a *Agent) vetCheckRegisterWithAuthorizer(authz acl.Authorizer, check *structs.HealthCheck) error {
|
2019-12-18 18:43:24 +00:00
|
|
|
var authzContext acl.AuthorizerContext
|
2019-12-10 02:26:41 +00:00
|
|
|
check.FillAuthzContext(&authzContext)
|
2021-12-13 17:43:33 +00:00
|
|
|
|
2016-12-14 22:16:46 +00:00
|
|
|
// Vet the check itself.
|
|
|
|
if len(check.ServiceName) > 0 {
|
2019-12-18 18:46:53 +00:00
|
|
|
if authz.ServiceWrite(check.ServiceName, &authzContext) != acl.Allow {
|
2022-02-11 20:53:23 +00:00
|
|
|
return acl.PermissionDeniedByACL(authz, &authzContext, acl.ResourceService, acl.AccessWrite, check.ServiceName)
|
2016-12-14 22:16:46 +00:00
|
|
|
}
|
|
|
|
} else {
|
2022-02-11 20:53:23 +00:00
|
|
|
// N.B. Should this authzContext be derived from a.AgentEnterpriseMeta()
|
2019-12-18 18:46:53 +00:00
|
|
|
if authz.NodeWrite(a.config.NodeName, &authzContext) != acl.Allow {
|
2022-02-11 20:53:23 +00:00
|
|
|
return acl.PermissionDeniedByACL(authz, &authzContext, acl.ResourceNode, acl.AccessWrite, a.config.NodeName)
|
2016-12-14 22:16:46 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Vet any check that might be getting overwritten.
|
2019-12-10 02:26:41 +00:00
|
|
|
if existing := a.State.Check(check.CompoundCheckID()); existing != nil {
|
2016-12-14 22:16:46 +00:00
|
|
|
if len(existing.ServiceName) > 0 {
|
2022-02-11 20:53:23 +00:00
|
|
|
// N.B. Should this authzContext be derived from existing.EnterpriseMeta?
|
2019-12-18 18:46:53 +00:00
|
|
|
if authz.ServiceWrite(existing.ServiceName, &authzContext) != acl.Allow {
|
2022-02-11 20:53:23 +00:00
|
|
|
return acl.PermissionDeniedByACL(authz, &authzContext, acl.ResourceService, acl.AccessWrite, existing.ServiceName)
|
2016-12-14 22:16:46 +00:00
|
|
|
}
|
|
|
|
} else {
|
2022-02-11 20:53:23 +00:00
|
|
|
// N.B. Should this authzContext be derived from a.AgentEnterpriseMeta()
|
2019-12-18 18:46:53 +00:00
|
|
|
if authz.NodeWrite(a.config.NodeName, &authzContext) != acl.Allow {
|
2022-02-11 20:53:23 +00:00
|
|
|
return acl.PermissionDeniedByACL(authz, &authzContext, acl.ResourceNode, acl.AccessWrite, a.config.NodeName)
|
2016-12-14 22:16:46 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2019-12-18 18:46:53 +00:00
|
|
|
func (a *Agent) vetCheckUpdateWithAuthorizer(authz acl.Authorizer, checkID structs.CheckID) error {
|
2019-12-18 18:43:24 +00:00
|
|
|
var authzContext acl.AuthorizerContext
|
2019-12-10 02:26:41 +00:00
|
|
|
checkID.FillAuthzContext(&authzContext)
|
|
|
|
|
2016-12-14 22:16:46 +00:00
|
|
|
// Vet any changes based on the existing check's info.
|
2019-12-10 02:26:41 +00:00
|
|
|
if existing := a.State.Check(checkID); existing != nil {
|
2016-12-14 22:16:46 +00:00
|
|
|
if len(existing.ServiceName) > 0 {
|
2019-12-18 18:46:53 +00:00
|
|
|
if authz.ServiceWrite(existing.ServiceName, &authzContext) != acl.Allow {
|
2022-02-11 20:53:23 +00:00
|
|
|
return acl.PermissionDeniedByACL(authz, &authzContext, acl.ResourceService, acl.AccessWrite, existing.ServiceName)
|
2016-12-14 22:16:46 +00:00
|
|
|
}
|
|
|
|
} else {
|
2019-12-18 18:46:53 +00:00
|
|
|
if authz.NodeWrite(a.config.NodeName, &authzContext) != acl.Allow {
|
2022-02-11 20:53:23 +00:00
|
|
|
return acl.PermissionDeniedByACL(authz, &authzContext, acl.ResourceNode, acl.AccessWrite, a.config.NodeName)
|
2016-12-14 22:16:46 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
2022-01-06 20:38:37 +00:00
|
|
|
return NotFoundError{Reason: fmt.Sprintf(
|
|
|
|
"Unknown check ID %q. Ensure that the check ID is passed, not the check name.",
|
|
|
|
checkID.String())}
|
2016-12-14 22:16:46 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// filterMembers redacts members that the token doesn't have access to.
|
|
|
|
func (a *Agent) filterMembers(token string, members *[]serf.Member) error {
|
|
|
|
// Resolve the token and bail if ACLs aren't enabled.
|
2021-08-04 21:51:19 +00:00
|
|
|
authz, err := a.delegate.ResolveTokenAndDefaultMeta(token, nil, nil)
|
2016-12-14 22:16:46 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2019-12-18 18:43:24 +00:00
|
|
|
var authzContext acl.AuthorizerContext
|
2016-12-14 22:16:46 +00:00
|
|
|
// Filter out members based on the node policy.
|
|
|
|
m := *members
|
|
|
|
for i := 0; i < len(m); i++ {
|
|
|
|
node := m[i].Name
|
2021-10-13 14:18:16 +00:00
|
|
|
serfMemberFillAuthzContext(&m[i], &authzContext)
|
2021-08-04 21:51:19 +00:00
|
|
|
if authz.NodeRead(node, &authzContext) == acl.Allow {
|
2016-12-14 22:16:46 +00:00
|
|
|
continue
|
|
|
|
}
|
2022-01-26 22:21:45 +00:00
|
|
|
accessorID := authz.AccessorID()
|
2020-01-28 23:50:41 +00:00
|
|
|
a.logger.Debug("dropping node from result due to ACLs", "node", node, "accessorID", accessorID)
|
2016-12-14 22:16:46 +00:00
|
|
|
m = append(m[:i], m[i+1:]...)
|
|
|
|
i--
|
|
|
|
}
|
|
|
|
*members = m
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2021-12-03 20:36:28 +00:00
|
|
|
func (a *Agent) filterServicesWithAuthorizer(authz acl.Authorizer, services map[string]*api.AgentService) error {
|
2019-12-18 18:43:24 +00:00
|
|
|
var authzContext acl.AuthorizerContext
|
2016-12-14 22:16:46 +00:00
|
|
|
// Filter out services based on the service policy.
|
2021-12-03 20:36:28 +00:00
|
|
|
for id, service := range services {
|
|
|
|
agentServiceFillAuthzContext(service, &authzContext)
|
2019-12-18 18:46:53 +00:00
|
|
|
if authz.ServiceRead(service.Service, &authzContext) == acl.Allow {
|
2016-12-14 22:16:46 +00:00
|
|
|
continue
|
|
|
|
}
|
2021-12-03 20:36:28 +00:00
|
|
|
a.logger.Debug("dropping service from result due to ACLs", "service", id)
|
|
|
|
delete(services, id)
|
2016-12-14 22:16:46 +00:00
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2021-12-03 20:36:28 +00:00
|
|
|
func (a *Agent) filterChecksWithAuthorizer(authz acl.Authorizer, checks map[types.CheckID]*structs.HealthCheck) error {
|
2019-12-18 18:43:24 +00:00
|
|
|
var authzContext acl.AuthorizerContext
|
2016-12-14 22:16:46 +00:00
|
|
|
// Filter out checks based on the node or service policy.
|
2021-12-03 20:36:28 +00:00
|
|
|
for id, check := range checks {
|
2021-08-25 18:43:11 +00:00
|
|
|
check.FillAuthzContext(&authzContext)
|
2016-12-14 22:16:46 +00:00
|
|
|
if len(check.ServiceName) > 0 {
|
2019-12-18 18:46:53 +00:00
|
|
|
if authz.ServiceRead(check.ServiceName, &authzContext) == acl.Allow {
|
2016-12-14 22:16:46 +00:00
|
|
|
continue
|
|
|
|
}
|
|
|
|
} else {
|
2019-12-18 18:46:53 +00:00
|
|
|
if authz.NodeRead(a.config.NodeName, &authzContext) == acl.Allow {
|
2016-12-14 22:16:46 +00:00
|
|
|
continue
|
|
|
|
}
|
|
|
|
}
|
2021-12-03 20:36:28 +00:00
|
|
|
a.logger.Debug("dropping check from result due to ACLs", "check", id)
|
|
|
|
delete(checks, id)
|
2016-12-14 22:16:46 +00:00
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|