Skip to content

feat(xds): per-stream call-credentials hook for the xDS transport#2705

Open
choonkijang wants to merge 3 commits into
grpc:masterfrom
choonkijang:feat-xds-call-credentials
Open

feat(xds): per-stream call-credentials hook for the xDS transport#2705
choonkijang wants to merge 3 commits into
grpc:masterfrom
choonkijang:feat-xds-call-credentials

Conversation

@choonkijang

@choonkijang choonkijang commented Jun 25, 2026

Copy link
Copy Markdown

Motivation

Per #2702, credential-gated xDS control planes (e.g. GCP Traffic Director / google_default) require two things the transport did not provide: per-stream call credentials on the ADS stream, and a secure channel to carry them. Their bootstrap server_uri is often scheme-less (e.g. trafficdirector.googleapis.com:443), which parses with no scheme — so Endpoint has no signal to negotiate TLS, the ADS connection never establishes a secure session, and routing times out with "route config not yet available".

Solution

xds-client:

  • Add a minimal TonicCallCredentials trait mirroring grpc-go's PerRPCCredentials. The tonic transport attaches it on each (re)connect, only over a secure channel.
  • Construction is builder-based: TonicTransportBuilder::with_call_credentials and with_channel(Channel, secure) for a pre-built channel.
  • On the secure path, prepend https:// to server_uri in TonicTransportBuilder::build when it's missing the scheme.

tonic-xds:

  • Promote google_default to a first-class ChannelCredentialType::GoogleDefault (gRFC A27).
  • Add XdsChannelConfig::with_call_credentials(...), so that applications can give a token source without relying on grpc or grpc-google crates at this time.

examples:

  • Add the channel_with_google_default tonic-xds example (reusing the testutil greeter), demonstrating two ADC token sources.

BREAKING CHANGE:

  • removes TonicTransport::from_channel (use TonicTransportBuilder::with_channel), and
  • adds a variant to the Error enum

Per grpc#2444, credential-gated xDS control planes (e.g. GCP Traffic Director / `google_default`) require two things the transport did not provide: per-stream call credentials on the ADS stream, and a secure channel to carry them. Their bootstrap `server_uri` is often scheme-less (e.g. `trafficdirector.googleapis.com:443`), which parses with no scheme — so `Endpoint` has no signal to negotiate TLS, the ADS connection never establishes a secure session, and routing times out with "route config not yet available".

xds-client:
- Add a minimal `CallCredentials` trait mirroring grpc-go's `PerRPCCredentials`. The tonic transport attaches it on each (re)connect, only over a secure channel.
- Construction is builder-based: `TonicTransportBuilder::with_call_credentials` and `with_channel(Channel, secure)` for a pre-built channel.
- On the secure path, prepend `https://` to `server_uri` in `TonicTransportBuilder::build` when it's missing the scheme.

tonic-xds:
- Promote `google_default` to a first-class `ChannelCredentialType::GoogleDefault` (gRFC A27).
- Add `XdsChannelConfig::with_call_credentials(...)`, so that applications can give a token source without relying on `grpc` or `grpc-google` crates at this time.

examples:
- Add the `channel_with_google_default` tonic-xds example (reusing the testutil greeter), demonstrating two ADC token sources.

BREAKING CHANGE:
* Relocate `TonicTransport::from_channel` to `TonicTransportBuilder::with_channel`, and
* Adds a variant to the `Error` enum
@linux-foundation-easycla

linux-foundation-easycla Bot commented Jun 25, 2026

Copy link
Copy Markdown

CLA Signed
The committers listed above are authorized under a signed CLA.

  • ✅ login: choonkijang / name: Choonki Jang (0472b49)

Comment thread tonic-xds/Cargo.toml Outdated
Comment thread xds-client/src/transport/tonic.rs Outdated
Comment thread xds-client/src/transport/tonic.rs Outdated
@choonkijang choonkijang requested a review from YutaoMa June 26, 2026 01:41
@choonkijang

Copy link
Copy Markdown
Author

@YutaoMa PTAL!

Comment thread xds-client/src/error.rs
#[error("stream error: {0}")]
Stream(#[from] tonic::Status),

/// Call credentials failed, or require a secure transport.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Let's remove the feature gating on this error variant, non-additive features in Rust can be a footgun: implicit feature enablement with other dependencies can break compilation.

While we are at it, let's also add #[non_exhaustive] to the Error, it's a miss on my end to not mark it so previously. That'll help with future extensions like this.

@choonkijang choonkijang Jul 1, 2026

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Done. Dropped the #[cfg(feature = "transport-tonic")] on CallCredentials and added #[non_exhaustive] to Error. See 447e849.

/// Generates the authentication metadata for a specific call.
async fn get_request_metadata(
&self,
metadata: &mut tonic::metadata::MetadataMap,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Is there any specific reason to prefer out-param style API? I'd recommend keeping this simple and idiomatic by returning an owned map.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Ah saw the grpc link comment above, seems like it's to keep the style consistent with grpc. In that case yeah it's good to keep it as is.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Yeah, thanks for confirmation!

Comment thread xds-client/src/transport/tonic.rs Outdated
///
/// Attached on each (re)connect, only when the channel is secure.
#[tonic::async_trait]
pub trait CallCredentials: Send + Sync + std::fmt::Debug + 'static {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Could you re-export this type in tonic-xds so user of tonic-xds doesn't need to explicitly depend on xds-client to use this feature? (xds-client is intended to stay as a low-level ADS transport library so gRPC service developers don't have to depend on it explicitly). Also I suggest naming it TonicCallCredentials here: once we support grpc transport in xds-client, similar API will need to be added for GrpcCallCredentials, keeping the name distinguishable helps with future extensions.

@choonkijang choonkijang Jul 1, 2026

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Done. Renamed and re-exported. See 447e849.

@choonkijang choonkijang force-pushed the feat-xds-call-credentials branch from bed4264 to 47030ae Compare July 1, 2026 21:51
@choonkijang choonkijang requested a review from YutaoMa July 1, 2026 21:54
@choonkijang

Copy link
Copy Markdown
Author

@YutaoMa PTAL!

- Added #[non_exhaustive] to pub enum Error, and remove #[cfg(feature = "transport-tonic")] from `CallCredentials` error entry.
- Rename `CallCredentials` as `TonicCallCredentials`, and let `tonic-xds` re-export it to public.
@choonkijang choonkijang force-pushed the feat-xds-call-credentials branch from 47030ae to 447e849 Compare July 1, 2026 22:09
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants