open-consul/acl/policy_merger.go

365 lines
9.1 KiB
Go

package acl
import (
"encoding/binary"
"fmt"
"hash"
"golang.org/x/crypto/blake2b"
)
type policyRulesMergeContext struct {
aclRule string
agentRules map[string]*AgentRule
agentPrefixRules map[string]*AgentRule
eventRules map[string]*EventRule
eventPrefixRules map[string]*EventRule
keyringRule string
keyRules map[string]*KeyRule
keyPrefixRules map[string]*KeyRule
nodeRules map[string]*NodeRule
nodePrefixRules map[string]*NodeRule
operatorRule string
preparedQueryRules map[string]*PreparedQueryRule
preparedQueryPrefixRules map[string]*PreparedQueryRule
serviceRules map[string]*ServiceRule
servicePrefixRules map[string]*ServiceRule
sessionRules map[string]*SessionRule
sessionPrefixRules map[string]*SessionRule
}
func (p *policyRulesMergeContext) init() {
p.aclRule = ""
p.agentRules = make(map[string]*AgentRule)
p.agentPrefixRules = make(map[string]*AgentRule)
p.eventRules = make(map[string]*EventRule)
p.eventPrefixRules = make(map[string]*EventRule)
p.keyringRule = ""
p.keyRules = make(map[string]*KeyRule)
p.keyPrefixRules = make(map[string]*KeyRule)
p.nodeRules = make(map[string]*NodeRule)
p.nodePrefixRules = make(map[string]*NodeRule)
p.operatorRule = ""
p.preparedQueryRules = make(map[string]*PreparedQueryRule)
p.preparedQueryPrefixRules = make(map[string]*PreparedQueryRule)
p.serviceRules = make(map[string]*ServiceRule)
p.servicePrefixRules = make(map[string]*ServiceRule)
p.sessionRules = make(map[string]*SessionRule)
p.sessionPrefixRules = make(map[string]*SessionRule)
}
func (p *policyRulesMergeContext) merge(policy *PolicyRules) {
if takesPrecedenceOver(policy.ACL, p.aclRule) {
p.aclRule = policy.ACL
}
for _, ap := range policy.Agents {
update := true
if permission, found := p.agentRules[ap.Node]; found {
update = takesPrecedenceOver(ap.Policy, permission.Policy)
}
if update {
p.agentRules[ap.Node] = ap
}
}
for _, ap := range policy.AgentPrefixes {
update := true
if permission, found := p.agentPrefixRules[ap.Node]; found {
update = takesPrecedenceOver(ap.Policy, permission.Policy)
}
if update {
p.agentPrefixRules[ap.Node] = ap
}
}
for _, ep := range policy.Events {
update := true
if permission, found := p.eventRules[ep.Event]; found {
update = takesPrecedenceOver(ep.Policy, permission.Policy)
}
if update {
p.eventRules[ep.Event] = ep
}
}
for _, ep := range policy.EventPrefixes {
update := true
if permission, found := p.eventPrefixRules[ep.Event]; found {
update = takesPrecedenceOver(ep.Policy, permission.Policy)
}
if update {
p.eventPrefixRules[ep.Event] = ep
}
}
if takesPrecedenceOver(policy.Keyring, p.keyringRule) {
p.keyringRule = policy.Keyring
}
for _, kp := range policy.Keys {
update := true
if permission, found := p.keyRules[kp.Prefix]; found {
update = takesPrecedenceOver(kp.Policy, permission.Policy)
}
if update {
p.keyRules[kp.Prefix] = kp
}
}
for _, kp := range policy.KeyPrefixes {
update := true
if permission, found := p.keyPrefixRules[kp.Prefix]; found {
update = takesPrecedenceOver(kp.Policy, permission.Policy)
}
if update {
p.keyPrefixRules[kp.Prefix] = kp
}
}
for _, np := range policy.Nodes {
update := true
if permission, found := p.nodeRules[np.Name]; found {
update = takesPrecedenceOver(np.Policy, permission.Policy)
}
if update {
p.nodeRules[np.Name] = np
}
}
for _, np := range policy.NodePrefixes {
update := true
if permission, found := p.nodePrefixRules[np.Name]; found {
update = takesPrecedenceOver(np.Policy, permission.Policy)
}
if update {
p.nodePrefixRules[np.Name] = np
}
}
if takesPrecedenceOver(policy.Operator, p.operatorRule) {
p.operatorRule = policy.Operator
}
for _, qp := range policy.PreparedQueries {
update := true
if permission, found := p.preparedQueryRules[qp.Prefix]; found {
update = takesPrecedenceOver(qp.Policy, permission.Policy)
}
if update {
p.preparedQueryRules[qp.Prefix] = qp
}
}
for _, qp := range policy.PreparedQueryPrefixes {
update := true
if permission, found := p.preparedQueryPrefixRules[qp.Prefix]; found {
update = takesPrecedenceOver(qp.Policy, permission.Policy)
}
if update {
p.preparedQueryPrefixRules[qp.Prefix] = qp
}
}
for _, sp := range policy.Services {
existing, found := p.serviceRules[sp.Name]
if !found {
p.serviceRules[sp.Name] = sp
continue
}
if takesPrecedenceOver(sp.Policy, existing.Policy) {
existing.Policy = sp.Policy
existing.EnterpriseRule = sp.EnterpriseRule
}
if takesPrecedenceOver(sp.Intentions, existing.Intentions) {
existing.Intentions = sp.Intentions
}
}
for _, sp := range policy.ServicePrefixes {
existing, found := p.servicePrefixRules[sp.Name]
if !found {
p.servicePrefixRules[sp.Name] = sp
continue
}
if takesPrecedenceOver(sp.Policy, existing.Policy) {
existing.Policy = sp.Policy
existing.EnterpriseRule = sp.EnterpriseRule
}
if takesPrecedenceOver(sp.Intentions, existing.Intentions) {
existing.Intentions = sp.Intentions
}
}
for _, sp := range policy.Sessions {
update := true
if permission, found := p.sessionRules[sp.Node]; found {
update = takesPrecedenceOver(sp.Policy, permission.Policy)
}
if update {
p.sessionRules[sp.Node] = sp
}
}
for _, sp := range policy.SessionPrefixes {
update := true
if permission, found := p.sessionPrefixRules[sp.Node]; found {
update = takesPrecedenceOver(sp.Policy, permission.Policy)
}
if update {
p.sessionPrefixRules[sp.Node] = sp
}
}
}
func (p *policyRulesMergeContext) update(merged *PolicyRules) {
merged.ACL = p.aclRule
merged.Keyring = p.keyringRule
merged.Operator = p.operatorRule
// All the for loop appends are ugly but Go doesn't have a way to get
// a slice of all values within a map so this is necessary
merged.Agents = []*AgentRule{}
for _, policy := range p.agentRules {
merged.Agents = append(merged.Agents, policy)
}
merged.AgentPrefixes = []*AgentRule{}
for _, policy := range p.agentPrefixRules {
merged.AgentPrefixes = append(merged.AgentPrefixes, policy)
}
merged.Events = []*EventRule{}
for _, policy := range p.eventRules {
merged.Events = append(merged.Events, policy)
}
merged.EventPrefixes = []*EventRule{}
for _, policy := range p.eventPrefixRules {
merged.EventPrefixes = append(merged.EventPrefixes, policy)
}
merged.Keys = []*KeyRule{}
for _, policy := range p.keyRules {
merged.Keys = append(merged.Keys, policy)
}
merged.KeyPrefixes = []*KeyRule{}
for _, policy := range p.keyPrefixRules {
merged.KeyPrefixes = append(merged.KeyPrefixes, policy)
}
merged.Nodes = []*NodeRule{}
for _, policy := range p.nodeRules {
merged.Nodes = append(merged.Nodes, policy)
}
merged.NodePrefixes = []*NodeRule{}
for _, policy := range p.nodePrefixRules {
merged.NodePrefixes = append(merged.NodePrefixes, policy)
}
merged.PreparedQueries = []*PreparedQueryRule{}
for _, policy := range p.preparedQueryRules {
merged.PreparedQueries = append(merged.PreparedQueries, policy)
}
merged.PreparedQueryPrefixes = []*PreparedQueryRule{}
for _, policy := range p.preparedQueryPrefixRules {
merged.PreparedQueryPrefixes = append(merged.PreparedQueryPrefixes, policy)
}
merged.Services = []*ServiceRule{}
for _, policy := range p.serviceRules {
merged.Services = append(merged.Services, policy)
}
merged.ServicePrefixes = []*ServiceRule{}
for _, policy := range p.servicePrefixRules {
merged.ServicePrefixes = append(merged.ServicePrefixes, policy)
}
merged.Sessions = []*SessionRule{}
for _, policy := range p.sessionRules {
merged.Sessions = append(merged.Sessions, policy)
}
merged.SessionPrefixes = []*SessionRule{}
for _, policy := range p.sessionPrefixRules {
merged.SessionPrefixes = append(merged.SessionPrefixes, policy)
}
}
type PolicyMerger struct {
idHasher hash.Hash
policyRulesMergeContext
enterprisePolicyRulesMergeContext
}
func NewPolicyMerger() *PolicyMerger {
merger := &PolicyMerger{}
merger.init()
return merger
}
func (m *PolicyMerger) init() {
var err error
m.idHasher, err = blake2b.New256(nil)
if err != nil {
panic(err)
}
m.policyRulesMergeContext.init()
m.enterprisePolicyRulesMergeContext.init()
}
func (m *PolicyMerger) Merge(policy *Policy) {
// This is part of calculating the merged policies ID
m.idHasher.Write([]byte(policy.ID))
binary.Write(m.idHasher, binary.BigEndian, policy.Revision)
m.policyRulesMergeContext.merge(&policy.PolicyRules)
m.enterprisePolicyRulesMergeContext.merge(&policy.EnterprisePolicyRules)
}
// Policy outputs the merged policy
func (m *PolicyMerger) Policy() *Policy {
merged := &Policy{
ID: fmt.Sprintf("%x", m.idHasher.Sum(nil)),
}
m.policyRulesMergeContext.update(&merged.PolicyRules)
m.enterprisePolicyRulesMergeContext.update(&merged.EnterprisePolicyRules)
return merged
}
func MergePolicies(policies []*Policy) *Policy {
var merger PolicyMerger
merger.init()
for _, p := range policies {
merger.Merge(p)
}
return merger.Policy()
}