»Intentions

Intentions define access control for services via Connect and are used to control which services may establish connections or make requests. Intentions can be managed via the API, CLI, or UI.

Intentions are enforced on inbound connections or requests by the proxy or within a natively integrated application.

Depending upon the protocol in use by the destination service, you can define intentions to control Connect traffic authorization either at networking layer 4 (e.g. TCP) and networking layer 7 (e.g. HTTP):

  • Identity-based - All intentions may enforce access based on identities encoded within TLS certificates. This allows for coarse all-or-nothing access control between pairs of services. These work with for services with any protocol as they only require awareness of the TLS handshake that wraps the opaque TCP connection. These can also be thought of as L4 intentions.

  • Application-aware - Some intentions may additionally enforce access based on L7 request attributes in addition to connection identity. These may only be defined for services with a protocol that is HTTP-based. These can also be thought of as L7 intentions.

At any given point in time, between any pair of services only one intention controls authorization. This may be either an L4 intention or an L7 intention, but at any given point in time only one of those applies.

The intention match API should be periodically called to retrieve all relevant intentions for the target destination. After verifying the TLS client certificate, the cached intentions should be consulted for each incoming connection/request to determine if it should be accepted or rejected.

The default intention behavior is defined by the default ACL policy. If the default ACL policy is "allow all", then all Connect connections are allowed by default. If the default ACL policy is "deny all", then all Connect connections or requests are denied by default.

»Intention Basics

Intentions are managed primarily via service-intentions config entries or the UI. Some simpler tasks can also be achieved with the older API or CLI. Please see the respective documentation for each for full details on options, flags, etc.

Below is an example of a basic service-intentions config entry representing two simple intentions. The full data model complete with more examples can be found in the service-intentions config entry documentation.

Kind = "service-intentions"
Name = "db"
Sources = [
  {
    Name   = "web"
    Action = "deny"
  },
  {
    Name   = "api"
    Action = "allow"
  }
]

This config entry defines two intentions with a common destination of "db". The first intention above is a deny intention with a source of "web". This says that connections from web to db are not allowed and the connection will be rejected. The second intention is an allow intention with a source of "api". This says that connections from api to db are allowed and connections will be accepted.

»Wildcard Intentions

An intention source or destination may also be the special wildcard value *. This matches any value and is used as a catch-all.

This example says that the "web" service cannot connect to any service:

Kind = "service-intentions"
Name = "*"
Sources = [
  {
    Name   = "web"
    Action = "deny"
  }
]

And this example says that no service can connect to the "db" service:

Kind = "service-intentions"
Name = "db"
Sources = [
  {
    Name   = "*"
    Action = "deny"
  }
]

»Enforcement

For services that define their protocol as TCP, intentions mediate the ability to establish new connections. When an intention is modified, existing connections will not be affected. This means that changing a connection from "allow" to "deny" today will not kill the connection.

For services that define their protocol as HTTP-based, intentions mediate the ability to issue new requests.

When an intention is modified, requests received after the modification will use the latest intention rules to enforce access. This means that though changing a connection from "allow" to "deny" today will not kill the connection, it will correctly block new requests from being processed.

»Precedence and Match Order

Intentions are matched in an implicit order based on specificity, preferring deny over allow. Specificity is determined by whether a value is an exact specified value or is the wildcard value *. The full precedence table is shown below and is evaluated top to bottom, with larger numbers being evaluated first.

Source NamespaceSource NameDestination NamespaceDestination NamePrecedence
ExactExactExactExact9
Exact*ExactExact8
**ExactExact7
ExactExactExact*6
Exact*Exact*5
**Exact*4
ExactExact**3
Exact***2
****1

The precedence value can be read from a field on the service-intentions config entry after it is modified. Precedence cannot be manually overridden today.

The numbers in the table above are not stable. Their ordering will remain fixed but the actual number values may change in the future.

»Intention Management Permissions

Intention management can be protected by ACLs. Permissions for intentions are destination-oriented, meaning the ACLs for managing intentions are looked up based on the destination value of the intention, not the source.

Intention permissions are by default implicitly granted at read level when granting service:read or service:write. This is because a service registered that wants to use Connect needs intentions:read for its own service name in order to know whether or not to authorize connections. The following ACL policy will implicitly grant intentions:read (note read) for service web.

service "web" {
  policy = "write"
}

It is possible to explicitly specify intention permissions. For example, the following policy will allow a service to be discovered without granting access to read intentions for it.

service "web" {
  policy = "read"
  intentions = "deny"
}

Note that intentions:read is required for a token that a Connect-enabled service uses to register itself or its proxy. If the token used does not have intentions:read then the agent will be unable to resolve intentions for the service and so will not be able to authorize any incoming connections.

»Performance and Intention Updates

The intentions for services registered with a Consul agent are cached locally on that agent. They are then updated via a background blocking query against the Consul servers.

Supported proxies (such as Envoy) also cache this data within their own configuration so that inbound connections or requests require no Consul agent involvement during authorization. All actions in the data path of connections happen within the proxy.

Updates to intentions are propagated nearly instantly to agents since agents maintain a continuous blocking query in the background for intention updates for registered services. Proxies similarly use blocking queries to update their local configurations quickly.

Because all the intention data is cached locally, the agents or proxies can fail static. Even if the agents are severed completely from the Consul servers, or the proxies are severed completely from their local Consul agent, inbound connection authorization continues to work indefinitely. Changes to intentions will not be picked up until the partition heals, but will then automatically take effect when connectivity is restored.