Using Consul Connect Intentions to Enforce Zero‑Trust Between Microservices
Learn how Consul Connect intentions let you enforce service‑to‑service allow/deny rules with zero‑trust mTLS, illustrated by a concrete deny‑rule example and its verification steps.
20 Aug 2025, 13:22 UTC

The problem: unintended service‑to‑service traffic
In a growing micro‑service landscape, developers often rely on network‑level firewalls or ad‑hoc routing rules to keep services isolated. As the number of services rises, these manual controls become error‑prone and difficult to audit. A missed rule can let a compromised service talk to a sensitive backend, creating a lateral‑movement path that is hard to detect.
Thesis: Declarative intentions in Consul Connect give you a version‑controlled, cluster‑wide way to allow or deny service‑to‑service traffic without touching application code.
How Connect and intentions work together
Consul Connect issues each service a short‑lived X.509 identity (SPIFFE format) via its built‑in CA or an external Vault PKI. Sidecar proxies (Envoy) terminate mTLS using these certificates, so the application sees plaintext traffic on localhost. Intentions are a separate policy layer that the sidecar consults at connection time: they evaluate the source and destination service identities and return allow or deny. Because intentions are stored in Consul’s Raft store, they are replicated to every agent and updated in real time.
Deploying the sidecar and creating an intention
Assume you have a Consul cluster running (version 1.12 or later) and two services registered: web and api. You will need an ACL token with the intentions:write capability to create or modify intentions.
- Start the sidecar for each service (run on the host where the service process lives, or inject via a sidecar container in Kubernetes).
# On the host running the web service consul connect envoy -sidecar-for web # On the host running the api service consul connect envoy -sidecar-for api - Create a deny intention that blocks traffic from
webtoapi.# Run on any Consul client with a token that has intentions:write consul intention create -deny web api
Worked example: verifying the deny rule
With the sidecars running and the intention in place, the web sidecar will intercept any outbound connection to api and, according to the policy, return an error to the application.
# From inside the web sidecar’s network namespace (e.g., exec into the sidecar container)
curl -v http://localhost:9191/api/health
You should observe a response status of 503 Service Unavailable and a response header similar to x-envoy-upstream-rq-timeout (Envoy’s way of signaling a blocked connection). The application logs will show a connection failure, but the sidecar logs will contain an entry like:
[2026-10-18T12:45:00Z] "GET /api/health HTTP/1.1" 503 UC - "-" "curl/7.88.0" 0 0 0 0 "-" "-"
To confirm the intention is active, you can query Consul:
consul intention list
The output will show a row with Action = deny, Source = web, Destination = api.
Trade‑offs and limitations
- Intentions are evaluated only at connection establishment. Long‑lived connections such as gRPC streams or WebSockets will not pick up a new deny rule until the connection is torn down and re‑established. For protocols that keep connections open for minutes or hours, you may need to force a restart or rely on client‑side idle timeouts.
- Managing a large number of fine‑grained intentions can become operationally heavy. Consider grouping services using Consul namespaces or partitions and writing broader rules with wildcards when appropriate.
- The sidecar adds a small latency overhead (typically 1‑2 ms p99 per hop). In latency‑critical paths, measure the impact with a tool like
heyorwrkbefore rolling out to production.
Actionable closing
Start by applying a single deny intention to a non‑critical service pair, observe the 503 response, and then extend the pattern to sensitive boundaries such as payment or data‑store services. Keep your intentions under version control (e.g., store the output of consul intention list -format=json in Git) and review them regularly alongside your ACL tokens. This gives you a reproducible, auditable zero‑trust network layer that works alongside Consul Connect’s automatic mTLS without requiring code changes.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.