Skip to content

Commit 5557aeb

Browse files
committed
RFC: App-to-App mTLS via GoRouter
Enable authenticated and authorized app-to-app communication via GoRouter using mutual TLS (mTLS). Applications connect to a shared internal domain (apps.mtls.internal), where GoRouter validates client certificates and enforces per-route access control using a default-deny model. Key features: - Phase 1a: Domain-specific mTLS in GoRouter (validates instance identity) - Phase 1b: Authorization enforcement via allowed_sources route option - Phase 2 (optional): Egress HTTP proxy for simplified client adoption Depends on RFC-0027 (Generic Per-Route Features) for route options support.
1 parent 8d676db commit 5557aeb

1 file changed

Lines changed: 198 additions & 0 deletions

File tree

Lines changed: 198 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,198 @@
1+
# Meta
2+
[meta]: #meta
3+
- Name: App-to-App mTLS via GoRouter
4+
- Start Date: 2026-02-16
5+
- Author(s): @rkoster
6+
- Status: Draft
7+
- RFC Pull Request: [community#1438](https://github.com/cloudfoundry/community/pull/1438)
8+
9+
10+
## Summary
11+
12+
Enable authenticated and authorized app-to-app communication via GoRouter using mutual TLS (mTLS). Applications connect to a shared internal domain (e.g., `apps.mtls.internal`), resolvable only from within CF, where GoRouter requires client certificates, validates caller identity, and enforces per-route access control before forwarding requests.
13+
14+
This follows the same default-deny model as container-to-container network policies: all traffic is blocked unless explicitly allowed.
15+
16+
17+
## Problem
18+
19+
Cloud Foundry applications can communicate via external routes (through GoRouter) or container-to-container networking (direct). Neither option provides authenticated app-to-app communication with platform-enforced authorization:
20+
21+
- **External routes**: Traffic leaves the VPC to reach the load balancer, adding latency and cost. GoRouter's client certificate settings are global, so enabling strict mTLS breaks external clients.
22+
- **C2C networking**: Requires `network.write` permission (often unavailable to developers), lacks load balancing and observability, and has no identity forwarding.
23+
24+
**The gap**: There is no way for applications to communicate securely through GoRouter where:
25+
- Only CF applications can connect (mTLS with instance identity)
26+
- Traffic stays internal (no load balancer round-trip)
27+
- The platform enforces which apps can call which routes
28+
- Standard GoRouter features work (load balancing, retries, observability)
29+
30+
Authentication alone is insufficient. Without authorization enforcement, any authenticated app could access any route on the mTLS domain, defeating the purpose of platform-enforced security.
31+
32+
33+
## Proposal
34+
35+
### Architecture Overview
36+
37+
```mermaid
38+
flowchart LR
39+
subgraph "App A Container"
40+
AppA["App A"]
41+
ProxyA["Envoy<br/>(egress proxy)"]
42+
end
43+
44+
subgraph "GoRouter"
45+
GR["1. Validate cert<br/>(Instance Identity CA)"]
46+
Auth["2. Check allowed_sources"]
47+
end
48+
49+
subgraph "App B Container"
50+
ProxyB["Envoy<br/>(validates GoRouter cert)"]
51+
AppB["App B"]
52+
end
53+
54+
AppA -->|"HTTP"| ProxyA
55+
ProxyA -->|"mTLS<br/>(instance cert)"| GR
56+
GR --> Auth
57+
Auth -->|"authorized<br/>mTLS<br/>(GoRouter cert)"| ProxyB
58+
Auth -.->|"403 Forbidden"| ProxyA
59+
ProxyB -->|"HTTP + XFCC"| AppB
60+
```
61+
62+
The solution has two core components that must ship together:
63+
64+
- **Phase 1a (mTLS Infrastructure)**: GoRouter requires and validates client certificates for the mTLS domain, forwarding caller identity via the XFCC header.
65+
- **Phase 1b (Authorization Enforcement)**: GoRouter enforces per-route access control. Routes are blocked by default unless `allowed_sources` explicitly permits the caller.
66+
67+
An optional enhancement simplifies client adoption:
68+
69+
- **Phase 2 (Egress HTTP Proxy)**: Sidecar proxy automatically injects instance identity certificates, so apps don't need TLS configuration.
70+
71+
### Phase 1a: mTLS Infrastructure
72+
73+
GoRouter gains the ability to require client certificates for specific domains while leaving other domains unaffected.
74+
75+
**How it works:**
76+
1. Operator configures an internal domain (e.g., `apps.mtls.internal`) with mTLS requirements
77+
2. BOSH DNS resolves `*.apps.mtls.internal` to GoRouter instances
78+
3. Applications map routes to this domain like any shared domain
79+
4. When a client connects, GoRouter:
80+
- Requires a client certificate
81+
- Validates it against the Instance Identity CA
82+
- Sets the XFCC header with the certificate details
83+
- Proceeds to authorization check (Phase 1b)
84+
85+
**Security settings** for the mTLS domain use the strictest configuration:
86+
- `client_cert_validation: require`: clients must present a certificate
87+
- `only_trust_client_ca_certs: true`: only Instance Identity CA is trusted
88+
- `forwarded_client_cert: sanitize_set`: XFCC header cannot be spoofed
89+
90+
### Phase 1b: Authorization Enforcement
91+
92+
GoRouter enforces access control at the routing layer using a default-deny model, matching the design of container-to-container network policies.
93+
94+
**How it works:**
95+
1. Route owner specifies `allowed_sources` in the route configuration
96+
2. When a request arrives on an mTLS domain, GoRouter:
97+
- Extracts the caller's identity from the client certificate (app GUID, space GUID, org GUID)
98+
- Checks if the identity matches any `allowed_sources` rule
99+
- Returns `403 Forbidden` if not authorized (default-deny)
100+
- Forwards the request with XFCC header if authorized
101+
102+
**Route configuration example:**
103+
```yaml
104+
applications:
105+
- name: backend-api
106+
routes:
107+
- route: backend.apps.mtls.internal
108+
options:
109+
allowed_sources:
110+
apps: ["frontend-app-guid"]
111+
spaces: ["trusted-space-guid"]
112+
```
113+
114+
This builds on the route options framework from [RFC-0027: Generic Per-Route Features](rfc-0027-generic-per-route-features.md). Phase 1b depends on RFC-0027 being implemented first.
115+
116+
### Implementation References
117+
118+
| Component | Reference |
119+
|-----------|-----------|
120+
| GoRouter TLS config | [`routing-release/.../config.go`](https://github.com/cloudfoundry/routing-release/blob/develop/src/code.cloudfoundry.org/gorouter/config/config.go) |
121+
| GoRouter BOSH spec | [`routing-release/jobs/gorouter/spec`](https://github.com/cloudfoundry/routing-release/blob/develop/jobs/gorouter/spec) |
122+
| RFC-0027 route options | [`toc/rfc/rfc-0027-generic-per-route-features.md`](rfc-0027-generic-per-route-features.md) |
123+
| Cloud Controller routes | [`cloud_controller_ng/.../route.rb`](https://github.com/cloudfoundry/cloud_controller_ng/blob/main/app/models/runtime/route.rb) |
124+
125+
126+
## Security Model
127+
128+
Two layers of mTLS enforcement ensure only authorized traffic reaches applications:
129+
130+
| Layer | Validates | Trusts |
131+
|-------|-----------|--------|
132+
| Client → GoRouter | App's instance identity cert | Instance Identity CA |
133+
| GoRouter → Backend | GoRouter's backend cert | GoRouter Backend CA |
134+
135+
This ensures:
136+
- Only CF application instances can connect to mTLS routes
137+
- Only GoRouter can connect to application backends
138+
- Applications cannot bypass GoRouter
139+
140+
141+
## Release Criteria
142+
143+
**Phase 1a and Phase 1b are co-requisites and must be released together.**
144+
145+
Deploying Phase 1a without Phase 1b would leave all mTLS routes accessible to any authenticated app, violating the default-deny security model. This RFC is not production-ready without both phases.
146+
147+
Phase 1b depends on [RFC-0027: Generic Per-Route Features](rfc-0027-generic-per-route-features.md) being implemented first.
148+
149+
This aligns with the container-to-container networking model where network policies enforce default-deny before traffic is allowed between apps.
150+
151+
152+
## Optional Enhancements
153+
154+
### Phase 2: Egress HTTP Proxy
155+
156+
To simplify client adoption, add an HTTP proxy to the application sidecar that automatically handles mTLS.
157+
158+
**How it works:**
159+
1. Diego configures an egress proxy (Envoy) listening on `127.0.0.1:8888`
160+
2. The proxy is configured to intercept requests to `*.apps.mtls.internal`
161+
3. For matching requests, the proxy:
162+
- Upgrades the connection to TLS
163+
- Presents the application's instance identity certificate
164+
- Forwards the request to GoRouter
165+
166+
**Application usage:**
167+
```bash
168+
# Client app sets HTTP_PROXY for the internal domain
169+
export HTTP_PROXY=http://127.0.0.1:8888
170+
export NO_PROXY=external-api.example.com
171+
172+
# Plain HTTP request, proxy handles mTLS automatically
173+
curl http://myservice.apps.mtls.internal/api
174+
```
175+
176+
This eliminates the need for applications to load certificates and configure TLS clients.
177+
178+
**Implementation references:**
179+
- Diego Envoy proxy: [`diego-release/.../envoy-builder`](https://github.com/cloudfoundry/diego-release/tree/develop/src/code.cloudfoundry.org/envoy-builder)
180+
- Instance identity certs: [`diego-release/docs/050-app-instance-identity.md`](https://github.com/cloudfoundry/diego-release/blob/develop/docs/050-app-instance-identity.md)
181+
182+
183+
## Key Repositories
184+
185+
| Repository | Changes |
186+
|------------|---------|
187+
| [routing-release](https://github.com/cloudfoundry/routing-release) | Phase 1a: `mtls_domains` config; Phase 1b: `allowed_sources` enforcement |
188+
| [capi-release](https://github.com/cloudfoundry/capi-release) | Phase 1b: `allowed_sources` route option validation |
189+
| [diego-release](https://github.com/cloudfoundry/diego-release) | Phase 2: Egress proxy configuration |
190+
| [cf-deployment](https://github.com/cloudfoundry/cf-deployment) | Ops-file for enabling mTLS app routing |
191+
192+
193+
## References
194+
195+
- [RFC-0027: Generic Per-Route Features](rfc-0027-generic-per-route-features.md)
196+
- [Container-to-Container Networking](https://docs.cloudfoundry.org/concepts/understand-cf-networking.html)
197+
- [Diego Instance Identity Documentation](https://github.com/cloudfoundry/diego-release/blob/develop/docs/050-app-instance-identity.md)
198+
- [GoRouter Client Certificate Configuration](https://github.com/cloudfoundry/routing-release/blob/develop/jobs/gorouter/spec)

0 commit comments

Comments
 (0)