github.com/uchennaokeke444/nomad@v0.11.8/nomad/consul_policy.go (about) 1 package nomad 2 3 import ( 4 "strings" 5 6 "github.com/hashicorp/consul/api" 7 "github.com/hashicorp/hcl" 8 "github.com/pkg/errors" 9 ) 10 11 // ConsulServiceRule represents a policy for a service. 12 type ConsulServiceRule struct { 13 Name string `hcl:",key"` 14 Policy string 15 } 16 17 // ConsulPolicy represents the parts of a ConsulServiceRule Policy that are 18 // relevant to Service Identity authorizations. 19 type ConsulPolicy struct { 20 Services []*ConsulServiceRule `hcl:"service,expand"` 21 ServicePrefixes []*ConsulServiceRule `hcl:"service_prefix,expand"` 22 } 23 24 // IsEmpty returns true if there are no Services or ServicePrefixes defined for 25 // the ConsulPolicy. 26 func (cp *ConsulPolicy) IsEmpty() bool { 27 if cp == nil { 28 return true 29 } 30 return len(cp.Services) == 0 && len(cp.ServicePrefixes) == 0 31 } 32 33 // ParseConsulPolicy parses raw string s into a ConsulPolicy. An error is 34 // returned if decoding the policy fails, or if the decoded policy has no 35 // Services or ServicePrefixes defined. 36 func ParseConsulPolicy(s string) (*ConsulPolicy, error) { 37 cp := new(ConsulPolicy) 38 if err := hcl.Decode(cp, s); err != nil { 39 return nil, errors.Wrap(err, "failed to parse ACL policy") 40 } 41 if cp.IsEmpty() { 42 // the only use case for now, may as well validate asap 43 return nil, errors.New("consul policy contains no service rules") 44 } 45 return cp, nil 46 } 47 48 func (c *consulACLsAPI) hasSufficientPolicy(task string, token *api.ACLToken) (bool, error) { 49 // check each policy directly attached to the token 50 for _, policyRef := range token.Policies { 51 if allowable, err := c.policyAllowsServiceWrite(task, policyRef.ID); err != nil { 52 return false, err 53 } else if allowable { 54 return true, nil 55 } 56 } 57 58 // check each policy on each role attached to the token 59 for _, roleLink := range token.Roles { 60 role, _, err := c.aclClient.RoleRead(roleLink.ID, &api.QueryOptions{ 61 AllowStale: false, 62 }) 63 if err != nil { 64 return false, err 65 } 66 67 for _, policyLink := range role.Policies { 68 allowable, err := c.policyAllowsServiceWrite(task, policyLink.ID) 69 if err != nil { 70 return false, err 71 } 72 if allowable { 73 return true, nil 74 } 75 } 76 } 77 78 return false, nil 79 } 80 81 func (c *consulACLsAPI) policyAllowsServiceWrite(task string, policyID string) (bool, error) { 82 policy, _, err := c.aclClient.PolicyRead(policyID, &api.QueryOptions{ 83 AllowStale: false, 84 }) 85 if err != nil { 86 return false, err 87 } 88 89 // compare policy to the necessary permission for service write 90 // e.g. service "db" { policy = "write" } 91 // e.g. service_prefix "" { policy == "write" } 92 cp, err := ParseConsulPolicy(policy.Rules) 93 if err != nil { 94 return false, err 95 } 96 97 if cp.allowsServiceWrite(task) { 98 return true, nil 99 } 100 101 return false, nil 102 } 103 104 const ( 105 serviceNameWildcard = "*" 106 ) 107 108 func (cp *ConsulPolicy) allowsServiceWrite(task string) bool { 109 for _, service := range cp.Services { 110 name := strings.ToLower(service.Name) 111 policy := strings.ToLower(service.Policy) 112 if policy == ConsulPolicyWrite { 113 if name == task || name == serviceNameWildcard { 114 return true 115 } 116 } 117 } 118 119 for _, servicePrefix := range cp.ServicePrefixes { 120 prefix := strings.ToLower(servicePrefix.Name) 121 policy := strings.ToLower(servicePrefix.Policy) 122 if policy == ConsulPolicyWrite { 123 if strings.HasPrefix(task, prefix) { 124 return true 125 } 126 } 127 } 128 return false 129 }