github.com/cilium/cilium@v1.16.2/pkg/hubble/relay/pool/client.go (about) 1 // SPDX-License-Identifier: Apache-2.0 2 // Copyright Authors of Cilium 3 4 package pool 5 6 import ( 7 "context" 8 "crypto/tls" 9 "fmt" 10 "net" 11 12 "google.golang.org/grpc" 13 "google.golang.org/grpc/credentials" 14 "google.golang.org/grpc/credentials/insecure" 15 16 "github.com/cilium/cilium/pkg/crypto/certloader" 17 poolTypes "github.com/cilium/cilium/pkg/hubble/relay/pool/types" 18 hubbleopts "github.com/cilium/cilium/pkg/hubble/server/serveroption" 19 "github.com/cilium/cilium/pkg/lock" 20 "github.com/cilium/cilium/pkg/time" 21 ) 22 23 // GRPCClientConnBuilder is a generic ClientConnBuilder implementation. 24 type GRPCClientConnBuilder struct { 25 // DialTimeout specifies the timeout used when establishing a new 26 // connection. 27 DialTimeout time.Duration 28 // Options is a set of grpc.DialOption to be used when creating a new 29 // connection. 30 Options []grpc.DialOption 31 32 // TLSConfig is used to build transport credentials for the connection. 33 // If not provided, insecure credentials are added to Options before creating 34 // a new ClientConn. 35 TLSConfig certloader.ClientConfigBuilder 36 } 37 38 // ClientConn implements ClientConnBuilder.ClientConn. 39 func (b GRPCClientConnBuilder) ClientConn(target, hostname string) (poolTypes.ClientConn, error) { 40 // Ensure that the hostname (used as ServerName) information is given when 41 // Relay is configured with mTLS, and empty otherwise. We do this to report 42 // a mTLS misconfiguration between Hubble and Relay as early as possible. 43 switch { 44 case b.TLSConfig != nil && hostname == "": 45 return nil, fmt.Errorf("missing TLS ServerName for %s", target) 46 case b.TLSConfig == nil && hostname != "": 47 return nil, fmt.Errorf("unexpected TLS ServerName %s for %s", hostname, target) 48 } 49 50 ctx, cancel := context.WithTimeout(context.Background(), b.DialTimeout) 51 defer cancel() 52 opts := make([]grpc.DialOption, len(b.Options)) 53 copy(opts, b.Options) 54 55 if b.TLSConfig == nil { 56 opts = append(opts, grpc.WithTransportCredentials(insecure.NewCredentials())) 57 } else { 58 // NOTE: gosec is unable to resolve the constant and warns about "TLS 59 // MinVersion too low". 60 baseConf := &tls.Config{ //nolint:gosec 61 ServerName: hostname, 62 MinVersion: hubbleopts.MinTLSVersion, 63 } 64 opts = append(opts, grpc.WithTransportCredentials( 65 &grpcTLSCredentialsWrapper{ 66 TransportCredentials: credentials.NewTLS(b.TLSConfig.ClientConfig(baseConf)), 67 baseConf: baseConf, 68 TLSConfig: b.TLSConfig, 69 }, 70 )) 71 } 72 return grpc.DialContext(ctx, target, opts...) 73 } 74 75 var _ credentials.TransportCredentials = &grpcTLSCredentialsWrapper{} 76 77 // grpcTLSCredentialsWrapper wraps gRPC TransportCredentials and fetches the 78 // newest TLS configuration from certloader whenever we a new TLS connection 79 // is established. 80 // 81 // A gRPC ClientConn will call ClientHandshake whenever it tries to establish 82 // a new TLS connection. This happens in the beginning when dialing, but also 83 // when the connection is lost and gRPC tries to reestablish the connection. 84 // Wrapping the ClientHandshake and fetching the updated certificate and CA, 85 // allows us to transparently reload certificates when they change, without 86 // closing and redialing the gRPC ClientConn. 87 type grpcTLSCredentialsWrapper struct { 88 credentials.TransportCredentials 89 90 mu lock.Mutex 91 baseConf *tls.Config 92 TLSConfig certloader.ClientConfigBuilder 93 } 94 95 // ClientHandshake implements credentials.TransportCredentials. 96 func (w *grpcTLSCredentialsWrapper) ClientHandshake(ctx context.Context, addr string, conn net.Conn) (net.Conn, credentials.AuthInfo, error) { 97 w.mu.Lock() 98 defer w.mu.Unlock() 99 w.TransportCredentials = credentials.NewTLS(w.TLSConfig.ClientConfig(w.baseConf)) 100 return w.TransportCredentials.ClientHandshake(ctx, addr, conn) 101 } 102 103 // Clone implements credentials.TransportCredentials. 104 func (w *grpcTLSCredentialsWrapper) Clone() credentials.TransportCredentials { 105 w.mu.Lock() 106 defer w.mu.Unlock() 107 return &grpcTLSCredentialsWrapper{ 108 baseConf: w.baseConf.Clone(), 109 TransportCredentials: w.TransportCredentials.Clone(), 110 TLSConfig: w.TLSConfig, 111 } 112 }