Managing Zero-Trust Traffic with Consul Connect Intentions
Stop relying on fragile firewall rules. Learn how to use Consul Connect Intentions to declaratively control service-to-service communication without changing a single line of code.
09 Mar 2026, 15:52 UTC

The Problem: The "Flat Network" Security Gap
In many microservices architectures, once a request passes the edge gateway, the internal network is treated as a trusted zone. This "flat network" approach means that if a single low-priority service is compromised, an attacker can move laterally to sensitive databases or internal APIs because there are no granular restrictions on service-to-service communication.
Traditional fixes—like managing thousands of IP-based firewall rules—are unsustainable in dynamic environments where containers scale and IP addresses change constantly. The goal is to enforce security based on service identity rather than network location, without forcing developers to write custom authorization logic into every single microservice.
The Solution: Declarative Service Intentions
Consul Connect Intentions provide a centralized, declarative way to define which services are allowed to communicate. Instead of managing ports and IPs, you define a policy: "Service A is allowed to talk to Service B."
These intentions are stored in the Consul catalog and enforced by sidecar proxies (like Envoy) that sit in front of your services. When a service attempts to connect to another, the proxy checks the current intention set. If no "allow" rule exists (or if a "deny" rule is explicitly set), the proxy drops the connection at the L4/L7 layer before it ever reaches the application code.
Worked Example: Restricting Frontend-to-Backend Traffic
This example assumes a Consul environment (v1.10+) with Connect enabled and two services registered: frontend and backend, both running with sidecar proxies.
1. Verify Default Blockage
By default, if you have a zero-trust posture, traffic should be blocked. Run this command from the frontend container's network namespace:
curl -v http://backend.service.consul:9090/api/data
Expected Result: The request should time out or return a connection reset. If you check the sidecar proxy logs, you will see entries indicating the connection was denied by an intention policy.
2. Create an Allow Intention
To permit the frontend to access the backend, run the following command from a terminal with the appropriate Consul ACL token (requires service:write permissions):
consul intention create -allow frontend backend
This tells Consul to propagate an "allow" rule to all proxies associated with the backend service.
3. Verify Connectivity
Repeat the curl request from the frontend container:
curl -v http://backend.service.consul:9090/api/data
Expected Result: The request now succeeds with a 200 OK, as the proxy has updated its local policy cache to allow the traffic.
4. Rollback: Explicitly Deny Traffic
If you need to immediately revoke access (e.g., during an incident), create a deny intention. Deny rules always take precedence over allow rules.
consul intention create -deny frontend backend
The next curl attempt will be blocked again, regardless of any existing allow rules.
Trade-offs and Technical Limitations
- Proxy Dependency: Intentions are only enforced if the traffic flows through the sidecar proxy. If a service bypasses the proxy to hit the destination's IP directly, the intention is ignored. To prevent this, you must configure the destination service to only listen on the local loopback interface.
- Eventual Consistency: Consul uses a gossip protocol to distribute state. There is a brief window—usually milliseconds—between creating an intention and the proxy enforcing it. During a leader election, proxies may rely on cached policies, which could lead to temporary discrepancies.
- L4 vs L7: Intentions primarily control whether a connection can be made (L4). They do not replace application-level authorization (e.g., checking if a user has a specific JWT claim to access a specific resource).
- Latency: Every request must pass through two proxies (source and destination), adding a small amount of overhead to the request latency.
Practical Implementation Strategy
To move toward a zero-trust model without causing a production outage, follow this sequence:
- Audit Mode: Deploy sidecars but keep intentions open. Monitor proxy logs to identify all existing service-to-service communication paths.
- Baseline Allows: Create explicit
-allowintentions for every path identified during the audit. - The "Deny All" Switch: Once all legitimate paths are documented, create a global deny intention:
consul intention create -deny '*' '*'. This ensures that any new, undocumented service communication is blocked by default. - Automate via IaC: Avoid manual CLI commands for production. Use the
consul_intentionresource in Terraform to version-control your security policies.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.