// ClientSetup loads various TLS certificates and creates a // gRPC TransportCredentials that presents the client certificate // and validates the certificate presented by the server is for a // specific hostname and issued by the provided issuer certificate // thens dials and returns a grpc.ClientConn to the remote service. func ClientSetup(c *cmd.GRPCClientConfig, stats metrics.Scope) (*grpc.ClientConn, error) { if len(c.ServerAddresses) == 0 { return nil, fmt.Errorf("boulder/grpc: ServerAddresses is empty") } if stats == nil { return nil, errNilScope } serverIssuerBytes, err := ioutil.ReadFile(c.ServerIssuerPath) if err != nil { return nil, err } rootCAs := x509.NewCertPool() if ok := rootCAs.AppendCertsFromPEM(serverIssuerBytes); !ok { return nil, fmt.Errorf("Failed to parse server issues from '%s'", c.ServerIssuerPath) } clientCert, err := tls.LoadX509KeyPair(c.ClientCertificatePath, c.ClientKeyPath) if err != nil { return nil, err } grpc_prometheus.EnableHandlingTimeHistogram() ci := clientInterceptor{stats.NewScope("gRPCClient"), clock.Default(), c.Timeout.Duration} creds := bcreds.NewClientCredentials(rootCAs, []tls.Certificate{clientCert}) return grpc.Dial( "", // Since our staticResolver provides addresses we don't need to pass an address here grpc.WithTransportCredentials(creds), grpc.WithBalancer(grpc.RoundRobin(newStaticResolver(c.ServerAddresses))), grpc.WithUnaryInterceptor(ci.intercept), ) }
func (c *Client) dial(endpoint string, dopts ...grpc.DialOption) (*grpc.ClientConn, error) { opts := c.dialSetupOpts(endpoint, dopts...) host := getHost(endpoint) if c.Username != "" && c.Password != "" { // use dial options without dopts to avoid reusing the client balancer auth, err := newAuthenticator(host, c.dialSetupOpts(endpoint)) if err != nil { return nil, err } defer auth.close() resp, err := auth.authenticate(c.ctx, c.Username, c.Password) if err != nil { return nil, err } opts = append(opts, grpc.WithPerRPCCredentials(authTokenCredential{token: resp.Token})) } // add metrics options opts = append(opts, grpc.WithUnaryInterceptor(prometheus.UnaryClientInterceptor)) opts = append(opts, grpc.WithStreamInterceptor(prometheus.StreamClientInterceptor)) conn, err := grpc.Dial(host, opts...) if err != nil { return nil, err } return conn, nil }
return nil } span = span.NewRemoteChild(req) return func(resp *http.Response) { if resp != nil { span.Finish(WithResponse(resp)) } else { span.Finish() } } } // EnableGRPCTracingDialOption enables tracing of requests that are sent over a // gRPC connection. // The functionality in gRPC that this relies on is currently experimental. var EnableGRPCTracingDialOption grpc.DialOption = grpc.WithUnaryInterceptor(grpc.UnaryClientInterceptor(grpcUnaryInterceptor)) // EnableGRPCTracing enables tracing of requests for clients that use gRPC // connections. // The functionality in gRPC that this relies on is currently experimental. var EnableGRPCTracing option.ClientOption = option.WithGRPCDialOption(EnableGRPCTracingDialOption) func grpcUnaryInterceptor(ctx context.Context, method string, req, reply interface{}, cc *grpc.ClientConn, invoker grpc.UnaryInvoker, opts ...grpc.CallOption) error { // TODO: also intercept streams. span := FromContext(ctx).NewChild(method) err := invoker(ctx, method, req, reply, cc, opts...) if err != nil { // TODO: standardize gRPC label names? span.SetLabel("error", err.Error()) } span.Finish()