2525package cli
2626
2727import (
28+ "crypto/tls"
29+ "crypto/x509"
30+ "errors"
31+ "io/ioutil"
32+ "net"
33+
2834 "github.com/urfave/cli"
2935 "go.temporal.io/api/workflowservice/v1"
3036 sdkclient "go.temporal.io/sdk/client"
3137 "go.uber.org/zap"
3238 "google.golang.org/grpc"
39+ "google.golang.org/grpc/credentials"
3340
3441 "go.temporal.io/server/api/adminservice/v1"
3542 "go.temporal.io/server/common/log"
36- "go.temporal.io/server/common/rpc"
3743)
3844
3945// ClientFactory is used to construct rpc clients
@@ -61,14 +67,14 @@ func NewClientFactory() ClientFactory {
6167
6268// FrontendClient builds a frontend client
6369func (b * clientFactory ) FrontendClient (c * cli.Context ) workflowservice.WorkflowServiceClient {
64- connection := b .createGRPCConnection (c . GlobalString ( FlagAddress ) )
70+ connection , _ := b .createGRPCConnection (c )
6571
6672 return workflowservice .NewWorkflowServiceClient (connection )
6773}
6874
6975// AdminClient builds an admin client (based on server side thrift interface)
7076func (b * clientFactory ) AdminClient (c * cli.Context ) adminservice.AdminServiceClient {
71- connection := b .createGRPCConnection (c . GlobalString ( FlagAddress ) )
77+ connection , _ := b .createGRPCConnection (c )
7278
7379 return adminservice .NewAdminServiceClient (connection )
7480}
@@ -95,16 +101,69 @@ func (b *clientFactory) SDKClient(c *cli.Context, namespace string) sdkclient.Cl
95101 return sdkClient
96102}
97103
98- func (b * clientFactory ) createGRPCConnection (hostPort string ) * grpc.ClientConn {
104+ func (b * clientFactory ) createGRPCConnection (c * cli.Context ) (* grpc.ClientConn , error ) {
105+ hostPort := c .GlobalString (FlagAddress )
99106 if hostPort == "" {
100107 hostPort = localHostPort
101108 }
109+ // Ignoring error as we'll fail to dial anyway, and that will produce a meaningful error
110+ host , _ , _ := net .SplitHostPort (hostPort )
111+
112+ certPath := c .GlobalString (FlagTLSCertPath )
113+ keyPath := c .GlobalString (FlagTLSKeyPath )
114+ caPath := c .GlobalString (FlagTLSCaPath )
115+
116+ grpcSecurityOptions := grpc .WithInsecure ()
117+ var cert * tls.Certificate
118+ var caPool * x509.CertPool
119+
120+ if caPath != "" {
121+ caCertPool , err := fetchCACert (caPath )
122+ if err != nil {
123+ b .logger .Fatal ("Failed to load server CA certificate" , zap .Error (err ))
124+ return nil , err
125+ }
126+ caPool = caCertPool
127+ }
128+ if certPath != "" {
129+ myCert , err := tls .LoadX509KeyPair (certPath , keyPath )
130+ if err != nil {
131+ b .logger .Fatal ("Failed to load client certificate" , zap .Error (err ))
132+ return nil , err
133+ }
134+ cert = & myCert
135+ }
136+ // If we are given arguments to verify either server or client, configure TLS
137+ if caPool != nil || cert != nil {
138+ tlsConfig := & tls.Config {
139+ ServerName : host ,
140+ }
141+ if caPool != nil {
142+ tlsConfig .RootCAs = caPool
143+ }
144+ if cert != nil {
145+ tlsConfig .Certificates = []tls.Certificate {* cert }
146+ }
147+ grpcSecurityOptions = grpc .WithTransportCredentials (credentials .NewTLS (tlsConfig ))
148+ }
102149
103- connection , err := rpc .Dial (hostPort , nil )
150+ connection , err := grpc .Dial (hostPort , grpcSecurityOptions )
104151 if err != nil {
105152 b .logger .Fatal ("Failed to create connection" , zap .Error (err ))
106- return nil
153+ return nil , err
107154 }
155+ return connection , nil
156+ }
108157
109- return connection
158+ func fetchCACert (path string ) (* x509.CertPool , error ) {
159+ caPool := x509 .NewCertPool ()
160+ caBytes , err := ioutil .ReadFile (path )
161+ if err != nil {
162+ return nil , err
163+ }
164+
165+ if ! caPool .AppendCertsFromPEM (caBytes ) {
166+ return nil , errors .New ("unknown failure constructing cert pool for ca" )
167+ }
168+ return caPool , nil
110169}
0 commit comments