github.com/prysmaticlabs/prysm@v1.4.4/validator/keymanager/remote/keymanager.go (about)

     1  package remote
     2  
     3  import (
     4  	"bytes"
     5  	"context"
     6  	"crypto/tls"
     7  	"crypto/x509"
     8  	"encoding/json"
     9  	"fmt"
    10  	"io"
    11  	"io/ioutil"
    12  	"sort"
    13  	"strings"
    14  
    15  	"github.com/golang/protobuf/ptypes/empty"
    16  	"github.com/logrusorgru/aurora"
    17  	"github.com/pkg/errors"
    18  	validatorpb "github.com/prysmaticlabs/prysm/proto/validator/accounts/v2"
    19  	"github.com/prysmaticlabs/prysm/shared/bls"
    20  	"github.com/prysmaticlabs/prysm/shared/bytesutil"
    21  	"github.com/prysmaticlabs/prysm/shared/event"
    22  	"github.com/prysmaticlabs/prysm/validator/keymanager"
    23  	"google.golang.org/grpc"
    24  	"google.golang.org/grpc/credentials"
    25  )
    26  
    27  var (
    28  	// ErrSigningFailed defines a failure from the remote server
    29  	// when performing a signing operation.
    30  	ErrSigningFailed = errors.New("signing failed in the remote server")
    31  	// ErrSigningDenied defines a failure from the remote server when
    32  	// performing a signing operation was denied by a remote server.
    33  	ErrSigningDenied = errors.New("signing request was denied by remote server")
    34  )
    35  
    36  // RemoteKeymanager defines the interface for remote Prysm wallets.
    37  type RemoteKeymanager interface {
    38  	keymanager.IKeymanager
    39  	ReloadPublicKeys(ctx context.Context) ([][48]byte, error)
    40  }
    41  
    42  // KeymanagerOpts for a remote keymanager.
    43  type KeymanagerOpts struct {
    44  	RemoteCertificate *CertificateConfig `json:"remote_cert"`
    45  	RemoteAddr        string             `json:"remote_address"`
    46  }
    47  
    48  // CertificateConfig defines configuration options for
    49  // certificate authority certs, client certs, and client keys
    50  // for TLS gRPC connections.
    51  type CertificateConfig struct {
    52  	RequireTls     bool   `json:"require_tls"`
    53  	ClientCertPath string `json:"crt_path"`
    54  	ClientKeyPath  string `json:"key_path"`
    55  	CACertPath     string `json:"ca_crt_path"`
    56  }
    57  
    58  // SetupConfig includes configuration values for initializing
    59  // a keymanager, such as passwords, the wallet, and more.
    60  type SetupConfig struct {
    61  	Opts           *KeymanagerOpts
    62  	MaxMessageSize int
    63  }
    64  
    65  // Keymanager implementation using remote signing keys via gRPC.
    66  type Keymanager struct {
    67  	opts                *KeymanagerOpts
    68  	client              validatorpb.RemoteSignerClient
    69  	orderedPubKeys      [][48]byte
    70  	accountsChangedFeed *event.Feed
    71  }
    72  
    73  // NewKeymanager instantiates a new imported keymanager from configuration options.
    74  func NewKeymanager(_ context.Context, cfg *SetupConfig) (*Keymanager, error) {
    75  	// Load the client certificates.
    76  	if cfg.Opts.RemoteCertificate == nil {
    77  		return nil, errors.New("certificate configuration is missing")
    78  	}
    79  
    80  	var clientCreds credentials.TransportCredentials
    81  
    82  	if cfg.Opts.RemoteCertificate.RequireTls {
    83  		if cfg.Opts.RemoteCertificate.ClientCertPath == "" {
    84  			return nil, errors.New("client certificate is required")
    85  		}
    86  		if cfg.Opts.RemoteCertificate.ClientKeyPath == "" {
    87  			return nil, errors.New("client key is required")
    88  		}
    89  		clientPair, err := tls.LoadX509KeyPair(cfg.Opts.RemoteCertificate.ClientCertPath, cfg.Opts.RemoteCertificate.ClientKeyPath)
    90  		if err != nil {
    91  			return nil, errors.Wrap(err, "failed to obtain client's certificate and/or key")
    92  		}
    93  
    94  		// Load the CA for the server certificate if present.
    95  		cp := x509.NewCertPool()
    96  		if cfg.Opts.RemoteCertificate.CACertPath != "" {
    97  			serverCA, err := ioutil.ReadFile(cfg.Opts.RemoteCertificate.CACertPath)
    98  			if err != nil {
    99  				return nil, errors.Wrap(err, "failed to obtain server's CA certificate")
   100  			}
   101  			if !cp.AppendCertsFromPEM(serverCA) {
   102  				return nil, errors.Wrap(err, "failed to add server's CA certificate to pool")
   103  			}
   104  		}
   105  
   106  		tlsCfg := &tls.Config{
   107  			Certificates: []tls.Certificate{clientPair},
   108  			RootCAs:      cp,
   109  			MinVersion:   tls.VersionTLS13,
   110  		}
   111  		clientCreds = credentials.NewTLS(tlsCfg)
   112  	}
   113  
   114  	grpcOpts := []grpc.DialOption{
   115  		// Receive large messages without erroring.
   116  		grpc.WithDefaultCallOptions(grpc.MaxCallRecvMsgSize(cfg.MaxMessageSize)),
   117  	}
   118  	if cfg.Opts.RemoteCertificate.RequireTls {
   119  		// Require TLS with client certificate.
   120  		grpcOpts = append(grpcOpts, grpc.WithTransportCredentials(clientCreds))
   121  	} else {
   122  		grpcOpts = append(grpcOpts, grpc.WithInsecure())
   123  	}
   124  
   125  	conn, err := grpc.Dial(cfg.Opts.RemoteAddr, grpcOpts...)
   126  	if err != nil {
   127  		return nil, errors.New("failed to connect to remote wallet")
   128  	}
   129  	client := validatorpb.NewRemoteSignerClient(conn)
   130  	k := &Keymanager{
   131  		opts:                cfg.Opts,
   132  		client:              client,
   133  		orderedPubKeys:      make([][48]byte, 0),
   134  		accountsChangedFeed: new(event.Feed),
   135  	}
   136  	return k, nil
   137  }
   138  
   139  // UnmarshalOptionsFile attempts to JSON unmarshal a keymanager
   140  // options file into a struct.
   141  func UnmarshalOptionsFile(r io.ReadCloser) (*KeymanagerOpts, error) {
   142  	enc, err := ioutil.ReadAll(r)
   143  	if err != nil {
   144  		return nil, errors.Wrap(err, "could not read config")
   145  	}
   146  	defer func() {
   147  		if err := r.Close(); err != nil {
   148  			log.Errorf("Could not close keymanager config file: %v", err)
   149  		}
   150  	}()
   151  	opts := &KeymanagerOpts{
   152  		RemoteCertificate: &CertificateConfig{RequireTls: true},
   153  	}
   154  	if err := json.Unmarshal(enc, opts); err != nil {
   155  		return nil, errors.Wrap(err, "could not JSON unmarshal")
   156  	}
   157  	return opts, nil
   158  }
   159  
   160  // MarshalOptionsFile for the keymanager.
   161  func MarshalOptionsFile(_ context.Context, cfg *KeymanagerOpts) ([]byte, error) {
   162  	return json.MarshalIndent(cfg, "", "\t")
   163  }
   164  
   165  // String pretty-print of a remote keymanager options.
   166  func (opts *KeymanagerOpts) String() string {
   167  	au := aurora.NewAurora(true)
   168  	var b strings.Builder
   169  	strAddr := fmt.Sprintf("%s: %s\n", au.BrightMagenta("Remote gRPC address"), opts.RemoteAddr)
   170  	if _, err := b.WriteString(strAddr); err != nil {
   171  		log.Error(err)
   172  		return ""
   173  	}
   174  	strRequireTls := fmt.Sprintf(
   175  		"%s: %t\n", au.BrightMagenta("Require TLS"), opts.RemoteCertificate.RequireTls,
   176  	)
   177  	if _, err := b.WriteString(strRequireTls); err != nil {
   178  		log.Error(err)
   179  		return ""
   180  	}
   181  	strCrt := fmt.Sprintf(
   182  		"%s: %s\n", au.BrightMagenta("Client cert path"), opts.RemoteCertificate.ClientCertPath,
   183  	)
   184  	if _, err := b.WriteString(strCrt); err != nil {
   185  		log.Error(err)
   186  		return ""
   187  	}
   188  	strKey := fmt.Sprintf(
   189  		"%s: %s\n", au.BrightMagenta("Client key path"), opts.RemoteCertificate.ClientKeyPath,
   190  	)
   191  	if _, err := b.WriteString(strKey); err != nil {
   192  		log.Error(err)
   193  		return ""
   194  	}
   195  	strCa := fmt.Sprintf(
   196  		"%s: %s\n", au.BrightMagenta("CA cert path"), opts.RemoteCertificate.CACertPath,
   197  	)
   198  	if _, err := b.WriteString(strCa); err != nil {
   199  		log.Error(err)
   200  		return ""
   201  	}
   202  	return b.String()
   203  }
   204  
   205  // KeymanagerOpts for the remote keymanager.
   206  func (km *Keymanager) KeymanagerOpts() *KeymanagerOpts {
   207  	return km.opts
   208  }
   209  
   210  // ReloadPublicKeys reloads public keys.
   211  func (km *Keymanager) ReloadPublicKeys(ctx context.Context) ([][48]byte, error) {
   212  	pubKeys, err := km.FetchValidatingPublicKeys(ctx)
   213  	if err != nil {
   214  		return nil, errors.Wrap(err, "could not reload public keys")
   215  	}
   216  
   217  	sort.Slice(pubKeys, func(i, j int) bool { return bytes.Compare(pubKeys[i][:], pubKeys[j][:]) == -1 })
   218  	if len(km.orderedPubKeys) != len(pubKeys) {
   219  		log.Info(keymanager.KeysReloaded)
   220  		km.accountsChangedFeed.Send(pubKeys)
   221  	} else {
   222  		for i := range km.orderedPubKeys {
   223  			if !bytes.Equal(km.orderedPubKeys[i][:], pubKeys[i][:]) {
   224  				log.Info(keymanager.KeysReloaded)
   225  				km.accountsChangedFeed.Send(pubKeys)
   226  				break
   227  			}
   228  		}
   229  	}
   230  
   231  	km.orderedPubKeys = pubKeys
   232  	return km.orderedPubKeys, nil
   233  }
   234  
   235  // FetchValidatingPublicKeys fetches the list of public keys that should be used to validate with.
   236  func (km *Keymanager) FetchValidatingPublicKeys(ctx context.Context) ([][48]byte, error) {
   237  	resp, err := km.client.ListValidatingPublicKeys(ctx, &empty.Empty{})
   238  	if err != nil {
   239  		return nil, errors.Wrap(err, "could not list accounts from remote server")
   240  	}
   241  	pubKeys := make([][48]byte, len(resp.ValidatingPublicKeys))
   242  	for i := range resp.ValidatingPublicKeys {
   243  		pubKeys[i] = bytesutil.ToBytes48(resp.ValidatingPublicKeys[i])
   244  	}
   245  	return pubKeys, nil
   246  }
   247  
   248  // Sign signs a message for a validator key via a gRPC request.
   249  func (km *Keymanager) Sign(ctx context.Context, req *validatorpb.SignRequest) (bls.Signature, error) {
   250  	resp, err := km.client.Sign(ctx, req)
   251  	if err != nil {
   252  		return nil, err
   253  	}
   254  	switch resp.Status {
   255  	case validatorpb.SignResponse_DENIED:
   256  		return nil, ErrSigningDenied
   257  	case validatorpb.SignResponse_FAILED:
   258  		return nil, ErrSigningFailed
   259  	}
   260  	return bls.SignatureFromBytes(resp.Signature)
   261  }
   262  
   263  // SubscribeAccountChanges creates an event subscription for a channel
   264  // to listen for public key changes at runtime, such as when new validator accounts
   265  // are imported into the keymanager while the validator process is running.
   266  func (km *Keymanager) SubscribeAccountChanges(pubKeysChan chan [][48]byte) event.Subscription {
   267  	return km.accountsChangedFeed.Subscribe(pubKeysChan)
   268  }