138 lines
4.1 KiB
Go
138 lines
4.1 KiB
Go
package consul
|
|
|
|
import (
|
|
"fmt"
|
|
|
|
"github.com/hashicorp/go-bexpr"
|
|
|
|
"github.com/hashicorp/consul/acl"
|
|
"github.com/hashicorp/consul/agent/consul/authmethod"
|
|
"github.com/hashicorp/consul/agent/structs"
|
|
|
|
// register these as a builtin auth method
|
|
_ "github.com/hashicorp/consul/agent/consul/authmethod/awsauth"
|
|
_ "github.com/hashicorp/consul/agent/consul/authmethod/kubeauth"
|
|
_ "github.com/hashicorp/consul/agent/consul/authmethod/ssoauth"
|
|
)
|
|
|
|
type authMethodValidatorEntry struct {
|
|
Validator authmethod.Validator
|
|
ModifyIndex uint64 // the raft index when this last changed
|
|
}
|
|
|
|
// loadAuthMethodValidator returns an authmethod.Validator for the given auth
|
|
// method configuration. If the cache is up to date as-of the provided index
|
|
// then the cached version is returned, otherwise a new validator is created
|
|
// and cached.
|
|
func (s *Server) loadAuthMethodValidator(idx uint64, method *structs.ACLAuthMethod) (authmethod.Validator, error) {
|
|
if prevIdx, v, ok := s.aclAuthMethodValidators.GetValidator(method); ok && idx <= prevIdx {
|
|
return v, nil
|
|
}
|
|
|
|
v, err := authmethod.NewValidator(s.logger, method)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("auth method validator for %q could not be initialized: %v", method.Name, err)
|
|
}
|
|
|
|
v = s.aclAuthMethodValidators.PutValidatorIfNewer(method, v, idx)
|
|
|
|
return v, nil
|
|
}
|
|
|
|
type aclBindings struct {
|
|
roles []structs.ACLTokenRoleLink
|
|
serviceIdentities []*structs.ACLServiceIdentity
|
|
nodeIdentities []*structs.ACLNodeIdentity
|
|
}
|
|
|
|
// evaluateRoleBindings evaluates all current binding rules associated with the
|
|
// given auth method against the verified data returned from the authentication
|
|
// process.
|
|
//
|
|
// A list of role links and service identities are returned.
|
|
func (s *Server) evaluateRoleBindings(
|
|
validator authmethod.Validator,
|
|
verifiedIdentity *authmethod.Identity,
|
|
methodMeta *acl.EnterpriseMeta,
|
|
targetMeta *acl.EnterpriseMeta,
|
|
) (*aclBindings, error) {
|
|
// Only fetch rules that are relevant for this method.
|
|
_, rules, err := s.fsm.State().ACLBindingRuleList(nil, validator.Name(), methodMeta)
|
|
if err != nil {
|
|
return nil, err
|
|
} else if len(rules) == 0 {
|
|
return nil, nil
|
|
}
|
|
|
|
// Find all binding rules that match the provided fields.
|
|
var matchingRules []*structs.ACLBindingRule
|
|
for _, rule := range rules {
|
|
if doesSelectorMatch(rule.Selector, verifiedIdentity.SelectableFields) {
|
|
matchingRules = append(matchingRules, rule)
|
|
}
|
|
}
|
|
if len(matchingRules) == 0 {
|
|
return nil, nil
|
|
}
|
|
|
|
// For all matching rules compute the attributes of a token.
|
|
var bindings aclBindings
|
|
for _, rule := range matchingRules {
|
|
bindName, valid, err := computeBindingRuleBindName(rule.BindType, rule.BindName, verifiedIdentity.ProjectedVars)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("cannot compute %q bind name for bind target: %v", rule.BindType, err)
|
|
} else if !valid {
|
|
return nil, fmt.Errorf("computed %q bind name for bind target is invalid: %q", rule.BindType, bindName)
|
|
}
|
|
|
|
switch rule.BindType {
|
|
case structs.BindingRuleBindTypeService:
|
|
bindings.serviceIdentities = append(bindings.serviceIdentities, &structs.ACLServiceIdentity{
|
|
ServiceName: bindName,
|
|
})
|
|
|
|
case structs.BindingRuleBindTypeNode:
|
|
bindings.nodeIdentities = append(bindings.nodeIdentities, &structs.ACLNodeIdentity{
|
|
NodeName: bindName,
|
|
Datacenter: s.config.Datacenter,
|
|
})
|
|
|
|
case structs.BindingRuleBindTypeRole:
|
|
_, role, err := s.fsm.State().ACLRoleGetByName(nil, bindName, targetMeta)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if role != nil {
|
|
bindings.roles = append(bindings.roles, structs.ACLTokenRoleLink{
|
|
ID: role.ID,
|
|
})
|
|
}
|
|
|
|
default:
|
|
// skip unknown bind type; don't grant privileges
|
|
}
|
|
}
|
|
|
|
return &bindings, nil
|
|
}
|
|
|
|
// doesSelectorMatch checks that a single selector matches the provided vars.
|
|
func doesSelectorMatch(selector string, selectableVars interface{}) bool {
|
|
if selector == "" {
|
|
return true // catch-all
|
|
}
|
|
|
|
eval, err := bexpr.CreateEvaluatorForType(selector, nil, selectableVars)
|
|
if err != nil {
|
|
return false // fails to match if selector is invalid
|
|
}
|
|
|
|
result, err := eval.Evaluate(selectableVars)
|
|
if err != nil {
|
|
return false // fails to match if evaluation fails
|
|
}
|
|
|
|
return result
|
|
}
|