From 13e26a4ca2279d26f8d6a96bc7eacae0080543da Mon Sep 17 00:00:00 2001 From: Gyuho Lee Date: Fri, 19 Jul 2019 08:35:58 -0700 Subject: [PATCH] clientv3/credentials: set dial target "Authority" with target address Overwrite authority when it's IP. When user dials with "grpc.WithDialer", "grpc.DialContext" "cc.parsedTarget" update only happens once. This is problematic, because when TLS is enabled, retries happen through "grpc.WithDialer" with static "cc.parsedTarget" from the initial dial call. If the server authenticates by IP addresses, we want to set a new endpoint as a new authority. Otherwise "transport: authentication handshake failed: x509: certificate is valid for 127.0.0.1, 192.168.121.180, not 192.168.223.156" when the new dial target is "192.168.121.180" whose certificate host name is also "192.168.121.180" but client tries to authenticate with previously set "cc.parsedTarget" field "192.168.223.156" Signed-off-by: Gyuho Lee --- clientv3/credentials/credentials.go | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/clientv3/credentials/credentials.go b/clientv3/credentials/credentials.go index f00a196c08a2..23894a06a38f 100644 --- a/clientv3/credentials/credentials.go +++ b/clientv3/credentials/credentials.go @@ -71,6 +71,27 @@ func newTransportCredential(cfg *tls.Config) *transportCredential { } func (tc *transportCredential) ClientHandshake(ctx context.Context, authority string, rawConn net.Conn) (net.Conn, grpccredentials.AuthInfo, error) { + // only overwrite when authority is an IP address! + // Let's say, a server runs SRV records on "etcd.local" that resolves + // to "m1.etcd.local", and its SAN field also includes "m1.etcd.local". + // But what if SAN does not include its resolved IP address (e.g. 127.0.0.1)? + // Then, the server should only authenticate using its DNS hostname "m1.etcd.local", + // instead of overwriting it with its IP address. + if net.ParseIP(authority) != nil { + target := rawConn.RemoteAddr().String() + if authority != target { + // When user dials with "grpc.WithDialer", "grpc.DialContext" "cc.parsedTarget" + // update only happens once. This is problematic, because when TLS is enabled, + // retries happen through "grpc.WithDialer" with static "cc.parsedTarget" from + // the initial dial call. + // If the server authenticates by IP addresses, we want to set a new endpoint as + // a new authority. Otherwise + // "transport: authentication handshake failed: x509: certificate is valid for 127.0.0.1, 192.168.121.180, not 192.168.223.156" + // when the new dial target is "192.168.121.180" whose certificate host name is also "192.168.121.180" + // but client tries to authenticate with previously set "cc.parsedTarget" field "192.168.223.156" + authority = target + } + } return tc.gtc.ClientHandshake(ctx, authority, rawConn) }