google.golang.org/grpc@v1.72.2/experimental/credentials/tls.go (about)

     1  /*
     2   *
     3   * Copyright 2025 gRPC authors.
     4   *
     5   * Licensed under the Apache License, Version 2.0 (the "License");
     6   * you may not use this file except in compliance with the License.
     7   * You may obtain a copy of the License at
     8   *
     9   *     http://www.apache.org/licenses/LICENSE-2.0
    10   *
    11   * Unless required by applicable law or agreed to in writing, software
    12   * distributed under the License is distributed on an "AS IS" BASIS,
    13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    14   * See the License for the specific language governing permissions and
    15   * limitations under the License.
    16   *
    17   */
    18  
    19  // Package credentials provides experimental TLS credentials.
    20  // The use of this package is strongly discouraged. These credentials exist
    21  // solely to maintain compatibility for users interacting with clients that
    22  // violate the HTTP/2 specification by not advertising support for "h2" in ALPN.
    23  // This package is slated for removal in upcoming grpc-go releases. Users must
    24  // not rely on this package directly. Instead, they should either vendor a
    25  // specific version of gRPC or copy the relevant credentials into their own
    26  // codebase if absolutely necessary.
    27  package credentials
    28  
    29  import (
    30  	"context"
    31  	"crypto/tls"
    32  	"crypto/x509"
    33  	"fmt"
    34  	"net"
    35  	"os"
    36  
    37  	"golang.org/x/net/http2"
    38  	"google.golang.org/grpc/credentials"
    39  	"google.golang.org/grpc/experimental/credentials/internal"
    40  )
    41  
    42  // tlsCreds is the credentials required for authenticating a connection using TLS.
    43  type tlsCreds struct {
    44  	// TLS configuration
    45  	config *tls.Config
    46  }
    47  
    48  func (c tlsCreds) Info() credentials.ProtocolInfo {
    49  	return credentials.ProtocolInfo{
    50  		SecurityProtocol: "tls",
    51  		SecurityVersion:  "1.2",
    52  		ServerName:       c.config.ServerName,
    53  	}
    54  }
    55  
    56  func (c *tlsCreds) ClientHandshake(ctx context.Context, authority string, rawConn net.Conn) (_ net.Conn, _ credentials.AuthInfo, err error) {
    57  	// use local cfg to avoid clobbering ServerName if using multiple endpoints
    58  	cfg := cloneTLSConfig(c.config)
    59  	if cfg.ServerName == "" {
    60  		serverName, _, err := net.SplitHostPort(authority)
    61  		if err != nil {
    62  			// If the authority had no host port or if the authority cannot be parsed, use it as-is.
    63  			serverName = authority
    64  		}
    65  		cfg.ServerName = serverName
    66  	}
    67  	conn := tls.Client(rawConn, cfg)
    68  	errChannel := make(chan error, 1)
    69  	go func() {
    70  		errChannel <- conn.Handshake()
    71  		close(errChannel)
    72  	}()
    73  	select {
    74  	case err := <-errChannel:
    75  		if err != nil {
    76  			conn.Close()
    77  			return nil, nil, err
    78  		}
    79  	case <-ctx.Done():
    80  		conn.Close()
    81  		return nil, nil, ctx.Err()
    82  	}
    83  
    84  	tlsInfo := credentials.TLSInfo{
    85  		State: conn.ConnectionState(),
    86  		CommonAuthInfo: credentials.CommonAuthInfo{
    87  			SecurityLevel: credentials.PrivacyAndIntegrity,
    88  		},
    89  	}
    90  	id := internal.SPIFFEIDFromState(conn.ConnectionState())
    91  	if id != nil {
    92  		tlsInfo.SPIFFEID = id
    93  	}
    94  	return internal.WrapSyscallConn(rawConn, conn), tlsInfo, nil
    95  }
    96  
    97  func (c *tlsCreds) ServerHandshake(rawConn net.Conn) (net.Conn, credentials.AuthInfo, error) {
    98  	conn := tls.Server(rawConn, c.config)
    99  	if err := conn.Handshake(); err != nil {
   100  		conn.Close()
   101  		return nil, nil, err
   102  	}
   103  	cs := conn.ConnectionState()
   104  	tlsInfo := credentials.TLSInfo{
   105  		State: cs,
   106  		CommonAuthInfo: credentials.CommonAuthInfo{
   107  			SecurityLevel: credentials.PrivacyAndIntegrity,
   108  		},
   109  	}
   110  	id := internal.SPIFFEIDFromState(conn.ConnectionState())
   111  	if id != nil {
   112  		tlsInfo.SPIFFEID = id
   113  	}
   114  	return internal.WrapSyscallConn(rawConn, conn), tlsInfo, nil
   115  }
   116  
   117  func (c *tlsCreds) Clone() credentials.TransportCredentials {
   118  	return NewTLSWithALPNDisabled(c.config)
   119  }
   120  
   121  func (c *tlsCreds) OverrideServerName(serverNameOverride string) error {
   122  	c.config.ServerName = serverNameOverride
   123  	return nil
   124  }
   125  
   126  // The following cipher suites are forbidden for use with HTTP/2 by
   127  // https://datatracker.ietf.org/doc/html/rfc7540#appendix-A
   128  var tls12ForbiddenCipherSuites = map[uint16]struct{}{
   129  	tls.TLS_RSA_WITH_AES_128_CBC_SHA:         {},
   130  	tls.TLS_RSA_WITH_AES_256_CBC_SHA:         {},
   131  	tls.TLS_RSA_WITH_AES_128_GCM_SHA256:      {},
   132  	tls.TLS_RSA_WITH_AES_256_GCM_SHA384:      {},
   133  	tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA: {},
   134  	tls.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA: {},
   135  	tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA:   {},
   136  	tls.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA:   {},
   137  }
   138  
   139  // NewTLSWithALPNDisabled uses c to construct a TransportCredentials based on
   140  // TLS. ALPN verification is disabled.
   141  func NewTLSWithALPNDisabled(c *tls.Config) credentials.TransportCredentials {
   142  	config := applyDefaults(c)
   143  	if config.GetConfigForClient != nil {
   144  		oldFn := config.GetConfigForClient
   145  		config.GetConfigForClient = func(hello *tls.ClientHelloInfo) (*tls.Config, error) {
   146  			cfgForClient, err := oldFn(hello)
   147  			if err != nil || cfgForClient == nil {
   148  				return cfgForClient, err
   149  			}
   150  			return applyDefaults(cfgForClient), nil
   151  		}
   152  	}
   153  	return &tlsCreds{config: config}
   154  }
   155  
   156  func applyDefaults(c *tls.Config) *tls.Config {
   157  	config := cloneTLSConfig(c)
   158  	config.NextProtos = appendH2ToNextProtos(config.NextProtos)
   159  	// If the user did not configure a MinVersion and did not configure a
   160  	// MaxVersion < 1.2, use MinVersion=1.2, which is required by
   161  	// https://datatracker.ietf.org/doc/html/rfc7540#section-9.2
   162  	if config.MinVersion == 0 && (config.MaxVersion == 0 || config.MaxVersion >= tls.VersionTLS12) {
   163  		config.MinVersion = tls.VersionTLS12
   164  	}
   165  	// If the user did not configure CipherSuites, use all "secure" cipher
   166  	// suites reported by the TLS package, but remove some explicitly forbidden
   167  	// by https://datatracker.ietf.org/doc/html/rfc7540#appendix-A
   168  	if config.CipherSuites == nil {
   169  		for _, cs := range tls.CipherSuites() {
   170  			if _, ok := tls12ForbiddenCipherSuites[cs.ID]; !ok {
   171  				config.CipherSuites = append(config.CipherSuites, cs.ID)
   172  			}
   173  		}
   174  	}
   175  	return config
   176  }
   177  
   178  // NewClientTLSFromCertWithALPNDisabled constructs TLS credentials from the
   179  // provided root certificate authority certificate(s) to validate server
   180  // connections. If certificates to establish the identity of the client need to
   181  // be included in the credentials (eg: for mTLS), use NewTLS instead, where a
   182  // complete tls.Config can be specified.
   183  // serverNameOverride is for testing only. If set to a non empty string,
   184  // it will override the virtual host name of authority (e.g. :authority header
   185  // field) in requests. ALPN verification is disabled.
   186  func NewClientTLSFromCertWithALPNDisabled(cp *x509.CertPool, serverNameOverride string) credentials.TransportCredentials {
   187  	return NewTLSWithALPNDisabled(&tls.Config{ServerName: serverNameOverride, RootCAs: cp})
   188  }
   189  
   190  // NewClientTLSFromFileWithALPNDisabled constructs TLS credentials from the
   191  // provided root certificate authority certificate file(s) to validate server
   192  // connections. If certificates to establish the identity of the client need to
   193  // be included in the credentials (eg: for mTLS), use NewTLS instead, where a
   194  // complete tls.Config can be specified.
   195  // serverNameOverride is for testing only. If set to a non empty string,
   196  // it will override the virtual host name of authority (e.g. :authority header
   197  // field) in requests. ALPN verification is disabled.
   198  func NewClientTLSFromFileWithALPNDisabled(certFile, serverNameOverride string) (credentials.TransportCredentials, error) {
   199  	b, err := os.ReadFile(certFile)
   200  	if err != nil {
   201  		return nil, err
   202  	}
   203  	cp := x509.NewCertPool()
   204  	if !cp.AppendCertsFromPEM(b) {
   205  		return nil, fmt.Errorf("credentials: failed to append certificates")
   206  	}
   207  	return NewTLSWithALPNDisabled(&tls.Config{ServerName: serverNameOverride, RootCAs: cp}), nil
   208  }
   209  
   210  // NewServerTLSFromCertWithALPNDisabled constructs TLS credentials from the
   211  // input certificate for server. ALPN verification is disabled.
   212  func NewServerTLSFromCertWithALPNDisabled(cert *tls.Certificate) credentials.TransportCredentials {
   213  	return NewTLSWithALPNDisabled(&tls.Config{Certificates: []tls.Certificate{*cert}})
   214  }
   215  
   216  // NewServerTLSFromFileWithALPNDisabled constructs TLS credentials from the
   217  // input certificate file and key file for server. ALPN verification is disabled.
   218  func NewServerTLSFromFileWithALPNDisabled(certFile, keyFile string) (credentials.TransportCredentials, error) {
   219  	cert, err := tls.LoadX509KeyPair(certFile, keyFile)
   220  	if err != nil {
   221  		return nil, err
   222  	}
   223  	return NewTLSWithALPNDisabled(&tls.Config{Certificates: []tls.Certificate{cert}}), nil
   224  }
   225  
   226  // cloneTLSConfig returns a shallow clone of the exported
   227  // fields of cfg, ignoring the unexported sync.Once, which
   228  // contains a mutex and must not be copied.
   229  //
   230  // If cfg is nil, a new zero tls.Config is returned.
   231  func cloneTLSConfig(cfg *tls.Config) *tls.Config {
   232  	if cfg == nil {
   233  		return &tls.Config{}
   234  	}
   235  
   236  	return cfg.Clone()
   237  }
   238  
   239  // appendH2ToNextProtos appends h2 to next protos.
   240  func appendH2ToNextProtos(ps []string) []string {
   241  	for _, p := range ps {
   242  		if p == http2.NextProtoTLS {
   243  			return ps
   244  		}
   245  	}
   246  	ret := make([]string, 0, len(ps)+1)
   247  	ret = append(ret, ps...)
   248  	return append(ret, http2.NextProtoTLS)
   249  }