Skip to content

XDS configured client not setting :authority for TLS handshake #11750

@dvilaverde

Description

@dvilaverde

What version of gRPC-Java are you using?

<grpc.version>1.69.0</grpc.version>

What is your environment?

macOS 15.1.1

java version "21.0.5" 2024-10-15 LTS
Java(TM) SE Runtime Environment (build 21.0.5+9-LTS-239)
Java HotSpot(TM) 64-Bit Server VM (build 21.0.5+9-LTS-239, mixed mode, sharing)

What did you expect to see?

When using grpc-java client with XDS configuration I was expecting the client request to succeed but instead it failed. The only way the client application will work is when I manually set the authority via channelBuilder.overrideAuthority(), but doing this doesn't seem to make sense since the GRPC client now needs to know something I feel should have been provided in the XDS response with the endpoints.

Note that I've enabled:

  • thetrusted_xds_server server feature in the bootstrap.json
  • set the feature flag GRPC_EXPERIMENTAL_XDS_AUTHORITY_REWRITE to true
  • and returned auto_host_rewrite: true in the RouteMatch for that virtual host.

Since this GRPC server is behind an envoy that is handling multiple TLS certificates the SNI information needs to be sent via the TLS handshake, and I think that is what may be happening here. Is there something that I need to do in order to send :authority as SNI info either in the XDS response or during the ManagedChannel setup?

What did you see instead?

Exception in thread "main" io.grpc.StatusRuntimeException: UNAVAILABLE: io exception
Channel Pipeline: [SslHandler#0, ProtocolNegotiators$ClientTlsHandler#0, WriteBufferingAndExceptionHandler#0, DefaultChannelPipeline$TailContext#0]
	at io.grpc.stub.ClientCalls.toStatusRuntimeException(ClientCalls.java:268)
	at io.grpc.stub.ClientCalls.getUnchecked(ClientCalls.java:249)
	at io.grpc.stub.ClientCalls.blockingUnaryCall(ClientCalls.java:167)
	at com.example.service.v1.LookupGrpc$LookupBlockingStub.resolve(LookupGrpc.java:160)
	at com.example.adc.App.main(App.java:38)
Caused by: java.net.SocketException: Connection reset
	at java.base/sun.nio.ch.SocketChannelImpl.throwConnectionReset(SocketChannelImpl.java:394)
	at java.base/sun.nio.ch.SocketChannelImpl.read(SocketChannelImpl.java:426)
	at io.grpc.netty.shaded.io.netty.buffer.PooledByteBuf.setBytes(PooledByteBuf.java:255)
	at io.grpc.netty.shaded.io.netty.buffer.AbstractByteBuf.writeBytes(AbstractByteBuf.java:1132)
	at io.grpc.netty.shaded.io.netty.channel.socket.nio.NioSocketChannel.doReadBytes(NioSocketChannel.java:356)
	at io.grpc.netty.shaded.io.netty.channel.nio.AbstractNioByteChannel$NioByteUnsafe.read(AbstractNioByteChannel.java:151)
	at io.grpc.netty.shaded.io.netty.channel.nio.NioEventLoop.processSelectedKey(NioEventLoop.java:788)
	at io.grpc.netty.shaded.io.netty.channel.nio.NioEventLoop.processSelectedKeysOptimized(NioEventLoop.java:724)
	at io.grpc.netty.shaded.io.netty.channel.nio.NioEventLoop.processSelectedKeys(NioEventLoop.java:650)
	at io.grpc.netty.shaded.io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:562)
	at io.grpc.netty.shaded.io.netty.util.concurrent.SingleThreadEventExecutor$4.run(SingleThreadEventExecutor.java:994)
	at io.grpc.netty.shaded.io.netty.util.internal.ThreadExecutorMap$2.run(ThreadExecutorMap.java:74)
	at io.grpc.netty.shaded.io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)
	at java.base/java.lang.Thread.run(Thread.java:842)

Steps to reproduce the bug

public class App 
{

    public static void main( String[] args )
    {
        String target = "xds:///cluster1:443";

        ChannelCredentials credentials = TlsChannelCredentials.create();
        ManagedChannelBuilder<?> channelBuilder = Grpc.newChannelBuilder(target, credentials);

        // NOTE: Client call will fail unless this is enabled.
//        channelBuilder.overrideAuthority("cluster1.us-east-1.example.com");

        final LookupGrpc.LookupBlockingStub blockingStub = LookupGrpc.newBlockingStub(channelBuilder.build());

        Request req = Request.newBuilder().addIps(IP.newBuilder()
            .setType(IPAddressType.IPV4)
            .setOriginAddress("8.8.8.8")).build();
        final Response result = blockingStub.resolve(req);
        if (result != null) {
          try {
              System.out.println(JsonFormat.printer().print(resolve));
          } catch (InvalidProtocolBufferException e) {
            throw new RuntimeException(e);
          }
        }
    }
}

my bootstrap.json looks like this:

{
  "client_default_listener_resource_name_template": "%s",
  "xds_servers": [
    {
      "server_uri": "localhost:8080",
      "channel_creds": [
        {
          "type": "insecure"
        }
      ],
      "server_features": [
        "xds_v3",
        "trusted_xds_server"
      ]
    }
  ],
  "cluster": "example-client-cluster",
  "node": {
    "id": "app1",
    "locality": { "region": "us-west-1" },
    "metadata": {}
  }
}

This is the ADS response from the XDS server:

Listener

- address:
    socket_address:
      address: 0.0.0.0
      port_value: 9999
  api_listener:
    api_listener:
      '@type': type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager
      http_filters:
      - name: envoy.filters.http.router
        typed_config:
          '@type': type.googleapis.com/envoy.extensions.filters.http.router.v3.Router
          suppress_envoy_headers: true
      rds:
        config_source:
          ads: {}
          resource_api_version: V3
        route_config_name: cluster1_route_config
      stat_prefix: cluster1
  name: cluster1:443

RouteConfiguration

- name: cluster1_route_config
  virtual_hosts:
  - domains:
    - cluster1.us-east-1.example.com
    name: default
    routes:
    - match:
        prefix: ""
      name: default_route
      route:
        auto_host_rewrite: true
        cluster: cluster1

Clusters

- eds_cluster_config:
    eds_config:
      ads: {}
      resource_api_version: V3
  name: cluster1
  type: EDS

Endpoints

- cluster_name: cluster1
  endpoints:
  - lb_endpoints:
    - endpoint:
        address:
          socket_address:
            address: 12.34.45.56
            port_value: 443
        hostname: cluster1.us-east-1.example.com
    - endpoint:
        address:
          socket_address:
            address: 12.34.45.57
            port_value: 443
        hostname: cluster1.us-east-1.example.com
    load_balancing_weight: 1
    locality:
      region: us-west-1

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions