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 }