Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions internal/testutils/xds/e2e/clientresources.go
Original file line number Diff line number Diff line change
Expand Up @@ -650,6 +650,8 @@ type BackendOptions struct {
HealthStatus v3corepb.HealthStatus
// Weight sets the backend weight. Defaults to 1.
Weight uint32
// Hostname sets the endpoint hostname for authority rewriting.
Hostname string
// Metadata sets the LB endpoint metadata (envoy.lb FilterMetadata field).
// See https://www.envoyproxy.io/docs/envoy/latest/api-v3/config/core/v3/base.proto#envoy-v3-api-msg-config-core-v3-metadata
Metadata map[string]any
Expand Down Expand Up @@ -721,6 +723,7 @@ func EndpointResourceWithOptions(opts EndpointOptions) *v3endpointpb.ClusterLoad
PortSpecifier: &v3corepb.SocketAddress_PortValue{PortValue: b.Ports[0]},
},
}},
Hostname: b.Hostname,
AdditionalAddresses: additionalAddresses,
}},
HealthStatus: b.HealthStatus,
Expand Down
6 changes: 6 additions & 0 deletions internal/xds/balancer/clusterimpl/clusterimpl.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ import (
"google.golang.org/grpc/internal/xds/clients"
"google.golang.org/grpc/internal/xds/clients/lrsclient"
"google.golang.org/grpc/internal/xds/xdsclient"
"google.golang.org/grpc/internal/xds/xdsclient/xdsresource"
"google.golang.org/grpc/resolver"
"google.golang.org/grpc/serviceconfig"
)
Expand Down Expand Up @@ -420,6 +421,8 @@ type scWrapper struct {
// locality needs to be atomic because it can be updated while being read by
// the picker.
locality atomic.Pointer[clients.Locality]

Comment thread
arjan-bal marked this conversation as resolved.
Outdated
hostname string
}

func (scw *scWrapper) updateLocalityID(lID clients.Locality) {
Expand All @@ -442,6 +445,9 @@ func (b *clusterImplBalancer) NewSubConn(addrs []resolver.Address, opts balancer
}
var sc balancer.SubConn
scw := &scWrapper{}
if len(addrs) > 0 {
scw.hostname = xdsresource.HostnameFromAddress(addrs[0])
}
oldListener := opts.StateListener
opts.StateListener = func(state balancer.SubConnState) {
b.updateSubConnState(sc, state, oldListener)
Expand Down
9 changes: 9 additions & 0 deletions internal/xds/balancer/clusterimpl/picker.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import (
xdsinternal "google.golang.org/grpc/internal/xds"
"google.golang.org/grpc/internal/xds/clients"
"google.golang.org/grpc/internal/xds/xdsclient"
"google.golang.org/grpc/metadata"
"google.golang.org/grpc/status"
)

Expand Down Expand Up @@ -145,6 +146,14 @@ func (d *picker) Pick(info balancer.PickInfo) (balancer.PickResult, error) {
// If locality ID isn't found in the wrapper, an empty locality ID will
// be used.
lID = scw.localityID()

if scw.hostname != "" && autoHostRewrite(info.Ctx) {
Comment thread
arjan-bal marked this conversation as resolved.
Outdated
if pr.Metadata == nil {
pr.Metadata = metadata.Pairs(":authority", scw.hostname)
} else {
pr.Metadata.Set(":authority", scw.hostname)
}
}
}

if err != nil {
Expand Down
220 changes: 211 additions & 9 deletions internal/xds/balancer/clusterimpl/tests/balancer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,14 @@ package clusterimpl_test

import (
"context"
"crypto/tls"
"crypto/x509"
"encoding/json"
"errors"
"fmt"
"math"
"net"
"os"
"strconv"
"strings"
"sync/atomic"
Expand All @@ -38,19 +41,24 @@ import (
"google.golang.org/grpc/balancer/pickfirst"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/connectivity"
"google.golang.org/grpc/credentials"
"google.golang.org/grpc/credentials/insecure"
"google.golang.org/grpc/internal"
"google.golang.org/grpc/internal/balancer/stub"
"google.golang.org/grpc/internal/envconfig"
"google.golang.org/grpc/internal/grpctest"
"google.golang.org/grpc/internal/stubserver"
"google.golang.org/grpc/internal/testutils"
"google.golang.org/grpc/internal/testutils/xds/e2e"
"google.golang.org/grpc/internal/testutils/xds/fakeserver"
"google.golang.org/grpc/internal/xds/bootstrap"
"google.golang.org/grpc/metadata"
"google.golang.org/grpc/peer"
"google.golang.org/grpc/resolver"
"google.golang.org/grpc/resolver/manual"
"google.golang.org/grpc/serviceconfig"
"google.golang.org/grpc/status"
"google.golang.org/grpc/testdata"
"google.golang.org/protobuf/testing/protocmp"
"google.golang.org/protobuf/types/known/durationpb"
"google.golang.org/protobuf/types/known/wrapperspb"
Expand Down Expand Up @@ -95,7 +103,6 @@ func (s) TestConfigUpdateWithSameLoadReportingServerConfig(t *testing.T) {
// Create bootstrap configuration pointing to the above management server.
nodeID := uuid.New().String()
bc := e2e.DefaultBootstrapContents(t, nodeID, mgmtServer.Address)
testutils.CreateBootstrapFileForTesting(t, bc)

// Create an xDS resolver with the above bootstrap configuration.
if internal.NewXDSResolverWithConfigForTesting == nil {
Expand Down Expand Up @@ -377,7 +384,6 @@ func (s) TestCircuitBreaking(t *testing.T) {
// Create bootstrap configuration pointing to the above management server.
nodeID := uuid.New().String()
bc := e2e.DefaultBootstrapContents(t, nodeID, mgmtServer.Address)
testutils.CreateBootstrapFileForTesting(t, bc)

// Create an xDS resolver with the above bootstrap configuration.
if internal.NewXDSResolverWithConfigForTesting == nil {
Expand Down Expand Up @@ -574,7 +580,6 @@ func (s) TestDropByCategory(t *testing.T) {
// Create bootstrap configuration pointing to the above management server.
nodeID := uuid.New().String()
bc := e2e.DefaultBootstrapContents(t, nodeID, mgmtServer.Address)
testutils.CreateBootstrapFileForTesting(t, bc)

// Create an xDS resolver with the above bootstrap configuration.
if internal.NewXDSResolverWithConfigForTesting == nil {
Expand Down Expand Up @@ -717,7 +722,6 @@ func (s) TestCircuitBreakingLogicalDNS(t *testing.T) {
// Create bootstrap configuration pointing to the above management server.
nodeID := uuid.New().String()
bc := e2e.DefaultBootstrapContents(t, nodeID, mgmtServer.Address)
testutils.CreateBootstrapFileForTesting(t, bc)

// Create an xDS resolver with the above bootstrap configuration.
if internal.NewXDSResolverWithConfigForTesting == nil {
Expand Down Expand Up @@ -836,7 +840,6 @@ func (s) TestLRSLogicalDNS(t *testing.T) {
// Create bootstrap configuration pointing to the above management server.
nodeID := uuid.New().String()
bc := e2e.DefaultBootstrapContents(t, nodeID, mgmtServer.Address)
testutils.CreateBootstrapFileForTesting(t, bc)

// Create an xDS resolver with the above bootstrap configuration.
if internal.NewXDSResolverWithConfigForTesting == nil {
Expand Down Expand Up @@ -928,7 +931,6 @@ func (s) TestReResolutionAfterTransientFailure(t *testing.T) {
// Create bootstrap configuration pointing to the above management server.
nodeID := uuid.New().String()
bc := e2e.DefaultBootstrapContents(t, nodeID, mgmtServer.Address)
testutils.CreateBootstrapFileForTesting(t, bc)

// Create an xDS resolver with the above bootstrap configuration.
if internal.NewXDSResolverWithConfigForTesting == nil {
Expand Down Expand Up @@ -1048,7 +1050,6 @@ func (s) TestUpdateLRSServerToNil(t *testing.T) {
// Create bootstrap configuration pointing to the above management server.
nodeID := uuid.New().String()
bc := e2e.DefaultBootstrapContents(t, nodeID, mgmtServer.Address)
testutils.CreateBootstrapFileForTesting(t, bc)

// Create an xDS resolver with the above bootstrap configuration.
if internal.NewXDSResolverWithConfigForTesting == nil {
Expand Down Expand Up @@ -1135,7 +1136,6 @@ func (s) TestChildPolicyChangeOnConfigUpdate(t *testing.T) {
// Create bootstrap configuration pointing to the above management server.
nodeID := uuid.New().String()
bc := e2e.DefaultBootstrapContents(t, nodeID, mgmtServer.Address)
testutils.CreateBootstrapFileForTesting(t, bc)

// Create an xDS resolver with the above bootstrap configuration.
if internal.NewXDSResolverWithConfigForTesting == nil {
Expand Down Expand Up @@ -1258,7 +1258,6 @@ func (s) TestFailedToParseChildPolicyConfig(t *testing.T) {
// Create bootstrap configuration pointing to the above management server.
nodeID := uuid.New().String()
bc := e2e.DefaultBootstrapContents(t, nodeID, mgmtServer.Address)
testutils.CreateBootstrapFileForTesting(t, bc)

// Create an xDS resolver with the above bootstrap configuration.
if internal.NewXDSResolverWithConfigForTesting == nil {
Expand Down Expand Up @@ -1316,3 +1315,206 @@ func (s) TestFailedToParseChildPolicyConfig(t *testing.T) {
t.Fatal("EmptyCall RPC succeeded when expected to fail")
}
}

// setupManagementServerAndResolver sets up an xDS management server and returns
// the management server, resolver builder and Node ID.
func setupManagementServerAndResolver(t *testing.T) (*e2e.ManagementServer, resolver.Builder, string) {
t.Helper()

nodeID := uuid.New().String()
mgmtServer := e2e.StartManagementServer(t, e2e.ManagementServerOptions{})

opts := bootstrap.ConfigOptionsForTesting{
Servers: []byte(fmt.Sprintf(`[{
"server_uri": %q,
"channel_creds": [{"type": "insecure"}],
"server_features": ["trusted_xds_server"]
}]`, mgmtServer.Address)),
Node: []byte(fmt.Sprintf(`{"id": "%s"}`, nodeID)),
}

contents, err := bootstrap.NewContentsForTesting(opts)
if err != nil {
t.Fatalf("Failed to create bootstrap configuration: %v", err)
}

// Create an xDS resolver with the above bootstrap configuration.
if internal.NewXDSResolverWithConfigForTesting == nil {
t.Fatalf("internal.NewXDSResolverWithConfigForTesting is 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.

nit: We can omit this check and let the test panic. This function is not expected to be nil so we don't need to check for it.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

We have been using this nil check in all test. Do you want me remove it from everywhere?

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.

I'm fine with removing the nil check from the other tests, though it's not strictly necessary.

resolverBuilder, err := internal.NewXDSResolverWithConfigForTesting.(func([]byte) (resolver.Builder, error))(contents)
if err != nil {
t.Fatalf("Failed to create xDS resolver for testing: %v", err)
}

return mgmtServer, resolverBuilder, nodeID
}

// configureXDSResources configures the management server with a route that
// enables auto_host_rewrite and an endpoint with the specified hostname.
func configureXDSResources(ctx context.Context, t *testing.T, mgmtServer *e2e.ManagementServer, nodeID string, serverAddr string, endpointHostname string) {
t.Helper()

const (
serviceName = "my-test-xds-service"
routeName = "route-my-test-xds-service"
clusterName = "cluster-my-test-xds-service"
endpointName = "endpoints-my-test-xds-service"
)

resources := e2e.DefaultClientResources(e2e.ResourceParams{
DialTarget: serviceName,
NodeID: nodeID,
Host: "localhost",
Port: testutils.ParsePort(t, serverAddr),
SecLevel: e2e.SecurityLevelNone,
})

// Set the endpoint hostname for authority rewriting.
resources.Endpoints[0].Endpoints[0].LbEndpoints[0].GetEndpoint().Hostname = endpointHostname

// Modify the route to enable AutoHostRewrite.
resources.Routes[0].VirtualHosts[0].Routes[0].GetRoute().HostRewriteSpecifier = &v3routepb.RouteAction_AutoHostRewrite{
AutoHostRewrite: &wrapperspb.BoolValue{Value: true},
}

if err := mgmtServer.Update(ctx, resources); err != nil {
t.Fatal(err)
}
}

// TestAuthorityOverriding verifies that the :authority header is correctly
// rewritten to the endpoint's hostname. Also verifies that CallAuthority
// call option takes precedence.
func (s) TestAuthorityOverriding(t *testing.T) {
testutils.SetEnvConfig(t, &envconfig.XDSAuthorityRewrite, true)
mgmtServer, resolverBuilder, nodeID := setupManagementServerAndResolver(t)

// Start a server backend exposing the test service.
authorityCh := make(chan string, 1)
Comment thread
arjan-bal marked this conversation as resolved.
Outdated
f := &stubserver.StubServer{
EmptyCallF: func(ctx context.Context, _ *testpb.Empty) (*testpb.Empty, error) {
if md, ok := metadata.FromIncomingContext(ctx); ok {
if authVals := md.Get(":authority"); len(authVals) > 0 {
authorityCh <- authVals[0]
}
}
return &testpb.Empty{}, nil
},
}
server := stubserver.StartTestService(t, f)
defer server.Stop()

const xdsAuthorityOverride = "rewritten.example.com"

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: nix newline

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Done.

ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
defer cancel()
configureXDSResources(ctx, t, mgmtServer, nodeID, server.Address, xdsAuthorityOverride)

// Create a ClientConn and make a successful RPC.
cc, err := grpc.NewClient("xds:///my-test-xds-service", grpc.WithTransportCredentials(insecure.NewCredentials()), grpc.WithResolvers(resolverBuilder))
if err != nil {
t.Fatalf("Failed to create client: %v", err)
}
defer cc.Close()

client := testgrpc.NewTestServiceClient(cc)
if _, err := client.EmptyCall(ctx, &testpb.Empty{}); err != nil {
t.Fatalf("client.EmptyCall() failed: %v", err)
}

select {
case gotAuth := <-authorityCh:
if gotAuth != xdsAuthorityOverride {
t.Errorf("invalid authority got: %q, want: %q", gotAuth, xdsAuthorityOverride)
}
case <-ctx.Done():
t.Fatalf("Timeout waiting for successful RPC after authority rewriting.")
}

// The authority specified via the `CallAuthority` CallOption takes the
// highest precedence when determining the `:authority` header.
userAuthorityOverride := "user-override.com"

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.

Make this const?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Done.

if _, err := client.EmptyCall(ctx, &testpb.Empty{}, grpc.CallAuthority(userAuthorityOverride)); err != nil {
t.Fatalf("client.EmptyCall() failed: %v", err)
}

select {
case got := <-authorityCh:
if got != userAuthorityOverride {
t.Errorf("Server received authority %q, want %q (user override)", got, userAuthorityOverride)
}
case <-ctx.Done():
t.Fatalf("Timeout waiting for successful RPC.")
}
}

// TestAuthorityOverridingWithTLS verifies the interaction between xDS Authority
// Rewriting and TLS Secure Naming. It ensures that when the :authority header
// is rewritten by the clusterimpl picker, the new authority is correctly
// validated against the server's TLS certificate before the RPC proceeds.
func (s) TestAuthorityOverridingWithTLS(t *testing.T) {

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.

I think what you might need is the following:

  • A server TLS credentials, where the certificate is presented for a domain name like x.test.exmaple.com
  • A client TLS credentials, where the roots are set to x509/server_ca_cert.pem, and the serverNameOverride is set to something like *.test.example.com
  • Send an xDS resource where the authority overwrite is set to x.test.exmaple.com and verify the connection works
  • Send an xDS resource where the authority overwrite is set to y.test.exmaple.com and verify the connection fails. This failure should be because the TLS handshake fails.
  • Send an xDS resource where the authority overwrite is set to xyz.exmaple.com and verify the connection fails. This failure should be because the TLS creds rejects the authority override.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I have updated the test and check both cases with valid authority and invalid authorityxyz.exmaple.com.

Send an xDS resource where the authority overwrite is set to y.test.exmaple.com and verify the connection fails. This failure should be because the TLS handshake fails.

Test is using the xdsCreds which will use InsecureSkipVerify while configuring tlsConfig to skip the tls handshake check. So, I don't think we can test that case now.

@arjan-bal arjan-bal Jan 2, 2026

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.

I don't think the authority override configuration should/will cause TLS handshakes to fail. The authority header is part of the HTTP/2 header frame that is sent after the transport credentials have already completed hanshaking.

The interaction b/w the transport credentials and the authority override is this: The transport credentials must validate the authority override header:

if callHdr.Authority != "" {
auth, ok := t.authInfo.(credentials.AuthorityValidator)
if !ok {
return nil, &NewStreamError{Err: status.Errorf(codes.Unavailable, "credentials type %q does not implement the AuthorityValidator interface, but authority override specified with CallAuthority call option", t.authInfo.AuthType())}
}
if err := auth.ValidateAuthority(callHdr.Authority); err != nil {
return nil, &NewStreamError{Err: status.Errorf(codes.Unavailable, "failed to validate authority %q : %v", callHdr.Authority, err)}
}
newCallHdr := *callHdr
newCallHdr.Host = callHdr.Authority
callHdr = &newCallHdr
}

When using xDS, we don't directly use TLS credentials on the client. Instead we use xdscredentials. The config for TLS needs to be set in the CDS resource.

See the following test as an example of configuring TLS using xDS:

func (s) TestGoodSecurityConfig(t *testing.T) {

On the server side, we can use the regular tlscreds like the CDS test above. We can can also use the xds creds and configure TLS in the LDS resource, similar to the following test:

// TestServerSideXDS_FileWatcherCerts is an e2e test which verifies xDS
// credentials with file watcher certificate provider.
//
// The following sequence of events happen as part of this test:
// - An xDS-enabled gRPC server is created and xDS credentials are configured.
// - xDS is enabled on the client by the use of the xds:/// scheme, and xDS
// credentials are configured.
// - Control plane is configured to send security configuration to both the
// client and the server, pointing to the file watcher certificate provider.
// We verify both TLS and mTLS scenarios.
func (s) TestServerSideXDS_FileWatcherCerts(t *testing.T) {


It looks like the xDS creds don't implement the AuthorityValidator interface, so authority overriding will probably fail when using TLS with xDS.

// reloadingCreds is a credentials.TransportCredentials for client
// side mTLS that reloads the server root CA certificate and the client
// certificates from the provider on every client handshake. This is necessary
// because the standard TLS credentials do not support reloading CA
// certificates.
type reloadingCreds struct {
provider certprovider.Provider
}
func (c *reloadingCreds) ClientHandshake(ctx context.Context, authority string, rawConn net.Conn) (net.Conn, credentials.AuthInfo, error) {
km, err := c.provider.KeyMaterial(ctx)
if err != nil {
return nil, nil, err
}
var config *tls.Config
if km.SPIFFEBundleMap != nil {
config = &tls.Config{
InsecureSkipVerify: true,
VerifyPeerCertificate: buildSPIFFEVerifyFunc(km.SPIFFEBundleMap),
Certificates: km.Certs,
}
} else {
config = &tls.Config{
RootCAs: km.Roots,
Certificates: km.Certs,
}
}
return credentials.NewTLS(config).ClientHandshake(ctx, authority, rawConn)
}
func (c *reloadingCreds) Info() credentials.ProtocolInfo {
return credentials.ProtocolInfo{SecurityProtocol: "tls"}
}
func (c *reloadingCreds) Clone() credentials.TransportCredentials {
return &reloadingCreds{provider: c.provider}
}
func (c *reloadingCreds) OverrideServerName(string) error {
return errors.New("overriding server name is not supported by xDS client TLS credentials")
}
func (c *reloadingCreds) ServerHandshake(net.Conn) (net.Conn, credentials.AuthInfo, error) {
return nil, nil, errors.New("server handshake is not supported by xDS client TLS credentials")
}

We would need to update the reloadingCreds to generate the required TLS credentials, similar to the ClientHandshake method and call ValidateAuthority on the tls creds object for things to work.

Once the xDS creds implement the AuthorityValidator interface, this test should use the CDS config along with xds credentials to test the authority override behaviour.

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.

@Pranjali-2501 apologies for the confusion. I misunderstood and thought that Credentials needed to implement the AuthorityValidator interface, but it is actually AuthInfo. Since XDSCredentials.ClientHandshake delegates to the TLS credentials (which return TLSAuthInfo), AuthorityValidator is effectively implemented when using xDS. The existing test seems correct.

testutils.SetEnvConfig(t, &envconfig.XDSAuthorityRewrite, true)
mgmtServer, resolverBuilder, nodeID := setupManagementServerAndResolver(t)

serverCert, err := tls.LoadX509KeyPair(testdata.Path("x509/server1_cert.pem"), testdata.Path("x509/server1_key.pem"))
if err != nil {
t.Fatalf("Failed to load server key pair: %v", err)
}

pemData, err := os.ReadFile(testdata.Path("x509/client_ca_cert.pem"))

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.

We have functions that do all this work of reading from a file etc, right? Somewhere in testutils?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Right, made the changes.

if err != nil {
t.Fatalf("Failed to read client CA cert: %v", err)
}
roots := x509.NewCertPool()
roots.AppendCertsFromPEM(pemData)
tlsConfig := &tls.Config{
Certificates: []tls.Certificate{serverCert},
ClientCAs: roots,

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.

I don't understand this. Why is the client presenting the serverCert and using x509/client_ca_cert.pem as the root cert to validate the server?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

This was not correct.

I have updated the test to use xdsCreds instead of directly using tlsConfig.

InsecureSkipVerify: true,

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 do you need to set this to true?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Update the test to use xdsCreds instead of tlsCreds.

}
clientCreds := credentials.NewTLS(tlsConfig)

// Start a server backend exposing the test service.
authorityCh := make(chan string, 1)
f := &stubserver.StubServer{
EmptyCallF: func(ctx context.Context, _ *testpb.Empty) (*testpb.Empty, error) {
if md, ok := metadata.FromIncomingContext(ctx); ok {
if authVals := md.Get(":authority"); len(authVals) > 0 {
authorityCh <- authVals[0]
}
}
return &testpb.Empty{}, nil
},
}
if err := f.StartServer(grpc.Creds(credentials.NewServerTLSFromCert(&serverCert))); err != nil {
t.Fatalf("Failed to start the server: %v", err)
}
defer f.Stop()

ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
defer cancel()
const xdsAuthorityOverride = "x.test.example.com"
configureXDSResources(ctx, t, mgmtServer, nodeID, f.Address, xdsAuthorityOverride)

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.

Can we also actually check if the RPC fails when:

  • the host name override is not specified (as the test does not specify a serverNameOverride in the TLS creds)
  • the host name override is invalid (i.e., is expected to be rejected by the TLS creds)


// Create ClientConn with TLS
cc, err := grpc.NewClient("xds:///my-test-xds-service", grpc.WithTransportCredentials(clientCreds), grpc.WithResolvers(resolverBuilder))
if err != nil {
t.Fatalf("Failed to create client: %v", err)
}
defer cc.Close()

client := testgrpc.NewTestServiceClient(cc)
if _, err := client.EmptyCall(ctx, &testpb.Empty{}); err != nil {
t.Fatalf("client.EmptyCall() failed: %v", err)
}

select {
case gotAuth := <-authorityCh:
if gotAuth != xdsAuthorityOverride {
t.Errorf("invalid authority got: %q, want: %q", gotAuth, xdsAuthorityOverride)
}
case <-ctx.Done():
t.Fatalf("Timeout waiting for successful RPC after authority rewriting.")
}
}
Loading