diff --git a/flags/flags.go b/flags/flags.go index 3ebdf3f9ee..f98f129291 100644 --- a/flags/flags.go +++ b/flags/flags.go @@ -338,6 +338,9 @@ type FlagsRemoteStore struct { GRPCConnectionTimeout time.Duration `default:"3s" help:"The timeout duration for gRPC connection establishment."` GRPCMaxConnectionRetries uint32 `default:"5" help:"The maximum number of retries to establish a gRPC connection."` GRPCHeaders map[string]string `help:"Additional gRPC headers to send with each request (key=value pairs)."` + + ClientCert string `help:"Client certificate for mTLS"` + ClientKey string `help:"Client key for mTLS"` } // FlagsDebuginfo contains flags to configure debuginfo. diff --git a/flags/grpc.go b/flags/grpc.go index 896a767231..f0692a3af5 100644 --- a/flags/grpc.go +++ b/flags/grpc.go @@ -4,6 +4,7 @@ import ( "context" "crypto/tls" "fmt" + "net/url" "os" "strings" "time" @@ -83,12 +84,27 @@ func (f FlagsRemoteStore) setupGrpcConnection(parent context.Context, metrics *g if f.Insecure { opts = append(opts, grpc.WithTransportCredentials(insecure.NewCredentials())) } else { - opts = append(opts, - grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{ - // Support only TLS1.3+ with valid CA certificates - MinVersion: tls.VersionTLS13, - InsecureSkipVerify: f.InsecureSkipVerify, - }))) + tlsConfig := tls.Config{ + // Support only TLS1.3+ with valid CA certificates + MinVersion: tls.VersionTLS13, + InsecureSkipVerify: f.InsecureSkipVerify, + } + + if f.ClientKey != "" && f.ClientCert != "" { + cert, err := tls.LoadX509KeyPair(f.ClientCert, f.ClientKey) + if err != nil { + return nil, fmt.Errorf("failed to load client certificates: %w", err) + } + tlsConfig.Certificates = []tls.Certificate{cert} + + url, err := url.Parse(f.Address) + if err != nil { + return nil, fmt.Errorf("couldn't parse address (%s): %w", f.Address, err) + } + tlsConfig.ServerName = url.Hostname() + } + + opts = append(opts, grpc.WithTransportCredentials(credentials.NewTLS(&tlsConfig))) } // Auth