Skip to content

RFC: App-to-App mTLS via GoRouter#1438

Open
rkoster wants to merge 2 commits intomainfrom
rfc-app-to-app-mtls-routing
Open

RFC: App-to-App mTLS via GoRouter#1438
rkoster wants to merge 2 commits intomainfrom
rfc-app-to-app-mtls-routing

Conversation

@rkoster
Copy link
Contributor

@rkoster rkoster commented Feb 17, 2026

Summary

This RFC proposes enabling authenticated and authorized app-to-app communication via GoRouter using mutual TLS (mTLS).

View the full RFC

Applications connect to a shared internal domain (apps.mtls.internal), where GoRouter:

  • Requires and validates client certificates (Instance Identity)
  • Enforces per-route access control using allowed_sources
  • Follows a default-deny model (aligned with C2C network policies)

Key Points

  • No new infrastructure: Uses existing GoRouter with domain-specific mTLS configuration
  • Default-deny security: Routes blocked unless explicitly allowed via allowed_sources
  • Internal-only domain: apps.mtls.internal is a separate domain tree, avoiding conflicts with existing apps.internal routes
  • Depends on RFC-0027: Route options framework required for allowed_sources support

Implementation Phases

  • Phase 1a (mTLS Infrastructure): GoRouter validates client certificates for the mTLS domain
  • Phase 1b (Authorization Enforcement): Per-route access control via allowed_sources (co-requisite with 1a)
  • Phase 2 (Optional): Egress HTTP proxy on port 8888 for simplified client adoption

cc @cloudfoundry/toc @cloudfoundry/wg-app-runtime-interfaces

@rkoster rkoster force-pushed the rfc-app-to-app-mtls-routing branch from bce2e1d to 5557aeb Compare February 17, 2026 15:47
@rkoster rkoster added toc rfc CFF community RFC labels Feb 17, 2026
@rkoster rkoster requested review from a team, Gerg, beyhan, cweibel and stephanme and removed request for a team February 17, 2026 15:55
@silvestre
Copy link
Member

I really like this proposal.

Just to be sure: It would be still possible to have an apps.mtls.internal route allowing access for any source app, so that the authorization check could be done in the app, right?

One use-case would be in the app-autoscaler service, where we expose an mTLS endpoint but check the authorization by determining if the app is bound to an autoscaler service instance, which is dynamic information we could not determine during route creation.

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.
@rkoster rkoster force-pushed the rfc-app-to-app-mtls-routing branch from 5557aeb to 8f3900a Compare February 17, 2026 19:04
@rkoster
Copy link
Contributor Author

rkoster commented Feb 17, 2026

I really like this proposal.

Just to be sure: It would be still possible to have an apps.mtls.internal route allowing access for any source app, so that the authorization check could be done in the app, right?

One use-case would be in the app-autoscaler service, where we expose an mTLS endpoint but check the authorization by determining if the app is bound to an autoscaler service instance, which is dynamic information we could not determine during route creation.

@silvestre I have update the RFC with:

applications:
- name: autoscaler-api
  routes:
  - route: autoscaler.apps.mtls.internal
    options:
      allowed_sources:
        any: true

@theghost5800
Copy link
Contributor

This idea is really interesting but will be possible to have communication to app containers on different ports or different protocol than http?

Give credit to Beyhan and Max for the initial work on this RFC
@rkoster
Copy link
Contributor Author

rkoster commented Feb 19, 2026

This idea is really interesting but will be possible to have communication to app containers on different ports or different protocol than http?

This RFC currently focuses on HTTP traffic via GoRouter, but non-HTTP protocol support is an interesting future direction.

Current constraints:

  • GoRouter uses Go's httputil.ReverseProxy which handles HTTP semantics (headers, paths, etc.)
  • Caller identity is forwarded via the XFCC HTTP header, which doesn't exist for raw TCP
  • GoRouter does not currently support HTTP CONNECT method for tunneling

What would be needed for non-HTTP support:

  1. HTTP CONNECT tunneling in GoRouter: GoRouter would need to detect CONNECT requests, validate mTLS + allowed_sources, then hijack the connection and relay raw TCP bytes to the backend. The pattern exists (similar to WebSocket upgrades), but would require new implementation.
  2. Identity forwarding challenge: Inside a TCP tunnel there's no XFCC header. Options include:
  • PROXY protocol v2 (GoRouter sends client cert info as TLV before the TCP stream)
  • Backend also requires mTLS and validates the client cert directly
  • Application-layer identity (less secure)
  1. Envoy egress proxy: The Phase 2 egress proxy (Envoy) already supports HTTP CONNECT tunneling, so apps could potentially use CONNECT backend.apps.mtls.internal:5432 to tunnel arbitrary protocols. But GoRouter still needs to support CONNECT for this to work end-to-end.

For now, this is out of scope to keep the RFC focused and achievable. But feel free to create a follow up RFC for Non-HTTP use cases.

@beyhan beyhan moved this from Inbox to In Progress in CF Community Feb 24, 2026
- `any: true` is mutually exclusive with `apps`, `spaces`, and `orgs`
- If `any` is not set, at least one of `apps`, `spaces`, or `orgs` must be specified (default-deny)

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.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We need to be careful with what we add to the per-route features. Each and every option we set in there will be transmitted via the NATS bus every 20s (unless adjusted by the operator) from each app instance to each gorouter instance. Even a slight increase in message size can have quite noticeable effects on the overall bandwidth consumption.

One of the thoughts we had, is to only allow very simple rules. Specifically allow from instances of the same app, from all apps within a space, from all apps within an org. This could be a simple enum option allowed_scope: app / space / org (name is just for illustration purposes) which would keep the size within a predictable scope.

If we go for a more flexible option we must introduce strict limits on the number of bytes each user is allowed to add to each route.

- Only CF applications can connect (mTLS with instance identity)
- Traffic stays internal (no load balancer round-trip)
- The platform enforces which apps can call which routes
- Standard GoRouter features work (load balancing, retries, observability)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Especially for larger deployments it can be beneficial to have a TCP load balancer in front of gorouter to guarantee a reliable load balancing across gorouter instances as DNS load balancing is always at the mercy of a good client-side implementation.

I don't see why this wouldn't be possible with the proposed design, just wanted to mention it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

rfc CFF community RFC toc

Projects

Status: In Progress

Development

Successfully merging this pull request may close these issues.

5 participants