[grpc-java] using NameResolver for grpc servers with TLS

1,549 views
Skip to first unread message

Jorg Heymans

unread,
Jan 26, 2017, 7:13:07 AM1/26/17
to grpc.io
Hi,

I have a grpc client-server setup using TLS (ClientAuth.REQUIRE) which is working fine. I am now trying to implement service discovery using Spring DiscoveryClient and zookeeper, very much like what was done here for eureka https://gist.github.com/Xorlev/eafce32667931b78fac003f228cedc53#file-eurekanameresolver-java-L51

Basically in the client I am creating a channel.forTarget("zookeeper://myservice-dev").nameResolverFactory(...).sslContext(...) and i get this exception serverside:

Caused by: java.security.cert.CertificateException: No subject alternative DNS name matching myservice-dev found.
at sun.security.util.HostnameChecker.matchDNS(HostnameChecker.java:204)
at sun.security.util.HostnameChecker.match(HostnameChecker.java:95)
at sun.security.ssl.X509TrustManagerImpl.checkIdentity(X509TrustManagerImpl.java:455)
at sun.security.ssl.X509TrustManagerImpl.checkIdentity(X509TrustManagerImpl.java:436)
at sun.security.ssl.X509TrustManagerImpl.checkTrusted(X509TrustManagerImpl.java:252)
at sun.security.ssl.X509TrustManagerImpl.checkServerTrusted(X509TrustManagerImpl.java:136)
at io.netty.handler.ssl.ReferenceCountedOpenSslClientContext$ExtendedTrustManagerVerifyCallback.verify(ReferenceCountedOpenSslClientContext.java:223)

It seems that the logical service id i am trying to connect to is used during TLS verification. Is this expected ? Adding this service id in the certificate SubjectAltNames makes things work but that's not a real solution. I have verified that the service discovery works fine without TLS and it does.

Any ideas what could be causing this, is it expected at all ?

Many thanks,
Jorg

Jorg Heymans

unread,
Jan 26, 2017, 8:17:14 AM1/26/17
to grpc.io
Small mistake in my mail, the stacktrace is shown clientside NOT serverside. 

Jorg

Jorg Heymans

unread,
Jan 26, 2017, 9:46:08 AM1/26/17
to grpc.io
Hi,

The error is caused by my NameResolver implementation returning the service id as authority:

public class DiscoveryClientNameResolver extends NameResolver {

  private DiscoveryClient discoveryClient;
  private final String name;
  private Listener listener;

  public DiscoveryClientNameResolver(String name, DiscoveryClient discoveryClient) {
    this.name = name;
    this.discoveryClient = discoveryClient;
  }

  @Override
  public String getServiceAuthority() {
    return name;
  }

  @Override
  public void start(Listener listener) {
    this.listener = listener;
    refresh();
  }

  @Override
  public void refresh() {
    List<ResolvedServerInfo> servers = new ArrayList<>();
    for (ServiceInstance serviceInstance : discoveryClient.getInstances(name)) {
      if (serviceInstance.getMetadata().get("type").equals("grpc")) {
        servers.add(new ResolvedServerInfo(
            InetSocketAddress.createUnresolved(serviceInstance.getHost(), serviceInstance.getPort()),
            Attributes.EMPTY));
      }
    }
    Collections.shuffle(servers);
    this.listener.onUpdate(Collections.singletonList(servers), Attributes.EMPTY);
  }

  @Override
  public void shutdown() {
  }
}


My question then becomes how the NameResolver is supposed to return the correct authority for the connection in progress so that TLS verification succeeds ? At the ClientInterceptor level i do not seem to have access to this information.

Jorg

Carl Mastrangelo

unread,
Jan 26, 2017, 2:52:54 PM1/26/17
to grpc.io
Name resolution happens before the connection, so I'm not sure what you mean by "connection in progress".  Could you clarify?  

Also, ClientInterceptors work on an RPC level, not a connection level.  You can change the authority there, but thats mainly for reducing connections that go through the same backend, but are logically different.   

