xds: Implement GrpcService parsing and shared side channels (gRFC A102) - #9319
xds: Implement GrpcService parsing and shared side channels (gRFC A102)#9319mbissa wants to merge 4 commits into
Conversation
Codecov Report❌ Patch coverage is 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
🚀 New features to boost your workflow:
|
|
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. |
d75cfcc to
40472da
Compare
…nn target parsing
| } | ||
| // 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) |
There was a problem hiding this comment.
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 { |
There was a problem hiding this comment.
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 { |
There was a problem hiding this comment.
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 |
There was a problem hiding this comment.
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) { |
There was a problem hiding this comment.
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) { |
There was a problem hiding this comment.
Should this be a table driven test ?
easwars
left a comment
There was a problem hiding this comment.
Haven't made a full pass, but have enough to move this forward for now.
There was a problem hiding this comment.
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.
| if !ok || credentials.CheckSecurityLevel(ri.AuthInfo, credentials.PrivacyAndIntegrity) != nil { | ||
| return nil, nil | ||
| } |
There was a problem hiding this comment.
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 |
There was a problem hiding this comment.
This should return true.
Note that the token will not be sent on the wire unless the connection has security level PRIVACY_AND_INTEGRITY.
| // 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. |
There was a problem hiding this comment.
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 |
There was a problem hiding this comment.
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) { |
There was a problem hiding this comment.
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.
| 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" |
There was a problem hiding this comment.
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 |
There was a problem hiding this comment.
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.
| 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) | ||
| } | ||
| } |
There was a problem hiding this comment.
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) { |
There was a problem hiding this comment.
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.
Stacked on #9315 and #9194 — only the last 2 commits are new here.
Implements gRFC A102
GrpcServicesupport, wired into ext_proc as the first consumer:internal/xds/grpcservicepackage: parses theGrpcServiceproto 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 bootstrapallowed_grpc_servicesallowlist (NACK otherwise).ClientFilterOptions.access_tokencall credentials (token sent only atPrivacyAndIntegrity).RELEASE NOTES: