You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Restructure RFC: Part 1 BOSH-only, CC API content in Part 2
- Architecture Overview gains end-to-end step table labelled by part
- Part 1 now contains only the GoRouter BOSH configuration (mtls_domains);
no Cloud Controller API changes belong here
- CC API domain settings (enforce_access_rules, access_rules_scope, scope
table, shared routes / endpoint pools, operator CLI commands) moved to
the top of Part 2
- Part 2 opening sentence makes the boundary explicit: implemented
entirely through CC API changes, no additional BOSH config needed
- xfcc_format yaml comment trimmed; new XFCC header format paragraph
added after the BOSH block with link to Envoy XFCC docs on the envoy
bullet
- Various fixes from PR review: selector_resource_guids filter, GUID
validation clarification, --path flag on all CLI commands, cf:any
mutual exclusivity, enable/disable-domain-access-rules separate
commands, API endpoints table expanded, cascade delete and metadata
@@ -43,7 +43,21 @@ GoRouter gains the ability to require client certificates for specific domains,
43
43
44
44
### Architecture Overview
45
45
46
-
The diagram below shows CF app-to-app routing (the most complex use case). For external client certificate validation, only GoRouter and the backend app are involved—external clients connect directly to GoRouter with their certificates.
46
+
**How it works end-to-end:**
47
+
48
+
| Step | Part | Actor | What happens |
49
+
|------|------|-------|--------------|
50
+
| 1 | 1 | Operator | Configures a domain with mTLS requirements in the `mtls_domains` BOSH configuration |
51
+
| 2 | 1 | DNS | BOSH DNS (or external DNS) resolves the domain to GoRouter instances |
52
+
| 3 | 1 | Developer | Maps application routes to this domain like any shared domain |
53
+
| 4 | 1 | GoRouter | Requires and validates a client certificate, sets the XFCC header |
54
+
| 5 | 2 | Operator | Enables access rules enforcement on the domain via the CF API (`enforce_access_rules: true`) |
55
+
| 6 | 2 | Developer | Creates access rules per route via the Access Rules API |
56
+
| 7 | 2 | GoRouter | Extracts CF identity from the certificate and enforces access rules |
57
+
58
+
Part 1 alone (without Part 2) is sufficient for external client certificate validation: GoRouter validates the cert and sets the XFCC header; backend applications handle authorization themselves based on that header.
59
+
60
+
The diagram below shows the most complex use case: CF app-to-app routing with both parts active.
47
61
48
62
```mermaid
49
63
flowchart LR
@@ -70,18 +84,7 @@ flowchart LR
70
84
71
85
### Part 1: mTLS Domain Infrastructure
72
86
73
-
GoRouter gains the ability to require client certificates for specific domains while leaving other domains unaffected. This infrastructure is generic and can be used for multiple purposes beyond CF app-to-app routing.
74
-
75
-
**How it works:**
76
-
1. Operator configures a domain with mTLS requirements in the `mtls_domains` BOSH configuration
77
-
2. Operator enables access rules enforcement via Cloud Controller API (`enforce_access_rules: true`)
78
-
3. DNS (BOSH DNS or external) resolves the domain to GoRouter instances
79
-
4. Applications map routes to this domain like any shared domain
80
-
5. When a client connects, GoRouter:
81
-
- Requires a client certificate
82
-
- Validates it against the configured CA
83
-
- Sets the XFCC header with certificate details (format configurable)
84
-
- If route options contain `access_rules`, enforces authorization (Part 2)
87
+
GoRouter gains the ability to require client certificates for specific domains while leaving other domains unaffected. This infrastructure is generic and can be used for multiple purposes beyond CF app-to-app routing. Operators configure it entirely through the BOSH manifest.
85
88
86
89
**GoRouter BOSH Configuration:**
87
90
@@ -100,26 +103,32 @@ router:
100
103
# always_forward - Always pass through, even if no client cert
101
104
forwarded_client_cert: sanitize_set
102
105
103
-
# Format of the XFCC header:
104
-
# raw (default) - Full base64-encoded certificate (~1.5KB)
# When using envoy format, GoRouter automatically extracts
108
-
# CF identity (app/space/org GUIDs) from the Subject OU fields
109
-
# for authorization enforcement.
106
+
# Format of the XFCC header value: raw (default) or envoy
110
107
xfcc_format: envoy
111
108
```
112
109
113
-
The BOSH configuration is minimal—just TLS settings. Authorization policy is managed entirely through Cloud Controller.
110
+
**XFCC header format:**
111
+
112
+
The `xfcc_format` field controls the format of the `X-Forwarded-Client-Cert` header that GoRouter sets on proxied requests:
113
+
114
+
- **`raw`** (default): The full PEM certificate is base64-encoded and placed in the header. This produces a large header value (approximately 1.5 KB per certificate) that the backend application must decode and parse to extract identity fields.
115
+
- **`envoy`**: GoRouter uses the same compact format as [Envoy's XFCC implementation](https://www.envoyproxy.io/docs/envoy/latest/configuration/http/http_conn_man/headers#x-forwarded-client-cert): `Hash=<sha256-fingerprint>;Subject="<distinguished-name>"`. This reduces the header to approximately 300 bytes. When `xfcc_format: envoy` is configured and Part 2 authorization is active, GoRouter parses identity directly from the Subject's `OU` fields (`OU=app:<guid>`, `OU=space:<guid>`, `OU=organization:<guid>`) without decoding the full certificate, which is more efficient.
116
+
117
+
### Part 2: CF Identity & Authorization
114
118
115
-
**Cloud Controller Domain Configuration:**
119
+
Part 2 adds Cloud Foundry identity and authorization on top of the mTLS infrastructure from Part 1. It is implemented entirely through Cloud Controller API changes — no additional BOSH configuration is required beyond Part 1.
116
120
117
-
Operators enable access rules enforcement via the Cloud Controller API:
121
+
**Operator configuration (CC API):**
122
+
123
+
Operators enable access rules enforcement on a domain via the Cloud Controller API:
@@ -158,8 +167,6 @@ EndpointPool for "api.apps.mtls.internal":
158
167
159
168
GoRouter iterates the pool's endpoints when evaluating scope, checking the caller's identity against each endpoint's tags and short-circuiting on the first match. For `scope: space`, if any endpoint's `space_id` matches the caller's space GUID, the request is allowed. A caller from Space A or Space B passes the check; a caller from Space C (with no app mapped to the route) is denied. This naturally enables cross-space access on shared routes between the participating spaces.
160
169
161
-
### Part 2: CF Identity & Authorization
162
-
163
170
When a domain has `enforce_access_rules: true`, GoRouter enforces access control at the routing layer using a default-deny model, matching the design of container-to-container network policies. If no access rules are configured for a route, all requests are denied.
164
171
165
172
**Identity extraction:** GoRouter extracts CF identity from Diego instance identity certificates regardless of `xfcc_format`. With `envoy` format, identity is parsed from pre-extracted Subject fields (`OU=app:<guid>,OU=space:<guid>,OU=organization:<guid>`). With `raw` format, GoRouter decodes the base64 certificate and extracts the same fields. The `envoy` format is more performant but both work identically for authorization.
@@ -182,16 +189,19 @@ Developers can only **restrict further** within operator boundaries. They cannot
182
189
183
190
#### Access Rules API
184
191
185
-
Developers manage route-level access rules through a dedicated Cloud Controller API. Each access rule has a human-readable name for auditability and a selector that identifies allowed callers. See [Access Rules API Examples](#access-rules-api-examples) for full request/response examples.
192
+
Developers manage route-level access rules through a dedicated Cloud Controller API. Each access rule has a human-readable name for auditability and a selector that identifies allowed callers. Access rules are owned by their route: deleting a route cascades to delete all its access rules. See [Access Rules API Examples](#access-rules-api-examples) for full request/response examples.
| `DELETE` | `/v3/access_rules/:guid` | Delete a rule by guid |
203
+
| `GET` | `/v3/routes/:route_guid/access_rules` | List access rules for a route |
204
+
| `POST` | `/v3/routes/:route_guid/access_rules` | Create access rules for a route (batch convenience endpoint) |
195
205
196
206
**List Query Parameters:**
197
207
@@ -200,6 +210,7 @@ Developers manage route-level access rules through a dedicated Cloud Controller
200
210
| `names` | Comma-delimited list of rule names to filter by |
201
211
| `route_guids` | Comma-delimited list of route guids to filter by |
202
212
| `selectors` | Comma-delimited list of selectors to filter by |
213
+
| `selector_resource_guids` | Comma-delimited list of resolved selector resource GUIDs to filter by. Pass empty value (`selector_resource_guids=`) to return only rules whose selector resource no longer exists (stale rules). |
203
214
| `include` | Comma-delimited list of related resources to include: `route`, `selector_resource` |
204
215
| `page` | Page to display (default: 1) |
205
216
| `per_page` | Number of results per page (default: 50) |
@@ -249,28 +260,27 @@ The `cf:` prefix is reserved for Cloud Foundry native identities. Future extensi
Selectors always require a GUID rather than a name. This is intentional: the person creating the rule does not need read access to the selector's source app, space, or org. The GUID is a public identity that the calling team shares out of band.
268
277
269
278
**Validation rules:**
270
279
- Access rules can only be created for routes on domains where `enforce_access_rules` is true
271
-
-`cf:any`is mutually exclusive with specific selectors on the same route (cannot combine `cf:any`with `cf:app:*`)
280
+
- `cf:any`cannot be combined with other selectors on the same route. If a route has a `cf:any` rule, no other rules (`cf:app:...`, `cf:space:...`, `cf:org:...`) can be added.
272
281
- Duplicate selectors on the same route are rejected with an error
273
282
- Rule names must be unique per route
283
+
- Selector GUIDs (`cf:app:<guid>`, `cf:space:<guid>`, `cf:org:<guid>`) are not validated against Cloud Controller at creation time. The developer creating the rule does not need visibility into the source app, space, or org. App and space GUIDs function as public identities that teams can share with each other out of band. This intentionally differs from C2C networking, where both sides of a policy must be reachable by the policy creator.
274
284
275
285
**Authorization:**
276
286
@@ -284,7 +294,7 @@ Cloud Controller stores access rules in a dedicated `route_access_rules` table.
284
294
# Access rules are converted to RFC-0027 compliant route options
0 commit comments