kan...@squareup.com

unread,
Jan 26, 2017, 3:00:45 PM1/26/17
to grpc.io
FYI: I got a similar error.. https://github.com/grpc/grpc-java/pull/2662 explains our configuration. 

Jorg Heymans

unread,
Jan 26, 2017, 4:39:23 PM1/26/17
to grpc.io


On Thursday, January 26, 2017 at 8:52:54 PM UTC+1, Carl Mastrangelo wrote:
Name resolution happens before the connection, so I'm not sure what you mean by "connection in progress".  Could you clarify?  

Well since the authority() returned by the NameResolver is used during setup of the SSLEngine (what i call "connection in progress aka handshake) and subsequentl verification of the certificates, i thought to try and figure out in the interceptor what host:port exactly is being used from the list of servers that was returned through service discovery. Then i would set this as a threadlocal for the NameResolver to pick it up. But this seems not the right way as i get from your comment. 

For the moment to get it to work i have added the service name to the certificates. It seems that  https://github.com/grpc/grpc-java/pull/2662  as replied in the other email is exactly the problem i am having.

Thanks,
Jorg

peep...@gmail.com

unread,
Aug 1, 2017, 4:05:54 AM8/1/17
to grpc.io
Hi All,

I'm trying to use name resolution and lb feature of gRPC for client side load balancing. However, it is not working properly.
Below is the construction of name resolution factory.

public Factory getNameResolverFactory() {

final Attributes NAME_RESOLVER_PARAMS = Attributes.newBuilder()
.set(GrpcNameResolutionLBConstant.RESOLUTION_ATTR, "yeah")
.build();
Attributes attrs = Attributes.newBuilder()
.set(GrpcNameResolutionLBConstant.ATTR_LB_ADDR_AUTHORITY, Constant.HOST + ":" + Constant.PORT)
.build();
final ArrayList EAG = new ArrayList();
SocketAddress addr = new InetSocketAddress(Constant.HOST, Constant.PORT);
EquivalentAddressGroup addrgrp = new EquivalentAddressGroup(addr, attrs);
EAG.add(addrgrp);

    final NameResolver.Listener nrlistener = null;

    Factory nameResolverFactory = new NameResolver.Factory() {
        @Override
        public NameResolver newNameResolver(URI targetUri, Attributes params) {
            try {
                targetUri = URI.create(Constant.HOST + ":" + Constant.PORT);
                params = NAME_RESOLVER_PARAMS;
            } catch (Exception e) {
                logger.log(Level.SEVERE, "Error: " + e);
            }
            NameResolver nrslvr = new NameResolver() {
                @Override
                public String getServiceAuthority() {
                    return Constant.HOST + ":" + Constant.PORT;
                }

                @Override
                public void start(NameResolver.Listener listener) {
                    listener = new NameResolver.Listener() {
                        public void onUpdate(List<ResolvedServerInfoGroup> servers, Attributes attributes) {
                            throw new UnsupportedOperationException("Not supported yet.");
                        }

                        public void onAddresses(List<EquivalentAddressGroup> servers, Attributes attributes) {
                            servers = EAG;
                            attributes = NAME_RESOLVER_PARAMS;
                        }

                        public void onError(Status error) {
                            logger.log(Level.SEVERE, "onError called: " + error);
                        }
                    };
                    listener.onAddresses(EAG, NAME_RESOLVER_PARAMS);
                }

                @Override
                public void shutdown() {
                    throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
                }
            };
            nrslvr.start(nrlistener);
            return nrslvr;
        }

        @Override
        public String getDefaultScheme() {
            return "defaultscheme";
        }
    };
    return nameResolverFactory;
}

Alongwith name resolution, using rrlb for load balancing.
RoundRobinLoadBalancerFactory.getInstance()

Things are working fine when I exclude nameResolverFactory.

Can someone help me?

P.S. Using NettyChannelBuilder.
Reply all
Reply to author
Forward
0 new messages