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 }