credentials: fix behavior of grpc.WithAuthority and credential handshake precedence (#8488)
diff --git a/clientconn.go b/clientconn.go
index d2717fb..3f76228 100644
--- a/clientconn.go
+++ b/clientconn.go
@@ -1824,7 +1824,7 @@
} else if auth, ok := cc.resolverBuilder.(resolver.AuthorityOverrider); ok {
cc.authority = auth.OverrideAuthority(cc.parsedTarget)
} else if strings.HasPrefix(endpoint, ":") {
- cc.authority = "localhost" + endpoint
+ cc.authority = "localhost" + encodeAuthority(endpoint)
} else {
cc.authority = encodeAuthority(endpoint)
}
diff --git a/credentials/alts/alts_test.go b/credentials/alts/alts_test.go
index 4710a32..dba11df 100644
--- a/credentials/alts/alts_test.go
+++ b/credentials/alts/alts_test.go
@@ -153,9 +153,6 @@
// use NewServerCreds and not NewClientCreds.
c := NewServerCreds(DefaultServerOptions())
info := c.Info()
- if got, want := info.ProtocolVersion, ""; got != want {
- t.Errorf("info.ProtocolVersion=%v, want %v", got, want)
- }
if got, want := info.SecurityProtocol, "alts"; got != want {
t.Errorf("info.SecurityProtocol=%v, want %v", got, want)
}
diff --git a/credentials/credentials.go b/credentials/credentials.go
index a63ab60..c8e337c 100644
--- a/credentials/credentials.go
+++ b/credentials/credentials.go
@@ -96,10 +96,11 @@
return c
}
-// ProtocolInfo provides information regarding the gRPC wire protocol version,
-// security protocol, security protocol version in use, server name, etc.
+// ProtocolInfo provides static information regarding transport credentials.
type ProtocolInfo struct {
// ProtocolVersion is the gRPC wire protocol version.
+ //
+ // Deprecated: this is unused by gRPC.
ProtocolVersion string
// SecurityProtocol is the security protocol in use.
SecurityProtocol string
@@ -109,7 +110,16 @@
//
// Deprecated: please use Peer.AuthInfo.
SecurityVersion string
- // ServerName is the user-configured server name.
+ // ServerName is the user-configured server name. If set, this overrides
+ // the default :authority header used for all RPCs on the channel using the
+ // containing credentials, unless grpc.WithAuthority is set on the channel,
+ // in which case that setting will take precedence.
+ //
+ // This must be a valid `:authority` header according to
+ // [RFC3986](https://datatracker.ietf.org/doc/html/rfc3986#section-3.2).
+ //
+ // Deprecated: Users should use grpc.WithAuthority to override the authority
+ // on a channel instead of configuring the credentials.
ServerName string
}
@@ -173,12 +183,17 @@
// Clone makes a copy of this TransportCredentials.
Clone() TransportCredentials
// OverrideServerName specifies the value used for the following:
+ //
// - verifying the hostname on the returned certificates
// - as SNI in the client's handshake to support virtual hosting
// - as the value for `:authority` header at stream creation time
//
- // Deprecated: use grpc.WithAuthority instead. Will be supported
- // throughout 1.x.
+ // The provided string should be a valid `:authority` header according to
+ // [RFC3986](https://datatracker.ietf.org/doc/html/rfc3986#section-3.2).
+ //
+ // Deprecated: this method is unused by gRPC. Users should use
+ // grpc.WithAuthority to override the authority on a channel instead of
+ // configuring the credentials.
OverrideServerName(string) error
}
diff --git a/credentials/tls.go b/credentials/tls.go
index 20f65f7..8277be7 100644
--- a/credentials/tls.go
+++ b/credentials/tls.go
@@ -110,14 +110,14 @@
func (c *tlsCreds) ClientHandshake(ctx context.Context, authority string, rawConn net.Conn) (_ net.Conn, _ AuthInfo, err error) {
// use local cfg to avoid clobbering ServerName if using multiple endpoints
cfg := credinternal.CloneTLSConfig(c.config)
- if cfg.ServerName == "" {
- serverName, _, err := net.SplitHostPort(authority)
- if err != nil {
- // If the authority had no host port or if the authority cannot be parsed, use it as-is.
- serverName = authority
- }
- cfg.ServerName = serverName
+
+ serverName, _, err := net.SplitHostPort(authority)
+ if err != nil {
+ // If the authority had no host port or if the authority cannot be parsed, use it as-is.
+ serverName = authority
}
+ cfg.ServerName = serverName
+
conn := tls.Client(rawConn, cfg)
errChannel := make(chan error, 1)
go func() {
@@ -259,9 +259,11 @@
// certificates to establish the identity of the client need to be included in
// the credentials (eg: for mTLS), use NewTLS instead, where a complete
// tls.Config can be specified.
-// serverNameOverride is for testing only. If set to a non empty string,
-// it will override the virtual host name of authority (e.g. :authority header
-// field) in requests.
+//
+// serverNameOverride is for testing only. If set to a non empty string, it will
+// override the virtual host name of authority (e.g. :authority header field) in
+// requests. Users should use grpc.WithAuthority passed to grpc.NewClient to
+// override the authority of the client instead.
func NewClientTLSFromCert(cp *x509.CertPool, serverNameOverride string) TransportCredentials {
return NewTLS(&tls.Config{ServerName: serverNameOverride, RootCAs: cp})
}
@@ -271,9 +273,11 @@
// certificates to establish the identity of the client need to be included in
// the credentials (eg: for mTLS), use NewTLS instead, where a complete
// tls.Config can be specified.
-// serverNameOverride is for testing only. If set to a non empty string,
-// it will override the virtual host name of authority (e.g. :authority header
-// field) in requests.
+//
+// serverNameOverride is for testing only. If set to a non empty string, it will
+// override the virtual host name of authority (e.g. :authority header field) in
+// requests. Users should use grpc.WithAuthority passed to grpc.NewClient to
+// override the authority of the client instead.
func NewClientTLSFromFile(certFile, serverNameOverride string) (TransportCredentials, error) {
b, err := os.ReadFile(certFile)
if err != nil {
diff --git a/credentials/xds/xds_client_test.go b/credentials/xds/xds_client_test.go
index 3d07c0b..023fbd4 100644
--- a/credentials/xds/xds_client_test.go
+++ b/credentials/xds/xds_client_test.go
@@ -47,7 +47,7 @@
defaultTestTimeout = 1 * time.Second
defaultTestShortTimeout = 10 * time.Millisecond
defaultTestCertSAN = "abc.test.example.com"
- authority = "authority"
+ authority = "x.test.example.com"
)
type s struct {
@@ -61,7 +61,7 @@
// Helper function to create a real TLS client credentials which is used as
// fallback credentials from multiple tests.
func makeFallbackClientCreds(t *testing.T) credentials.TransportCredentials {
- creds, err := credentials.NewClientTLSFromFile(testdata.Path("x509/server_ca_cert.pem"), "x.test.example.com")
+ creds, err := credentials.NewClientTLSFromFile(testdata.Path("x509/server_ca_cert.pem"), "")
if err != nil {
t.Fatal(err)
}
diff --git a/dialoptions.go b/dialoptions.go
index ec0ca89..7a5ac2e 100644
--- a/dialoptions.go
+++ b/dialoptions.go
@@ -608,6 +608,8 @@
// WithAuthority returns a DialOption that specifies the value to be used as the
// :authority pseudo-header and as the server name in authentication handshake.
+// This overrides all other ways of setting authority on the channel, but can be
+// overridden per-call by using grpc.CallAuthority.
func WithAuthority(a string) DialOption {
return newFuncDialOption(func(o *dialOptions) {
o.authority = a
diff --git a/experimental/credentials/tls.go b/experimental/credentials/tls.go
index 7b50b0a..0e19c86 100644
--- a/experimental/credentials/tls.go
+++ b/experimental/credentials/tls.go
@@ -56,14 +56,14 @@
func (c *tlsCreds) ClientHandshake(ctx context.Context, authority string, rawConn net.Conn) (_ net.Conn, _ credentials.AuthInfo, err error) {
// use local cfg to avoid clobbering ServerName if using multiple endpoints
cfg := cloneTLSConfig(c.config)
- if cfg.ServerName == "" {
- serverName, _, err := net.SplitHostPort(authority)
- if err != nil {
- // If the authority had no host port or if the authority cannot be parsed, use it as-is.
- serverName = authority
- }
- cfg.ServerName = serverName
+
+ serverName, _, err := net.SplitHostPort(authority)
+ if err != nil {
+ // If the authority had no host port or if the authority cannot be parsed, use it as-is.
+ serverName = authority
}
+ cfg.ServerName = serverName
+
conn := tls.Client(rawConn, cfg)
errChannel := make(chan error, 1)
go func() {
diff --git a/resolver/resolver.go b/resolver/resolver.go
index b84ef26..8e6af95 100644
--- a/resolver/resolver.go
+++ b/resolver/resolver.go
@@ -332,6 +332,11 @@
// OverrideAuthority returns the authority to use for a ClientConn with the
// given target. The implementation must generate it without blocking,
// typically in line, and must keep it unchanged.
+ //
+ // The returned string must be a valid ":authority" header value, i.e. be
+ // encoded according to
+ // [RFC3986](https://datatracker.ietf.org/doc/html/rfc3986#section-3.2) as
+ // necessary.
OverrideAuthority(Target) string
}
diff --git a/scripts/vet.sh b/scripts/vet.sh
index 18c4085..38b6650 100755
--- a/scripts/vet.sh
+++ b/scripts/vet.sh
@@ -179,6 +179,7 @@
OverrideServerName is deprecated:
RemoveSubConn is deprecated:
SecurityVersion is deprecated:
+.ServerName is deprecated:
stats.PickerUpdated is deprecated:
Target is deprecated: Use the Target field in the BuildOptions instead.
UpdateAddresses is deprecated:
diff --git a/security/advancedtls/advancedtls.go b/security/advancedtls/advancedtls.go
index ddd67aa..c1f7a11 100644
--- a/security/advancedtls/advancedtls.go
+++ b/security/advancedtls/advancedtls.go
@@ -430,11 +430,10 @@
func (c *advancedTLSCreds) ClientHandshake(ctx context.Context, authority string, rawConn net.Conn) (net.Conn, credentials.AuthInfo, error) {
// Use local cfg to avoid clobbering ServerName if using multiple endpoints.
cfg := credinternal.CloneTLSConfig(c.config)
- // We return the full authority name to users if ServerName is empty without
- // stripping the trailing port.
- if cfg.ServerName == "" {
- cfg.ServerName = authority
- }
+ // We return the full authority name to users without stripping the trailing
+ // port.
+ cfg.ServerName = authority
+
peerVerifiedChains := CertificateChains{}
cfg.VerifyPeerCertificate = buildVerifyFunc(c, cfg.ServerName, rawConn, &peerVerifiedChains)
conn := tls.Client(rawConn, cfg)
diff --git a/test/balancer_test.go b/test/balancer_test.go
index 7e3280d..ef0348d 100644
--- a/test/balancer_test.go
+++ b/test/balancer_test.go
@@ -154,6 +154,7 @@
te.tapHandle = authHandle
te.customDialOptions = []grpc.DialOption{
grpc.WithDefaultServiceConfig(fmt.Sprintf(`{"loadBalancingConfig": [{"%s":{}}]}`, testBalancerName)),
+ grpc.WithAuthority("x.test.example.com"),
}
creds, err := credentials.NewServerTLSFromFile(testdata.Path("x509/server1_cert.pem"), testdata.Path("x509/server1_key.pem"))
if err != nil {
diff --git a/test/creds_test.go b/test/creds_test.go
index bedafa5..6ef62d9 100644
--- a/test/creds_test.go
+++ b/test/creds_test.go
@@ -83,6 +83,7 @@
te.tapHandle = authHandle
te.customDialOptions = []grpc.DialOption{
grpc.WithCredentialsBundle(&testCredsBundle{t: t}),
+ grpc.WithAuthority("x.test.example.com"),
}
creds, err := credentials.NewServerTLSFromFile(testdata.Path("x509/server1_cert.pem"), testdata.Path("x509/server1_key.pem"))
if err != nil {
@@ -107,6 +108,7 @@
te := newTest(t, env{name: "creds-bundle", network: "tcp", security: "empty"})
te.customDialOptions = []grpc.DialOption{
grpc.WithCredentialsBundle(&testCredsBundle{t: t, mode: bundleTLSOnly}),
+ grpc.WithAuthority("x.test.example.com"),
}
creds, err := credentials.NewServerTLSFromFile(testdata.Path("x509/server1_cert.pem"), testdata.Path("x509/server1_key.pem"))
if err != nil {