Identity-aware routing
Page last updated:
This topic provides you with an overview of how identity-aware routing works in Cloud Foundry.
Identity-aware routing lets the Gorouter require and validate mutual TLS (mTLS) on a per-domain basis and route requests based on the caller’s verified identity. It enables app-to-app traffic to flow through the Gorouter — load-balanced and platform-enforced — while the platform authorizes each request from the caller’s certificate rather than relying on the app to authenticate callers itself.
Identity-aware routing has two tiers:
- Identity-aware domain (the headline use case). An operator creates an mTLS domain with route-policy enforcement enabled. The Gorouter validates the caller’s Diego instance identity certificate, extracts the caller’s app, space, and org, and enforces route policies with a default-deny model. This is the recommended way to do authenticated Cloud Foundry app-to-app communication over the Gorouter.
- mTLS domain (a variant). An operator creates an mTLS domain that requires and validates any client certificate signed by a configured certificate authority (CA), then forwards it to the app. Route policies are not enforced, so the backend app does its own authorization. This tier suits external, non-Cloud Foundry clients.
The *.apps.identity domain is to identity-aware routing what *.apps.internal is to container-to-container (C2C) networking: a conventional wildcard domain for app-to-app traffic. The key difference is the data path. Identity-aware traffic goes through the Gorouter, where it is load-balanced and centrally authorized, whereas C2C traffic flows directly between containers over an overlay network.
For direct, low-latency app-to-app traffic over an overlay network instead of through the Gorouter, see Container-to-container networking.
Architecture
Identity-aware routing combines four pieces: per-domain mTLS in the Gorouter, route policies stored by the Cloud Controller, the Diego instance identity that gives each app instance a verifiable certificate, and BOSH DNS for the *.apps.identity wildcard alias.
To understand the components and how they work together, see the following diagram and table.
| Part | Function |
|---|---|
| Gorouter (per-domain mTLS) | Terminates mTLS for domains configured under router.domains. For each such domain it:
|
| Cloud Controller | Stores route policies and the per-domain enforcement setting. It flattens policies into route options that are synced to Diego so the Gorouter can enforce them. |
| Diego instance identity | Issues every app instance a short-lived identity certificate. The Subject carries the caller’s identity as organizational units, for example CN=<instance-id>, OU=app:<app-guid>, OU=space:<space-guid>, OU=organization:<org-guid>. The certificate and key are available to the app as CF_INSTANCE_CERT and CF_INSTANCE_KEY. |
| BOSH DNS | Resolves the *.apps.identity wildcard alias to the Gorouter so that callers reach the platform’s mTLS listener. |
How app-to-app identity-aware routing works
When one app calls another over an identity-aware domain, the request flows through the following steps. The numbers correspond to the architecture diagram above.
- The calling app (for example,
frontend-app) makes an HTTPS request to the destination route, such ashttps://backend.apps.identity, presenting its Diego instance identity certificate as the client certificate. The certificate and key are mounted in the container asCF_INSTANCE_CERTandCF_INSTANCE_KEY. - The Gorouter terminates mTLS for the domain and validates the presented certificate against the domain’s configured CA (the instance identity CA).
- The Gorouter extracts the caller’s Cloud Foundry identity from the certificate Subject organizational units:
OU=app:<app-guid>,OU=space:<space-guid>, andOU=organization:<org-guid>. - The Gorouter checks the destination route’s policies. If no policy allows this caller, the Gorouter denies the request with an HTTP
403 Forbiddenresponse. This is the default-deny model. - If a policy allows the caller, the Gorouter forwards the request to the backend app and sets the
X-Forwarded-Client-Cert(XFCC) header so the backend can also see the verified caller identity. For more information, see The client certificate header and identity.
This model is destination-controlled: the policies that decide who may reach a route live on the destination route, not on the caller.
Route policies and the default-deny model
Route policies determine which callers are allowed to reach a route on an identity-aware domain. They are destination-controlled: only a Space Developer in the route’s own space manages them.
Enabling enforcement
Route-policy enforcement is turned on when the domain is created, and it is immutable for the life of the domain. An operator or org manager creates the domain with --enforce-route-policies:
$ cf create-shared-domain apps.identity --enforce-route-policies $ cf create-private-domain my-org apps.identity --enforce-route-policies
You can optionally bound the scope that policy sources might target with --scope, which accepts any, org, or space. The --scope flag is only valid together with --enforce-route-policies:
$ cf create-shared-domain apps.identity --enforce-route-policies --scope org
When enforcement is on, every route on the domain denies all callers until a policy explicitly allows them.
Policy sources
A policy allows one source to reach a route. A source identifies the caller by its verified Cloud Foundry identity and takes one of the following forms:
| Source | Friendly flag | Raw value |
|---|---|---|
| A specific app | --source-app APP |
cf:app:<app-guid> |
| All apps in a space | --source-space SPACE |
cf:space:<space-guid> |
| All apps in an org | --source-org ORG |
cf:org:<org-guid> |
| Any authenticated Cloud Foundry caller | --source-any |
cf:any |
Managing policies
Use the cf CLI to add, list, and remove route policies. For example, to allow frontend-app to reach the backend route, list the policies on the domain, and then remove the policy:
$ cf add-route-policy apps.identity --hostname backend --source-app frontend-app $ cf route-policies --domain apps.identity $ cf remove-route-policy apps.identity --hostname backend --source-app frontend-app
You can express the same source with the raw --source form, for example --source cf:app:<app-guid>. To match a specific path, add --path.
The client certificate header and identity
After the Gorouter validates the caller’s certificate, it passes the certificate — or a digest of it — to the backend app in the X-Forwarded-Client-Cert (XFCC) header. This is the same mechanism Cloud Foundry uses for forwarding client certificates in general. For more information, see Forwarding client certificate to apps.
A router.domains entry sets how the certificate is forwarded with xfcc_format:
| Format | Header contents | Approximate size |
|---|---|---|
raw (default) |
The full client certificate, base64-encoded PEM. | ~1.5 KB |
envoy |
A compact representation, Hash=<sha256>;Subject="<DN>". |
~300 B |
The backend app reads the certificate Subject organizational units (OU=app:<app-guid>, OU=space:<space-guid>, OU=organization:<org-guid>) to learn the caller’s app, space, and org. On an identity-aware domain, the platform has already validated the certificate and authorized the request, so the XFCC header conveys an identity the app can trust for auditing or finer-grained, app-level decisions.
Consuming the envoy (hashed) XFCC value in Java apps relies on the java-buildpack-client-certificate-mapper (cloudfoundry/java-buildpack-client-certificate-mapper#11). If that support is not yet released in your buildpack, prefer the raw format for Java backends.
Identity-aware routing, C2C networking, and ASGs
Identity-aware routing, container-to-container (C2C) networking, and application security groups (ASGs) all control app connectivity, but they operate at different points and with different identity models.
| Identity-aware routing | C2C networking | ASGs | |
|---|---|---|---|
| Data path | Through the Gorouter (load-balanced) | Direct, over the overlay network | Egress firewall (no app-to-app path of its own) |
| Identity source | Verified client certificate (Diego instance identity; app, space, org OUs) | Source app GUID, tagged in the VXLAN GBP header | Source space |
| Granularity | Caller app, space, or org → a route | Source app → destination app | Space → destination IP range and ports |
| Enforcement point | Gorouter (per-domain mTLS) | VXLAN policy agent on the Diego Cell | Diego Cell egress |
| Default model | Deny until a policy allows | Deny until a policy allows | Deny until an ASG allows |
| Load balancing and access logs | Yes (Gorouter access logs) | No (direct connection) | Not applicable |
In short: use identity-aware routing for north-south app-to-app traffic over the Gorouter with a verified caller identity; use C2C networking for direct, low-latency east-west traffic; and use ASGs for coarse-grained egress control.
External client certificates
Not every caller is a Cloud Foundry app. Partner systems, IoT devices, and other external clients present their own certificates, which do not carry a Diego instance identity. For these callers an operator configures a plain mTLS domain.
In this configuration the operator adds a router.domains entry whose CA (ca_certs) is the external CA that issues the client certificates. The Gorouter requires and validates the client certificate against that CA and forwards it to the backend in the XFCC header. Because there is no Cloud Foundry identity to evaluate, route policies are not used, and the backend app authorizes the request from the certificate it receives.
Do not pass --enforce-route-policies for a domain that serves non-Cloud Foundry certificates. Route policies key off the Cloud Foundry identity organizational units (app, space, org) in a Diego instance identity certificate, which external certificates do not have. Enforcement on such a domain would deny every caller.
Observability
The Gorouter records the outcome of mTLS validation and route-policy enforcement in its access logs. When the following fields are enabled as extra fields in the access-log configuration, each router (RTR) log line can include the verified caller identity and the policy decision:
| Field | Meaning |
|---|---|
caller_cf_app |
The caller’s app GUID, from the validated client certificate. |
caller_cf_space |
The caller’s space GUID. |
caller_cf_org |
The caller’s org GUID. |
route_policy |
The route-policy rule that matched the request, for example cf:app:<app-guid>. It is - when no rule matched or enforcement is disabled. |
tls_sni |
The TLS Server Name Indication (SNI) value the caller requested. |
Each field is - when the request carried no verified identity or the field does not apply. For example, an allowed request to backend.apps.identity records the caller and the matching rule:
... 200 ... tls_sni:"backend.apps.identity" caller_cf_app:"app-guid-123" caller_cf_space:"space-guid-456" caller_cf_org:"org-guid-789" route_policy:"cf:app:app-guid-123"
A request that is denied by the default-deny model records the verified caller but no matching rule, with an HTTP 403 status:
... 403 ... tls_sni:"backend.apps.identity" caller_cf_app:"app-guid-123" caller_cf_space:"space-guid-456" caller_cf_org:"org-guid-789" route_policy:"-"
These fields let operators audit who reached a route and which policy decisions allowed or denied traffic.
Related reading
- RFC-0055: Identity-Aware Routing for Gorouter
- Container-to-container networking
- HTTP routing: Forwarding client certificate to apps
- Configuring identity-aware routing (developer how-to)
- Enabling identity-aware routing (operator setup)