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

     1  /*
     2   *
     3   * Copyright 2014 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
    20  
    21  import (
    22  	"context"
    23  	"crypto/tls"
    24  	"crypto/x509"
    25  	"fmt"
    26  	"net"
    27  	"net/url"
    28  	"os"
    29  
    30  	"google.golang.org/grpc/grpclog"
    31  	credinternal "google.golang.org/grpc/internal/credentials"
    32  	"google.golang.org/grpc/internal/envconfig"
    33  )
    34  
    35  const alpnFailureHelpMessage = "If you upgraded from a grpc-go version earlier than 1.67, your TLS connections may have stopped working due to ALPN enforcement. For more details, see: https://github.com/grpc/grpc-go/issues/434"
    36  
    37  var logger = grpclog.Component("credentials")
    38  
    39  // TLSInfo contains the auth information for a TLS authenticated connection.
    40  // It implements the AuthInfo interface.
    41  type TLSInfo struct {
    42  	State tls.ConnectionState
    43  	CommonAuthInfo
    44  	// This API is experimental.
    45  	SPIFFEID *url.URL
    46  }
    47  
    48  // AuthType returns the type of TLSInfo as a string.
    49  func (t TLSInfo) AuthType() string {
    50  	return "tls"
    51  }
    52  
    53  // cipherSuiteLookup returns the string version of a TLS cipher suite ID.
    54  func cipherSuiteLookup(cipherSuiteID uint16) string {
    55  	for _, s := range tls.CipherSuites() {
    56  		if s.ID == cipherSuiteID {
    57  			return s.Name
    58  		}
    59  	}
    60  	for _, s := range tls.InsecureCipherSuites() {
    61  		if s.ID == cipherSuiteID {
    62  			return s.Name
    63  		}
    64  	}
    65  	return fmt.Sprintf("unknown ID: %v", cipherSuiteID)
    66  }
    67  
    68  // GetSecurityValue returns security info requested by channelz.
    69  func (t TLSInfo) GetSecurityValue() ChannelzSecurityValue {
    70  	v := &TLSChannelzSecurityValue{
    71  		StandardName: cipherSuiteLookup(t.State.CipherSuite),
    72  	}
    73  	// Currently there's no way to get LocalCertificate info from tls package.
    74  	if len(t.State.PeerCertificates) > 0 {
    75  		v.RemoteCertificate = t.State.PeerCertificates[0].Raw
    76  	}
    77  	return v
    78  }
    79  
    80  // tlsCreds is the credentials required for authenticating a connection using TLS.
    81  type tlsCreds struct {
    82  	// TLS configuration
    83  	config *tls.Config
    84  }
    85  
    86  func (c tlsCreds) Info() ProtocolInfo {
    87  	return ProtocolInfo{
    88  		SecurityProtocol: "tls",
    89  		SecurityVersion:  "1.2",
    90  		ServerName:       c.config.ServerName,
    91  	}
    92  }
    93  
    94  func (c *tlsCreds) ClientHandshake(ctx context.Context, authority string, rawConn net.Conn) (_ net.Conn, _ AuthInfo, err error) {
    95  	// use local cfg to avoid clobbering ServerName if using multiple endpoints
    96  	cfg := credinternal.CloneTLSConfig(c.config)
    97  	if cfg.ServerName == "" {
    98  		serverName, _, err := net.SplitHostPort(authority)
    99  		if err != nil {
   100  			// If the authority had no host port or if the authority cannot be parsed, use it as-is.
   101  			serverName = authority
   102  		}
   103  		cfg.ServerName = serverName
   104  	}
   105  	conn := tls.Client(rawConn, cfg)
   106  	errChannel := make(chan error, 1)
   107  	go func() {
   108  		errChannel <- conn.Handshake()
   109  		close(errChannel)
   110  	}()
   111  	select {
   112  	case err := <-errChannel:
   113  		if err != nil {
   114  			conn.Close()
   115  			return nil, nil, err
   116  		}
   117  	case <-ctx.Done():
   118  		conn.Close()
   119  		return nil, nil, ctx.Err()
   120  	}
   121  
   122  	// The negotiated protocol can be either of the following:
   123  	// 1. h2: When the server supports ALPN. Only HTTP/2 can be negotiated since
   124  	//    it is the only protocol advertised by the client during the handshake.
   125  	//    The tls library ensures that the server chooses a protocol advertised
   126  	//    by the client.
   127  	// 2. "" (empty string): If the server doesn't support ALPN. ALPN is a requirement
   128  	//    for using HTTP/2 over TLS. We can terminate the connection immediately.
   129  	np := conn.ConnectionState().NegotiatedProtocol
   130  	if np == "" {
   131  		if envconfig.EnforceALPNEnabled {
   132  			conn.Close()
   133  			return nil, nil, fmt.Errorf("credentials: cannot check peer: missing selected ALPN property. %s", alpnFailureHelpMessage)
   134  		}
   135  		logger.Warningf("Allowing TLS connection to server %q with ALPN disabled. TLS connections to servers with ALPN disabled will be disallowed in future grpc-go releases", cfg.ServerName)
   136  	}
   137  	tlsInfo := TLSInfo{
   138  		State: conn.ConnectionState(),
   139  		CommonAuthInfo: CommonAuthInfo{
   140  			SecurityLevel: PrivacyAndIntegrity,
   141  		},
   142  	}
   143  	id := credinternal.SPIFFEIDFromState(conn.ConnectionState())
   144  	if id != nil {
   145  		tlsInfo.SPIFFEID = id
   146  	}
   147  	return credinternal.WrapSyscallConn(rawConn, conn), tlsInfo, nil
   148  }
   149  
   150  func (c *tlsCreds) ServerHandshake(rawConn net.Conn) (net.Conn, AuthInfo, error) {
   151  	conn := tls.Server(rawConn, c.config)
   152  	if err := conn.Handshake(); err != nil {
   153  		conn.Close()
   154  		return nil, nil, err
   155  	}
   156  	cs := conn.ConnectionState()
   157  	// The negotiated application protocol can be empty only if the client doesn't
   158  	// support ALPN. In such cases, we can close the connection since ALPN is required
   159  	// for using HTTP/2 over TLS.
   160  	if cs.NegotiatedProtocol == "" {
   161  		if envconfig.EnforceALPNEnabled {
   162  			conn.Close()
   163  			return nil, nil, fmt.Errorf("credentials: cannot check peer: missing selected ALPN property. %s", alpnFailureHelpMessage)
   164  		} else if logger.V(2) {
   165  			logger.Info("Allowing TLS connection from client with ALPN disabled. TLS connections with ALPN disabled will be disallowed in future grpc-go releases")
   166  		}
   167  	}
   168  	tlsInfo := TLSInfo{
   169  		State: cs,
   170  		CommonAuthInfo: CommonAuthInfo{
   171  			SecurityLevel: PrivacyAndIntegrity,
   172  		},
   173  	}
   174  	id := credinternal.SPIFFEIDFromState(conn.ConnectionState())
   175  	if id != nil {
   176  		tlsInfo.SPIFFEID = id
   177  	}
   178  	return credinternal.WrapSyscallConn(rawConn, conn), tlsInfo, nil
   179  }
   180  
   181  func (c *tlsCreds) Clone() TransportCredentials {
   182  	return NewTLS(c.config)
   183  }
   184  
   185  func (c *tlsCreds) OverrideServerName(serverNameOverride string) error {
   186  	c.config.ServerName = serverNameOverride
   187  	return nil
   188  }
   189  
   190  // The following cipher suites are forbidden for use with HTTP/2 by
   191  // https://datatracker.ietf.org/doc/html/rfc7540#appendix-A
   192  var tls12ForbiddenCipherSuites = map[uint16]struct{}{
   193  	tls.TLS_RSA_WITH_AES_128_CBC_SHA:         {},
   194  	tls.TLS_RSA_WITH_AES_256_CBC_SHA:         {},
   195  	tls.TLS_RSA_WITH_AES_128_GCM_SHA256:      {},
   196  	tls.TLS_RSA_WITH_AES_256_GCM_SHA384:      {},
   197  	tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA: {},
   198  	tls.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA: {},
   199  	tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA:   {},
   200  	tls.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA:   {},
   201  }
   202  
   203  // NewTLS uses c to construct a TransportCredentials based on TLS.
   204  func NewTLS(c *tls.Config) TransportCredentials {
   205  	config := applyDefaults(c)
   206  	if config.GetConfigForClient != nil {
   207  		oldFn := config.GetConfigForClient
   208  		config.GetConfigForClient = func(hello *tls.ClientHelloInfo) (*tls.Config, error) {
   209  			cfgForClient, err := oldFn(hello)
   210  			if err != nil || cfgForClient == nil {
   211  				return cfgForClient, err
   212  			}
   213  			return applyDefaults(cfgForClient), nil
   214  		}
   215  	}
   216  	return &tlsCreds{config: config}
   217  }
   218  
   219  func applyDefaults(c *tls.Config) *tls.Config {
   220  	config := credinternal.CloneTLSConfig(c)
   221  	config.NextProtos = credinternal.AppendH2ToNextProtos(config.NextProtos)
   222  	// If the user did not configure a MinVersion and did not configure a
   223  	// MaxVersion < 1.2, use MinVersion=1.2, which is required by
   224  	// https://datatracker.ietf.org/doc/html/rfc7540#section-9.2
   225  	if config.MinVersion == 0 && (config.MaxVersion == 0 || config.MaxVersion >= tls.VersionTLS12) {
   226  		config.MinVersion = tls.VersionTLS12
   227  	}
   228  	// If the user did not configure CipherSuites, use all "secure" cipher
   229  	// suites reported by the TLS package, but remove some explicitly forbidden
   230  	// by https://datatracker.ietf.org/doc/html/rfc7540#appendix-A
   231  	if config.CipherSuites == nil {
   232  		for _, cs := range tls.CipherSuites() {
   233  			if _, ok := tls12ForbiddenCipherSuites[cs.ID]; !ok {
   234  				config.CipherSuites = append(config.CipherSuites, cs.ID)
   235  			}
   236  		}
   237  	}
   238  	return config
   239  }
   240  
   241  // NewClientTLSFromCert constructs TLS credentials from the provided root
   242  // certificate authority certificate(s) to validate server connections. If
   243  // certificates to establish the identity of the client need to be included in
   244  // the credentials (eg: for mTLS), use NewTLS instead, where a complete
   245  // tls.Config can be specified.
   246  // serverNameOverride is for testing only. If set to a non empty string,
   247  // it will override the virtual host name of authority (e.g. :authority header
   248  // field) in requests.
   249  func NewClientTLSFromCert(cp *x509.CertPool, serverNameOverride string) TransportCredentials {
   250  	return NewTLS(&tls.Config{ServerName: serverNameOverride, RootCAs: cp})
   251  }
   252  
   253  // NewClientTLSFromFile constructs TLS credentials from the provided root
   254  // certificate authority certificate file(s) to validate server connections. If
   255  // certificates to establish the identity of the client need to be included in
   256  // the credentials (eg: for mTLS), use NewTLS instead, where a complete
   257  // tls.Config can be specified.
   258  // serverNameOverride is for testing only. If set to a non empty string,
   259  // it will override the virtual host name of authority (e.g. :authority header
   260  // field) in requests.
   261  func NewClientTLSFromFile(certFile, serverNameOverride string) (TransportCredentials, error) {
   262  	b, err := os.ReadFile(certFile)
   263  	if err != nil {
   264  		return nil, err
   265  	}
   266  	cp := x509.NewCertPool()
   267  	if !cp.AppendCertsFromPEM(b) {
   268  		return nil, fmt.Errorf("credentials: failed to append certificates")
   269  	}
   270  	return NewTLS(&tls.Config{ServerName: serverNameOverride, RootCAs: cp}), nil
   271  }
   272  
   273  // NewServerTLSFromCert constructs TLS credentials from the input certificate for server.
   274  func NewServerTLSFromCert(cert *tls.Certificate) TransportCredentials {
   275  	return NewTLS(&tls.Config{Certificates: []tls.Certificate{*cert}})
   276  }
   277  
   278  // NewServerTLSFromFile constructs TLS credentials from the input certificate file and key
   279  // file for server.
   280  func NewServerTLSFromFile(certFile, keyFile string) (TransportCredentials, error) {
   281  	cert, err := tls.LoadX509KeyPair(certFile, keyFile)
   282  	if err != nil {
   283  		return nil, err
   284  	}
   285  	return NewTLS(&tls.Config{Certificates: []tls.Certificate{cert}}), nil
   286  }
   287  
   288  // TLSChannelzSecurityValue defines the struct that TLS protocol should return
   289  // from GetSecurityValue(), containing security info like cipher and certificate used.
   290  //
   291  // # Experimental
   292  //
   293  // Notice: This type is EXPERIMENTAL and may be changed or removed in a
   294  // later release.
   295  type TLSChannelzSecurityValue struct {
   296  	ChannelzSecurityValue
   297  	StandardName      string
   298  	LocalCertificate  []byte
   299  	RemoteCertificate []byte
   300  }