decred.org/dcrwallet/v3@v3.1.0/rpcserver.go (about)

     1  // Copyright (c) 2013-2015 The btcsuite developers
     2  // Copyright (c) 2015-2020 The Decred developers
     3  // Use of this source code is governed by an ISC
     4  // license that can be found in the LICENSE file.
     5  
     6  package main
     7  
     8  import (
     9  	"bytes"
    10  	"context"
    11  	"crypto/rand"
    12  	"crypto/tls"
    13  	"crypto/x509"
    14  	"crypto/x509/pkix"
    15  	"encoding/pem"
    16  	"fmt"
    17  	"math/big"
    18  	"net"
    19  	"os"
    20  	"path/filepath"
    21  	"runtime"
    22  	"strings"
    23  	"time"
    24  
    25  	"decred.org/dcrwallet/v3/errors"
    26  	"decred.org/dcrwallet/v3/internal/cfgutil"
    27  	"decred.org/dcrwallet/v3/internal/loader"
    28  	"decred.org/dcrwallet/v3/internal/loggers"
    29  	"decred.org/dcrwallet/v3/internal/rpc/jsonrpc"
    30  	"decred.org/dcrwallet/v3/internal/rpc/rpcserver"
    31  
    32  	"google.golang.org/grpc"
    33  	"google.golang.org/grpc/codes"
    34  	"google.golang.org/grpc/credentials"
    35  	"google.golang.org/grpc/peer"
    36  	"google.golang.org/grpc/status"
    37  )
    38  
    39  // openRPCKeyPair creates or loads the RPC TLS keypair specified by the
    40  // application config.  This function respects the cfg.OneTimeTLSKey setting.
    41  func openRPCKeyPair() (tls.Certificate, error) {
    42  	// Check for existence of the TLS key file.  If one time TLS keys are
    43  	// enabled but a key already exists, this function should error since
    44  	// it's possible that a persistent certificate was copied to a remote
    45  	// machine.  Otherwise, generate a new keypair when the key is missing.
    46  	// When generating new persistent keys, overwriting an existing cert is
    47  	// acceptable if the previous execution used a one time TLS key.
    48  	// Otherwise, both the cert and key should be read from disk.  If the
    49  	// cert is missing, the read error will occur in LoadX509KeyPair.
    50  	_, e := os.Stat(cfg.RPCKey.Value)
    51  	keyExists := !os.IsNotExist(e)
    52  	switch {
    53  	case cfg.OneTimeTLSKey && keyExists:
    54  		err := errors.Errorf("one time TLS keys are enabled, but TLS key "+
    55  			"`%s` already exists", cfg.RPCKey)
    56  		return tls.Certificate{}, err
    57  	case cfg.OneTimeTLSKey:
    58  		return generateRPCKeyPair(false)
    59  	case !keyExists:
    60  		return generateRPCKeyPair(true)
    61  	default:
    62  		return tls.LoadX509KeyPair(cfg.RPCCert.Value, cfg.RPCKey.Value)
    63  	}
    64  }
    65  
    66  // generateRPCKeyPair generates a new RPC TLS keypair and writes the cert and
    67  // possibly also the key in PEM format to the paths specified by the config.  If
    68  // successful, the new keypair is returned.
    69  func generateRPCKeyPair(writeKey bool) (tls.Certificate, error) {
    70  	log.Infof("Generating TLS certificates...")
    71  
    72  	// Create directories for cert and key files if they do not yet exist.
    73  	certDir, _ := filepath.Split(cfg.RPCCert.Value)
    74  	keyDir, _ := filepath.Split(cfg.RPCKey.Value)
    75  	err := os.MkdirAll(certDir, 0700)
    76  	if err != nil {
    77  		return tls.Certificate{}, err
    78  	}
    79  	err = os.MkdirAll(keyDir, 0700)
    80  	if err != nil {
    81  		return tls.Certificate{}, err
    82  	}
    83  
    84  	// Generate cert pair.
    85  	org := "dcrwallet autogenerated cert"
    86  	validUntil := time.Now().Add(time.Hour * 24 * 365 * 10)
    87  	cert, key, err := cfg.TLSCurve.CertGen(org, validUntil, nil)
    88  	if err != nil {
    89  		return tls.Certificate{}, err
    90  	}
    91  	keyPair, err := tls.X509KeyPair(cert, key)
    92  	if err != nil {
    93  		return tls.Certificate{}, err
    94  	}
    95  
    96  	// Write cert and (potentially) the key files.
    97  	err = os.WriteFile(cfg.RPCCert.Value, cert, 0600)
    98  	if err != nil {
    99  		return tls.Certificate{}, err
   100  	}
   101  	if writeKey {
   102  		err = os.WriteFile(cfg.RPCKey.Value, key, 0600)
   103  		if err != nil {
   104  			rmErr := os.Remove(cfg.RPCCert.Value)
   105  			if rmErr != nil {
   106  				log.Warnf("Cannot remove written certificates: %v",
   107  					rmErr)
   108  			}
   109  			return tls.Certificate{}, err
   110  		}
   111  	}
   112  
   113  	log.Info("Done generating TLS certificates")
   114  	return keyPair, nil
   115  }
   116  
   117  func randomX509SerialNumber() (*big.Int, error) {
   118  	serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128)
   119  	serialNumber, err := rand.Int(rand.Reader, serialNumberLimit)
   120  	if err != nil {
   121  		return nil, fmt.Errorf("failed to generate serial number: %s", err)
   122  	}
   123  	return serialNumber, nil
   124  }
   125  
   126  // End of ASN.1 time
   127  var endOfTime = time.Date(2049, 12, 31, 23, 59, 59, 0, time.UTC)
   128  
   129  type ClientCA struct {
   130  	CertBlock  []byte
   131  	Cert       *x509.Certificate
   132  	PrivateKey interface{}
   133  }
   134  
   135  func generateAuthority(pub, priv interface{}) (*ClientCA, error) {
   136  	validUntil := time.Now().Add(time.Hour * 24 * 365 * 10)
   137  	now := time.Now()
   138  	if validUntil.After(endOfTime) {
   139  		validUntil = endOfTime
   140  	}
   141  	if validUntil.Before(now) {
   142  		return nil, fmt.Errorf("valid until date %v already elapsed", validUntil)
   143  	}
   144  	serialNumber, err := randomX509SerialNumber()
   145  	if err != nil {
   146  		return nil, err
   147  	}
   148  	template := &x509.Certificate{
   149  		SerialNumber: serialNumber,
   150  		Subject: pkix.Name{
   151  			CommonName:         "dcrwallet",
   152  			Organization:       []string{"dcrwallet"},
   153  			OrganizationalUnit: []string{"dcrwallet certificate authority"},
   154  		},
   155  		NotBefore:             now.Add(-time.Hour * 24),
   156  		NotAfter:              validUntil,
   157  		KeyUsage:              x509.KeyUsageCertSign,
   158  		BasicConstraintsValid: true,
   159  		IsCA:                  true,
   160  	}
   161  	cert, err := x509.CreateCertificate(rand.Reader, template, template, pub, priv)
   162  	if err != nil {
   163  		return nil, err
   164  	}
   165  	buf := new(bytes.Buffer)
   166  	err = pem.Encode(buf, &pem.Block{Type: "CERTIFICATE", Bytes: cert})
   167  	if err != nil {
   168  		return nil, fmt.Errorf("failed to encode certificate: %v", err)
   169  	}
   170  	certBlock := buf.Bytes()
   171  
   172  	x509Cert, err := x509.ParseCertificate(cert)
   173  	if err != nil {
   174  		return nil, err
   175  	}
   176  
   177  	clientCA := &ClientCA{
   178  		CertBlock:  certBlock,
   179  		Cert:       x509Cert,
   180  		PrivateKey: priv,
   181  	}
   182  	return clientCA, nil
   183  }
   184  
   185  func marshalPrivateKey(key interface{}) ([]byte, error) {
   186  	der, err := x509.MarshalPKCS8PrivateKey(key)
   187  	if err != nil {
   188  		return nil, fmt.Errorf("failed to marshal private key: %v", err)
   189  	}
   190  	buf := new(bytes.Buffer)
   191  	err = pem.Encode(buf, &pem.Block{Type: "PRIVATE KEY", Bytes: der})
   192  	if err != nil {
   193  		return nil, fmt.Errorf("failed to encode private key: %v", err)
   194  	}
   195  	return buf.Bytes(), nil
   196  }
   197  
   198  func createSignedClientCert(pub, caPriv interface{}, ca *x509.Certificate) ([]byte, error) {
   199  	serialNumber, err := randomX509SerialNumber()
   200  	if err != nil {
   201  		return nil, err
   202  	}
   203  	template := &x509.Certificate{
   204  		SerialNumber: serialNumber,
   205  		NotBefore:    time.Now().Add(-time.Hour * 24),
   206  		NotAfter:     ca.NotAfter,
   207  		Subject: pkix.Name{
   208  			CommonName:         "dcrwallet",
   209  			Organization:       []string{"dcrwallet"},
   210  			OrganizationalUnit: []string{"dcrwallet client certificate"},
   211  		},
   212  	}
   213  	cert, err := x509.CreateCertificate(rand.Reader, template, ca, pub, caPriv)
   214  	if err != nil {
   215  		return nil, err
   216  	}
   217  	buf := new(bytes.Buffer)
   218  	err = pem.Encode(buf, &pem.Block{Type: "CERTIFICATE", Bytes: cert})
   219  	if err != nil {
   220  		return nil, fmt.Errorf("failed to encode certificate: %v", err)
   221  	}
   222  	return buf.Bytes(), nil
   223  }
   224  
   225  func generateClientKeyPair(caPriv interface{}, ca *x509.Certificate) (cert, key []byte, err error) {
   226  	pub, priv, err := cfg.TLSCurve.GenerateKeyPair(rand.Reader)
   227  	if err != nil {
   228  		return
   229  	}
   230  	key, err = marshalPrivateKey(priv)
   231  	if err != nil {
   232  		return
   233  	}
   234  	cert, err = createSignedClientCert(pub, caPriv, ca)
   235  	if err != nil {
   236  		return
   237  	}
   238  	return cert, key, nil
   239  }
   240  
   241  func startRPCServers(walletLoader *loader.Loader) (*grpc.Server, *jsonrpc.Server, error) {
   242  	var jsonrpcAddrNotifier jsonrpcListenerEventServer
   243  	var grpcAddrNotifier grpcListenerEventServer
   244  	if cfg.RPCListenerEvents {
   245  		jsonrpcAddrNotifier = newJSONRPCListenerEventServer(outgoingPipeMessages)
   246  		grpcAddrNotifier = newGRPCListenerEventServer(outgoingPipeMessages)
   247  	}
   248  
   249  	var (
   250  		server         *grpc.Server
   251  		jsonrpcServer  *jsonrpc.Server
   252  		jsonrpcListen  = net.Listen
   253  		keyPair        tls.Certificate
   254  		clientCAsExist bool
   255  		err            error
   256  	)
   257  	if cfg.DisableServerTLS {
   258  		log.Info("Server TLS is disabled.  Only JSON-RPC may be used")
   259  	} else {
   260  		keyPair, err = openRPCKeyPair()
   261  		if err != nil {
   262  			return nil, nil, err
   263  		}
   264  
   265  		tlsConfig := &tls.Config{
   266  			Certificates: []tls.Certificate{keyPair},
   267  			MinVersion:   tls.VersionTLS12,
   268  			ClientCAs:    x509.NewCertPool(),
   269  		}
   270  		clientCAsExist, _ = cfgutil.FileExists(cfg.ClientCAFile.Value)
   271  		if clientCAsExist {
   272  			cafile, err := os.ReadFile(cfg.ClientCAFile.Value)
   273  			if err != nil {
   274  				return nil, nil, err
   275  			}
   276  			if !tlsConfig.ClientCAs.AppendCertsFromPEM(cafile) {
   277  				log.Warnf("No certificates added from CA file %v",
   278  					cfg.ClientCAFile)
   279  			}
   280  		}
   281  		if cfg.JSONRPCAuthType == "clientcert" {
   282  			tlsConfig.ClientAuth = tls.RequireAndVerifyClientCert
   283  		}
   284  		if cfg.IssueClientCert {
   285  			pub, priv, err := cfg.TLSCurve.GenerateKeyPair(rand.Reader)
   286  			if err != nil {
   287  				return nil, nil, err
   288  			}
   289  			ca, err := generateAuthority(pub, priv)
   290  			if err != nil {
   291  				return nil, nil, err
   292  			}
   293  			certBlock, keyBlock, err := generateClientKeyPair(ca.PrivateKey, ca.Cert)
   294  			if err != nil {
   295  				return nil, nil, err
   296  			}
   297  			tlsConfig.ClientCAs.AddCert(ca.Cert)
   298  
   299  			s := newIssuedClientCertEventServer(outgoingPipeMessages)
   300  			s.notify(keyBlock, certBlock, ca.CertBlock)
   301  		}
   302  
   303  		// Change the standard net.Listen function to the tls one.
   304  		jsonrpcListen = func(net string, laddr string) (net.Listener, error) {
   305  			return tls.Listen(net, laddr, tlsConfig)
   306  		}
   307  
   308  		clientCAsExist = clientCAsExist || cfg.IssueClientCert
   309  		if !clientCAsExist && len(cfg.GRPCListeners) != 0 {
   310  			log.Warnf("gRPC server is configured with listeners, but no "+
   311  				"trusted client certificates exist (looked in %v)",
   312  				cfg.ClientCAFile)
   313  		} else if clientCAsExist && len(cfg.GRPCListeners) != 0 {
   314  			tlsConfig := tlsConfig.Clone()
   315  			tlsConfig.ClientAuth = tls.RequireAndVerifyClientCert
   316  			listeners := makeListeners(cfg.GRPCListeners, net.Listen)
   317  			if len(listeners) == 0 {
   318  				err := errors.New("failed to create listeners for RPC server")
   319  				return nil, nil, err
   320  			}
   321  			server = grpc.NewServer(
   322  				grpc.Creds(credentials.NewTLS(tlsConfig)),
   323  				grpc.StreamInterceptor(interceptStreaming),
   324  				grpc.UnaryInterceptor(interceptUnary),
   325  			)
   326  			rpcserver.RegisterServices(server)
   327  			rpcserver.StartWalletLoaderService(server, walletLoader, activeNet)
   328  			rpcserver.StartTicketBuyerV2Service(server, walletLoader)
   329  			rpcserver.StartAccountMixerService(server, walletLoader)
   330  			rpcserver.StartAgendaService(server, activeNet.Params)
   331  			rpcserver.StartDecodeMessageService(server, activeNet.Params)
   332  			rpcserver.StartMessageVerificationService(server, activeNet.Params)
   333  			for _, lis := range listeners {
   334  				lis := lis
   335  				go func() {
   336  					laddr := lis.Addr().String()
   337  					grpcAddrNotifier.notify(laddr)
   338  					log.Infof("gRPC server listening on %s", laddr)
   339  					err := server.Serve(lis)
   340  					log.Tracef("Finished serving gRPC: %v", err)
   341  				}()
   342  			}
   343  		}
   344  	}
   345  
   346  	if !cfg.DisableServerTLS && len(cfg.LegacyRPCListeners) != 0 &&
   347  		cfg.JSONRPCAuthType == "clientcert" && !clientCAsExist {
   348  		log.Warnf("JSON-RPC TLS server is configured with listeners and "+
   349  			"client cert auth, but no trusted client certificates exist "+
   350  			"(looked in %v)", cfg.ClientCAFile)
   351  	} else if cfg.JSONRPCAuthType == "basic" && (cfg.Username == "" || cfg.Password == "") {
   352  		log.Info("JSON-RPC server disabled (basic auth requires username and " +
   353  			"password, and client cert authentication is not enabled)")
   354  	} else if len(cfg.LegacyRPCListeners) != 0 {
   355  		listeners := makeListeners(cfg.LegacyRPCListeners, jsonrpcListen)
   356  		if len(listeners) == 0 {
   357  			err := errors.New("failed to create listeners for JSON-RPC server")
   358  			return nil, nil, err
   359  		}
   360  		var user, pass string
   361  		if cfg.JSONRPCAuthType == "basic" {
   362  			user, pass = cfg.Username, cfg.Password
   363  		}
   364  		opts := jsonrpc.Options{
   365  			Username:            user,
   366  			Password:            pass,
   367  			MaxPOSTClients:      cfg.LegacyRPCMaxClients,
   368  			MaxWebsocketClients: cfg.LegacyRPCMaxWebsockets,
   369  			CSPPServer:          cfg.CSPPServer,
   370  			DialCSPPServer:      cfg.dialCSPPServer,
   371  			MixAccount:          cfg.mixedAccount,
   372  			MixBranch:           cfg.mixedBranch,
   373  			MixChangeAccount:    cfg.ChangeAccount,
   374  			VSPHost:             cfg.VSPOpts.URL,
   375  			VSPPubKey:           cfg.VSPOpts.PubKey,
   376  			VSPMaxFee:           cfg.VSPOpts.MaxFee.Amount,
   377  			TicketSplitAccount:  cfg.TicketSplitAccount,
   378  			Dial:                cfg.dial,
   379  		}
   380  		jsonrpcServer = jsonrpc.NewServer(&opts, activeNet.Params, walletLoader, listeners)
   381  		for _, lis := range listeners {
   382  			jsonrpcAddrNotifier.notify(lis.Addr().String())
   383  		}
   384  	}
   385  
   386  	// Error when neither the GRPC nor JSON-RPC servers can be started.
   387  	if server == nil && jsonrpcServer == nil {
   388  		return nil, nil, errors.New("no suitable RPC services can be started")
   389  	}
   390  
   391  	return server, jsonrpcServer, nil
   392  }
   393  
   394  // serviceName returns the package.service segment from the full gRPC method
   395  // name `/package.service/method`.
   396  func serviceName(method string) string {
   397  	// Slice off first /
   398  	method = method[1:]
   399  	// Keep everything before the next /
   400  	return method[:strings.IndexRune(method, '/')]
   401  }
   402  
   403  func interceptStreaming(srv interface{}, ss grpc.ServerStream, info *grpc.StreamServerInfo, handler grpc.StreamHandler) error {
   404  	p, ok := peer.FromContext(ss.Context())
   405  	if ok {
   406  		loggers.GrpcLog.Debugf("Streaming method %s invoked by %s", info.FullMethod,
   407  			p.Addr.String())
   408  	}
   409  	err := rpcserver.ServiceReady(serviceName(info.FullMethod))
   410  	if err != nil {
   411  		return err
   412  	}
   413  	err = handler(srv, ss)
   414  	if err != nil && ok {
   415  		logf := loggers.GrpcLog.Errorf
   416  		if status.Code(err) == codes.Canceled && done(ss.Context()) {
   417  			// Canceled contexts in streaming calls are expected
   418  			// when client-initiated, so only log them with debug
   419  			// level to reduce clutter.
   420  			logf = loggers.GrpcLog.Debugf
   421  		}
   422  
   423  		logf("Streaming method %s invoked by %s errored: %v",
   424  			info.FullMethod, p.Addr.String(), err)
   425  	}
   426  	return err
   427  }
   428  
   429  func interceptUnary(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (resp interface{}, err error) {
   430  	p, ok := peer.FromContext(ctx)
   431  	if ok {
   432  		loggers.GrpcLog.Debugf("Unary method %s invoked by %s", info.FullMethod,
   433  			p.Addr.String())
   434  	}
   435  	err = rpcserver.ServiceReady(serviceName(info.FullMethod))
   436  	if err != nil {
   437  		return nil, err
   438  	}
   439  	resp, err = handler(ctx, req)
   440  	if err != nil && ok {
   441  		loggers.GrpcLog.Errorf("Unary method %s invoked by %s errored: %v",
   442  			info.FullMethod, p.Addr.String(), err)
   443  	}
   444  	return resp, err
   445  }
   446  
   447  type listenFunc func(net string, laddr string) (net.Listener, error)
   448  
   449  // makeListeners splits the normalized listen addresses into IPv4 and IPv6
   450  // addresses and creates new net.Listeners for each with the passed listen func.
   451  // Invalid addresses are logged and skipped.
   452  func makeListeners(normalizedListenAddrs []string, listen listenFunc) []net.Listener {
   453  	ipv4Addrs := make([]string, 0, len(normalizedListenAddrs)*2)
   454  	ipv6Addrs := make([]string, 0, len(normalizedListenAddrs)*2)
   455  	for _, addr := range normalizedListenAddrs {
   456  		host, _, err := net.SplitHostPort(addr)
   457  		if err != nil {
   458  			// Shouldn't happen due to already being normalized.
   459  			log.Errorf("`%s` is not a normalized "+
   460  				"listener address", addr)
   461  			continue
   462  		}
   463  
   464  		// Empty host or host of * on plan9 is both IPv4 and IPv6.
   465  		if host == "" || (host == "*" && runtime.GOOS == "plan9") {
   466  			ipv4Addrs = append(ipv4Addrs, addr)
   467  			ipv6Addrs = append(ipv6Addrs, addr)
   468  			continue
   469  		}
   470  
   471  		// Remove the IPv6 zone from the host, if present.  The zone
   472  		// prevents ParseIP from correctly parsing the IP address.
   473  		// ResolveIPAddr is intentionally not used here due to the
   474  		// possibility of leaking a DNS query over Tor if the host is a
   475  		// hostname and not an IP address.
   476  		zoneIndex := strings.Index(host, "%")
   477  		if zoneIndex != -1 {
   478  			host = host[:zoneIndex]
   479  		}
   480  
   481  		ip := net.ParseIP(host)
   482  		switch {
   483  		case ip == nil:
   484  			log.Warnf("`%s` is not a valid IP address", host)
   485  		case ip.To4() == nil:
   486  			ipv6Addrs = append(ipv6Addrs, addr)
   487  		default:
   488  			ipv4Addrs = append(ipv4Addrs, addr)
   489  		}
   490  	}
   491  	listeners := make([]net.Listener, 0, len(ipv6Addrs)+len(ipv4Addrs))
   492  	for _, addr := range ipv4Addrs {
   493  		listener, err := listen("tcp4", addr)
   494  		if err != nil {
   495  			log.Warnf("Can't listen on %s: %v", addr, err)
   496  			continue
   497  		}
   498  		listeners = append(listeners, listener)
   499  	}
   500  	for _, addr := range ipv6Addrs {
   501  		listener, err := listen("tcp6", addr)
   502  		if err != nil {
   503  			log.Warnf("Can't listen on %s: %v", addr, err)
   504  			continue
   505  		}
   506  		listeners = append(listeners, listener)
   507  	}
   508  	return listeners
   509  }