github.com/judwhite/consul@v1.4.4-0.20190315202039-6ef970a191d3/agent/connect_auth.go (about) 1 package agent 2 3 import ( 4 "fmt" 5 6 "github.com/hashicorp/consul/acl" 7 "github.com/hashicorp/consul/agent/cache" 8 cachetype "github.com/hashicorp/consul/agent/cache-types" 9 "github.com/hashicorp/consul/agent/connect" 10 "github.com/hashicorp/consul/agent/structs" 11 ) 12 13 // ConnectAuthorize implements the core authorization logic for Connect. It's in 14 // a separate agent method here because we need to re-use this both in our own 15 // HTTP API authz endpoint and in the gRPX xDS/ext_authz API for envoy. 16 // 17 // The ACL token and the auth request are provided and the auth decision (true 18 // means authorized) and reason string are returned. 19 // 20 // If the request input is invalid the error returned will be a BadRequestError, 21 // if the token doesn't grant necessary access then an acl.ErrPermissionDenied 22 // error is returned, otherwise error indicates an unexpected server failure. If 23 // access is denied, no error is returned but the first return value is false. 24 func (a *Agent) ConnectAuthorize(token string, 25 req *structs.ConnectAuthorizeRequest) (authz bool, reason string, m *cache.ResultMeta, err error) { 26 27 // Helper to make the error cases read better without resorting to named 28 // returns which get messy and prone to mistakes in a method this long. 29 returnErr := func(err error) (bool, string, *cache.ResultMeta, error) { 30 return false, "", nil, err 31 } 32 33 if req == nil { 34 return returnErr(BadRequestError{"Invalid request"}) 35 } 36 37 // We need to have a target to check intentions 38 if req.Target == "" { 39 return returnErr(BadRequestError{"Target service must be specified"}) 40 } 41 42 // Parse the certificate URI from the client ID 43 uri, err := connect.ParseCertURIFromString(req.ClientCertURI) 44 if err != nil { 45 return returnErr(BadRequestError{"ClientCertURI not a valid Connect identifier"}) 46 } 47 48 uriService, ok := uri.(*connect.SpiffeIDService) 49 if !ok { 50 return returnErr(BadRequestError{"ClientCertURI not a valid Service identifier"}) 51 } 52 53 // We need to verify service:write permissions for the given token. 54 // We do this manually here since the RPC request below only verifies 55 // service:read. 56 rule, err := a.resolveToken(token) 57 if err != nil { 58 return returnErr(err) 59 } 60 if rule != nil && !rule.ServiceWrite(req.Target, nil) { 61 return returnErr(acl.ErrPermissionDenied) 62 } 63 64 // Note that we DON'T explicitly validate the trust-domain matches ours. See 65 // the PR for this change for details. 66 67 // TODO(banks): Implement revocation list checking here. 68 69 // Get the intentions for this target service. 70 args := &structs.IntentionQueryRequest{ 71 Datacenter: a.config.Datacenter, 72 Match: &structs.IntentionQueryMatch{ 73 Type: structs.IntentionMatchDestination, 74 Entries: []structs.IntentionMatchEntry{ 75 { 76 Namespace: structs.IntentionDefaultNamespace, 77 Name: req.Target, 78 }, 79 }, 80 }, 81 QueryOptions: structs.QueryOptions{Token: token}, 82 } 83 84 raw, meta, err := a.cache.Get(cachetype.IntentionMatchName, args) 85 if err != nil { 86 return returnErr(err) 87 } 88 89 reply, ok := raw.(*structs.IndexedIntentionMatches) 90 if !ok { 91 return returnErr(fmt.Errorf("internal error: response type not correct")) 92 } 93 if len(reply.Matches) != 1 { 94 return returnErr(fmt.Errorf("Internal error loading matches")) 95 } 96 97 // Test the authorization for each match 98 for _, ixn := range reply.Matches[0] { 99 if auth, ok := uriService.Authorize(ixn); ok { 100 reason = fmt.Sprintf("Matched intention: %s", ixn.String()) 101 return auth, reason, &meta, nil 102 } 103 } 104 105 // No match, we need to determine the default behavior. We do this by 106 // specifying the anonymous token, which will get the default behavior. The 107 // default behavior if ACLs are disabled is to allow connections to mimic the 108 // behavior of Consul itself: everything is allowed if ACLs are disabled. 109 rule, err = a.resolveToken("") 110 if err != nil { 111 return returnErr(err) 112 } 113 if rule == nil { 114 // ACLs not enabled at all, the default is allow all. 115 return true, "ACLs disabled, access is allowed by default", &meta, nil 116 } 117 reason = "Default behavior configured by ACLs" 118 return rule.IntentionDefaultAllow(), reason, &meta, nil 119 }