Skip to content

xds: Implement GrpcService parsing and shared side channels (gRFC A102) - #9319

Open
mbissa wants to merge 4 commits into
grpc:masterfrom
mbissa:pr4-rebased
Open

xds: Implement GrpcService parsing and shared side channels (gRFC A102)#9319
mbissa wants to merge 4 commits into
grpc:masterfrom
mbissa:pr4-rebased

Conversation

@mbissa

@mbissa mbissa commented Aug 13, 2026

Copy link
Copy Markdown
Contributor

Stacked on #9315 and #9194 — only the last 2 commits are new here.

Implements gRFC A102 GrpcService support, wired into ext_proc as the first consumer:

  • New internal/xds/grpcservice package: parses the GrpcService proto and applies the trust decision — a trusted server's (A81) credentials come from the proto's plugins; an untrusted server's target must be in the bootstrap allowed_grpc_services allowlist (NACK otherwise).
  • Shared, refcounted side channels on the xDS client, exposed to filters via ClientFilterOptions.
  • access_token call credentials (token sent only at PrivacyAndIntegrity).
  • ext_proc switched from its placeholder stubs to the real parse and shared channels.

RELEASE NOTES:

  • xds: Implement GrpcService parsing and shared side channels (gRFC A102)

@mbissa mbissa added this to the 1.84 Release milestone Aug 13, 2026
@mbissa mbissa added the Type: Feature New features or improvements in behavior label Aug 13, 2026
@codecov

codecov Bot commented Aug 13, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 68.11024% with 81 lines in your changes missing coverage. Please review.
✅ Project coverage is 83.08%. Comparing base (30ce1d5) to head (979d438).

Files with missing lines Patch % Lines
internal/xds/grpcservice/grpcservice.go 52.67% 49 Missing and 13 partials ⚠️
internal/xds/xdsclient/channel.go 82.35% 10 Missing and 2 partials ⚠️
internal/xds/httpfilter/extproc/ext_proc.go 83.33% 2 Missing and 1 partial ⚠️
internal/xds/httpfilter/ext_authz/ext_authz.go 0.00% 2 Missing ⚠️
xds/bootstrap/credentials.go 60.00% 2 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master    #9319      +/-   ##
==========================================
+ Coverage   83.06%   83.08%   +0.01%     
==========================================
  Files         423      426       +3     
  Lines       35236    35467     +231     
==========================================
+ Hits        29270    29466     +196     
- Misses       4449     4471      +22     
- Partials     1517     1530      +13     
Files with missing lines Coverage Δ
...ernal/xds/bootstrap/accesstokencreds/call_creds.go 100.00% <100.00%> (ø)
internal/xds/bootstrap/bootstrap.go 69.57% <100.00%> (+5.91%) ⬆️
internal/xds/httpfilter/extproc/config.go 89.47% <ø> (ø)
...ternal/xds/httpfilter/extproc/internal/internal.go 100.00% <100.00%> (+100.00%) ⬆️
internal/xds/httpfilter/httpfilter.go 87.50% <ø> (ø)
internal/xds/resolver/xds_resolver.go 87.63% <100.00%> (+0.04%) ⬆️
internal/xds/xdsclient/client.go 100.00% <ø> (ø)
internal/xds/xdsclient/clientimpl.go 78.29% <ø> (ø)
internal/xds/xdsclient/pool.go 83.78% <100.00%> (+0.45%) ⬆️
internal/xds/httpfilter/ext_authz/ext_authz.go 80.00% <0.00%> (ø)
... and 4 more

... and 23 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@mbissa
mbissa requested review from easwars and eshitachandwani and a lite review from Copilot and removed request for Copilot August 13, 2026 19:26
@mbissa

mbissa commented Aug 13, 2026

Copy link
Copy Markdown
Contributor Author

The dependencies check flags the two new envoy extension proto packages (grpc_service/call_credentials/access_token/v3, grpc_service/channel_credentials/xds/v3) pulled in by the A102 GrpcService credential-plugin parsing — confined to the xds packages; core grpc dependencies are unchanged.

@mbissa
mbissa force-pushed the pr4-rebased branch 2 times, most recently from d75cfcc to 40472da Compare August 14, 2026 12:43
Comment thread internal/xds/grpcservice/grpcservice.go Outdated
Comment thread internal/xds/grpcservice/grpcservice.go Outdated
Comment thread internal/xds/grpcservice/grpcservice.go Outdated
}
// The entry's refcount already dropped to zero and it is being
// cleaned up; remove it and create a fresh channel below.
delete(c.sideChannels, key)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Do we need this here? We already have a delete from map when the refcount goes to zero

// sideChannelRelease returns an idempotent release function for the given
// channel entry. It must be called without holding sideChannelsMu, since the
// last release runs the cleanup synchronously, which acquires the mutex.
func sideChannelRelease(rc *grpcsync.RefCounted[*grpc.ClientConn]) func() error {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Do we need this to return error if it is always going to return nil?


// GrpcService parses GrpcService protos in the context of a bootstrap
// configuration and a trust level for the delivering xDS server.
type GrpcService struct {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

nit: I am a little apprehensive about the GrpcService name, because from the name it seems like it should store the parsed grpcService proto , but it does not. Can we change it to something else.

}

// New returns a GrpcService that parses GrpcService protos against the given
// bootstrap configuration. The trusted argument indicates whether the xDS

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Nit: Can we change this comment a little? parses GrpcService protos against the given // bootstrap configuration is not very clear and very confusing if someone new reads this code with no context.

}
}

func TestParse(t *testing.T) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

can we have these tests as function of grpctest.Tester

// Tests that CreateChannel fails when the target is not allowlisted and the
// provided channel credentials are missing or unsupported, and when a call
// credentials type is not registered.
func (s) TestCreateChannel_Errors(t *testing.T) {

@eshitachandwani eshitachandwani Aug 17, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Should this be a table driven test ?

@easwars easwars left a comment

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.

Haven't made a full pass, but have enough to move this forward for now.

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.

These call credentials are configured via the xDS GrpcService proto and not via the bootstrap file. So, I don't think these should reside in this directory, which is specifically for credentials configured via the bootstrap file.

Comment on lines +63 to +65
if !ok || credentials.CheckSecurityLevel(ri.AuthInfo, credentials.PrivacyAndIntegrity) != nil {
return nil, nil
}

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.

This should return an error saying it is not suitable to transmit these tokens on a connection that does not provide PrivacyAndIntegrity.

// connection, but GetRequestMetadata withholds the token on connections that
// do not provide privacy and integrity.
func (c *callCreds) RequireTransportSecurity() bool {
return false

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.

This should return true.

Note that the token will not be sent on the wire unless the connection has security level PRIVACY_AND_INTEGRITY.

Comment on lines +69 to +71
// RequireTransportSecurity returns false. The credentials may be used on any
// connection, but GetRequestMetadata withholds the token on connections that
// do not provide privacy and integrity.

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.

The credentials may be used on any connection, but GetRequestMetadata withholds the token on connections that do not provide privacy and integrity.

I don't think this is true.

return map[string]string{"authorization": "Bearer " + c.token}, nil
}

// RequireTransportSecurity returns false. The credentials may be used on any

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.

The ideal docstring for this should be:

// RequireTransportSecurity indicates whether the credentials requires
// transport security.

//
// The caller is expected to invoke the cancel function when they are done
// using the returned call creds. This cancel function is idempotent.
func NewCallCredentials(configJSON json.RawMessage) (credentials.PerRPCCredentials, func(), error) {

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.

What is the point of this cleanup function? This call creds does not spawn a goroutine or allocate resources that need to be cleaned up.

Comment on lines +30 to +32
v3corepb "github.com/envoyproxy/go-control-plane/envoy/config/core/v3"
access_tokenpb "github.com/envoyproxy/go-control-plane/envoy/extensions/grpc_service/call_credentials/access_token/v3"
xdspb "github.com/envoyproxy/go-control-plane/envoy/extensions/grpc_service/channel_credentials/xds/v3"

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.

Nit: Please group proto imports in a separate block.

And please don't have introduce underscores in the renamed import.

// configuration and a trust level for the delivering xDS server.
type GrpcService struct {
config *bootstrap.Config
trusted bool

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.

The trusted bit indicates whether the xDS management server delivering a GrpcService proto is trusted or not. It does not say anything about an external service being trusted or not.

Comment on lines +104 to +132
var channelCreds bootstrap.ChannelCreds
var callCreds []bootstrap.CallCredsConfig
if g.trusted {
var err error
if channelCreds, err = extractChannelCredentials(googleGrpc.GetChannelCredentialsPlugin()); err != nil {
return Config{}, fmt.Errorf("grpcservice: failed to extract channel credentials: %v", err)
}
if callCreds, err = extractCallCredentials(googleGrpc.GetCallCredentialsPlugin()); err != nil {
return Config{}, fmt.Errorf("grpcservice: failed to extract call credentials: %v", err)
}
} else {
// For untrusted servers we ignore the credentials in the proto.
// The target must be present in the allowed_grpc_services
// allowlist, but the credentials themselves are resolved later,
// at channel creation time; they are left empty in the parsed
// config here. A nil bootstrap config has no allowlist, so all
// targets are rejected.
var allowedSvc *bootstrap.AllowedGRPCService
var ok bool
if g.config != nil {
allowedSvc, ok = g.config.AllowedGRPCService(targetURI)
}
if !ok {
return Config{}, fmt.Errorf("grpcservice: target_uri %q is not present in allowed_grpc_services", targetURI)
}
if allowedSvc == nil {
return Config{}, fmt.Errorf("grpcservice: allowed gRPC service %q has nil configuration", targetURI)
}
}

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.

Why does this distinction have to be done at parse time. At parse time, we simply parse an xDS GrpcService proto into an internal representation. Whether that internal representation gets used eventually or not depends on whether the server that delivered the resource is trusted or not.

// proto; otherwise the target URI must be present in the allowed_grpc_services
// map, and the credentials are resolved later at channel creation time and left
// empty here.
func (g *GrpcService) Parse(gs *v3corepb.GrpcService) (Config, error) {

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.

This should ideally be a function that accepts a *v3corepb.GrpcService and returns a (*Config, error). A pointer to Config is easier to work with since a nil value can be returned instead of an empty struct.

@easwars easwars removed their assignment Aug 17, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Type: Feature New features or improvements in behavior

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants