Consul Associate study materials
Consul Associate study resources

L7 Intention That Behaves Like a Blanket Allow

Consul Associate · Put your knowledge to work in context.

All case studies

L7 Intention That Behaves Like a Blanket Allow

Tailspin runs Connect with Envoy sidecars for 30 services in one datacenter, with mTLS on. The security team added a wildcard * -> * deny intention and then per-service allows, and asked the payments team to expose only GET /v1/public/* to web. The payments team created one intention: source web, destination payments, action allow, with an L7 permission allowing PathExact /v1/public and Methods [GET]. They confirmed with consul intention check -src web -dst payments that the intention matches. In testing, web can still call POST /v1/refunds on payments and get a 200. The payments service was registered by the SDK with only a name, port, HTTP health check, and connect { sidecar_service {} }. No config entries exist anywhere in the cluster.

1. The intention clearly matches and clearly says GET-only, yet POST succeeds. Why, and what exactly has to change?

Read the worked answer

L7 permissions are only evaluated when Consul knows the destination speaks an L7 protocol. That is declared by a service-defaults config entry, and the default protocol is tcp. Because no config entries exist, payments is treated as TCP, so the permission list is never compiled into Envoy's HTTP filters and the intention degrades to a plain L4 allow. The security team's wildcard deny is defeated by an allow intention that was supposed to be path-scoped.

Steps:

  1. Write the config entry:
Kind = "service-defaults"
Name = "payments"
Protocol = "http"
  1. Regenerate the sidecars so Envoy receives HTTP listeners and filters. Config entries propagate over xDS, but a protocol change changes the shape of the proxy's config, so plan a rolling restart of the payments sidecars and, in practice, of the calling web sidecars too.
  2. Re-test with a POST /v1/refunds - expect a 403 from the downstream sidecar, not a connection error. A 403 confirms the filter is doing the work; a connection reset usually means mTLS or intention matching failed earlier.

Note this also means L7 features are unusable with the built-in proxy, which is L4 only; Envoy sidecars are required.

2. A second consumer, `reporting`, now needs `GET /v1/reports/*` on payments. Walk through the change and any ordering hazards.

Read the worked answer

Intentions are matched most-specific-first, and the wildcard * -> * deny sits at the bottom of that ordering. Adding an exact-source, exact-destination allow for reporting -> payments will therefore win over the wildcard deny without touching any existing rule - no need to modify the web intention or to relax the wildcard.

Order of operations:

  1. Confirm reporting actually runs a sidecar. Intentions match on the source's mTLS identity, i.e. the service name in its certificate. A reporting process without a sidecar has no identity and can never match; it will simply be denied, and the logs will look like a rule problem rather than a topology problem.
  2. Create the intention with the L7 permissions for GET /v1/reports/* on destination payments. The http protocol is already set on payments from the earlier fix, so the permission list will be enforced rather than ignored - verify this assumption rather than trusting it, since a missing protocol silently converts the rule to a blanket allow.
  3. Verify with consul intention check -src reporting -dst payments that it reports the new intention as the match, and confirm the web rule still wins for web.
  4. Deploy and test both the intended path (200 on /v1/reports/x) and an adjacent path (/v1/refunds should still 403 for reporting).

No restart of the payments sidecar is needed: intentions are pushed to proxies dynamically, which is the point of keeping them out of static config.

3. A batch job on a VM with no sidecar needs to read `GET /v1/reports/*` on payments. What are the options, and which is correct?

Read the worked answer

Intentions are enforced by the destination sidecar on the source identity presented in the mTLS certificate. A process without a sidecar has no certificate issued by the mesh CA, so it has no identity that an intention can match - there is no source name to write in a rule.

Options:

  • Put it in the mesh (recommended). Register a sidecar for the batch job, even if it only makes outbound calls. The job then presents an identity you can name in an intention, and you get mTLS plus the same L7 filtering as everyone else.
  • Route through an ingress gateway. The caller connects to a gateway, and the gateway's service identity is what reaches payments. The intention is then written against the gateway's service name, not the job's, so the rule is necessarily broader than the job itself and must be reviewed as such. Appropriate when many external callers share a controlled entry point.
  • Bypass the mesh to the pod/VM directly. This works only while the target's port is reachable and is exactly what the mesh migration was meant to close; it also bypasses the intention entirely, so a path-scoped rule protects nothing. Treat any such residual path as a finding, not a design.

The judgement point: an intention cannot be granted to something that has no mesh identity, so the answer is always a topology change (add a sidecar or a gateway), never a new rule.