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