Consul service intentions are the primary mechanism for controlling L7 traffic flow between services within a Consul-managed environment. They operate by defining explicit permissions, ensuring that only authorized services can communicate with each other, and critically, they do this without modifying application code.
Let’s see this in action. Imagine we have two services: frontend and backend. By default, frontend might not be able to talk to backend. We’ll use the Consul API to change this.
First, let’s check the current state. If frontend tries to call backend now, it will likely fail. We can see this failure in the logs of the frontend service or, if using a service mesh like Consul Connect, the Envoy sidecar proxy logs on the frontend pod.
# Example: Checking intention status via Consul API (replace with your Consul API endpoint)
curl "http://localhost:8500/v1/acl/tokens/lookup" -H "X-Consul-Token: your_consul_token" | jq '.Policies[].Name'
# To create an intention:
curl \
--request PUT \
--data '{"source_name": "frontend", "destination_name": "backend", "action": "allow"}' \
http://localhost:8500/v1/config/service-intentions/config/frontend-to-backend
The above curl command creates a new configuration entry in Consul. The path /v1/config/service-intentions/config/frontend-to-backend is a convention; Consul uses key-value pairs under service-intentions/config/ to manage intentions. The payload {"source_name": "frontend", "destination_name": "backend", "action": "allow"} explicitly grants permission for any service registered as frontend to communicate with any service registered as backend.
Once this intention is applied, the frontend service should be able to successfully communicate with the backend service. This is because the service mesh (if used) or Consul’s agent will enforce this policy at the network level, typically by configuring the Envoy sidecar proxies associated with each service. The proxies intercept traffic, check against the defined intentions, and either allow or deny the request.
The core problem Consul service intentions solve is the inherent insecurity of service-to-service communication in distributed systems. Without explicit controls, any service could potentially reach any other service, leading to a wide attack surface. Intentions provide a zero-trust model where communication is denied by default and only explicitly allowed.
Internally, Consul stores these intentions as configuration entries. When a service mesh is active, Consul agents (or the control plane) watch for changes in these intention configurations. Upon detecting a new or modified intention, they program the data plane proxies (like Envoy) to enforce the new policy. This programming involves updating routing rules and access control lists within the proxies.
The exact levers you control are the source_name, destination_name, and action. source_name and destination_name map to the Name field in a Consul service registration. The action can be "allow" or "deny". You can also specify L7 attributes for more granular control. For example, to only allow GET requests to /api/users on the backend service from frontend:
{
"source_name": "frontend",
"destination_name": "backend",
"action": "allow",
"l7_rules": [
{
"match": [
{
"http": {
"method": "GET",
"path": {
"regex": "^/api/users$"
}
}
}
]
}
]
}
This L7 rule is applied to the intention configuration in Consul. The Envoy proxies, when programmed by Consul, will inspect the HTTP method and path of incoming requests. If the request from frontend to backend matches these criteria (GET to /api/users), it will be allowed; otherwise, it will be denied, even if a general allow intention exists.
A surprising aspect of Consul intentions is their ability to manage traffic at a granular L7 level without requiring any code changes in the actual services being connected. This means you can enforce complex routing and security policies purely through configuration, drastically reducing the cognitive load on development teams and improving the security posture of your applications. The proxies handle all the inspection and enforcement, acting as a transparent security layer.
The next step after mastering service intentions is understanding how to leverage Consul’s catalog and health checking to dynamically manage which service instances are available to receive traffic, further refining your L7 traffic control.