rpcserver.go raw

   1  package wallet
   2  
   3  import (
   4  	"crypto/tls"
   5  	"errors"
   6  	"fmt"
   7  	"io/ioutil"
   8  	"net"
   9  	"os"
  10  	"path/filepath"
  11  	"runtime"
  12  	"strings"
  13  	"time"
  14  
  15  	"github.com/p9c/p9/pkg/util"
  16  	"github.com/p9c/p9/pod/config"
  17  	"github.com/p9c/p9/pod/state"
  18  )
  19  
  20  type listenFunc func(net string, laddr string) (net.Listener, error)
  21  
  22  // GenerateRPCKeyPair generates a new RPC TLS keypair and writes the cert and possibly also the key in PEM format to the
  23  // paths specified by the config. If successful, the new keypair is returned.
  24  func GenerateRPCKeyPair(config *config.Config, writeKey bool) (tls.Certificate, error) {
  25  	D.Ln("generating TLS certificates")
  26  	// Create directories for cert and key files if they do not yet exist.
  27  	D.Ln("rpc tls ", *config.RPCCert, " ", *config.RPCKey)
  28  	certDir, _ := filepath.Split(config.RPCCert.V())
  29  	keyDir, _ := filepath.Split(config.RPCKey.V())
  30  	e := os.MkdirAll(certDir, 0700)
  31  	if e != nil {
  32  		return tls.Certificate{}, e
  33  	}
  34  	e = os.MkdirAll(keyDir, 0700)
  35  	if e != nil {
  36  		return tls.Certificate{}, e
  37  	}
  38  	// Generate cert pair.
  39  	org := "pod/wallet autogenerated cert"
  40  	validUntil := time.Now().Add(time.Hour * 24 * 365 * 10)
  41  	cert, key, e := util.NewTLSCertPair(org, validUntil, nil)
  42  	if e != nil {
  43  		return tls.Certificate{}, e
  44  	}
  45  	keyPair, e := tls.X509KeyPair(cert, key)
  46  	if e != nil {
  47  		return tls.Certificate{}, e
  48  	}
  49  	// Write cert and (potentially) the key files.
  50  	e = ioutil.WriteFile(config.RPCCert.V(), cert, 0600)
  51  	if e != nil {
  52  		rmErr := os.Remove(config.RPCCert.V())
  53  		if rmErr != nil {
  54  			E.Ln("cannot remove written certificates:", rmErr)
  55  		}
  56  		return tls.Certificate{}, e
  57  	}
  58  	e = ioutil.WriteFile(config.CAFile.V(), cert, 0600)
  59  	if e != nil {
  60  		rmErr := os.Remove(config.RPCCert.V())
  61  		if rmErr != nil {
  62  			E.Ln("cannot remove written certificates:", rmErr)
  63  		}
  64  		return tls.Certificate{}, e
  65  	}
  66  	if writeKey {
  67  		e = ioutil.WriteFile(config.RPCKey.V(), key, 0600)
  68  		if e != nil {
  69  			rmErr := os.Remove(config.RPCCert.V())
  70  			if rmErr != nil {
  71  				E.Ln("cannot remove written certificates:", rmErr)
  72  			}
  73  			rmErr = os.Remove(config.CAFile.V())
  74  			if rmErr != nil {
  75  				E.Ln("cannot remove written certificates:", rmErr)
  76  			}
  77  			return tls.Certificate{}, e
  78  		}
  79  	}
  80  	I.Ln("done generating TLS certificates")
  81  	return keyPair, nil
  82  }
  83  
  84  // makeListeners splits the normalized listen addresses into IPv4 and IPv6 addresses and creates new net.Listeners for
  85  // each with the passed listen func. Invalid addresses are logged and skipped.
  86  func makeListeners(normalizedListenAddrs []string, listen listenFunc) []net.Listener {
  87  	ipv4Addrs := make([]string, 0, len(normalizedListenAddrs)*2)
  88  	// ipv6Addrs := make([]string, 0, len(normalizedListenAddrs)*2)
  89  	for _, addr := range normalizedListenAddrs {
  90  		var host string
  91  		var e error
  92  		host, _, e = net.SplitHostPort(addr)
  93  		if e != nil {
  94  			// Shouldn't happen due to already being normalized.
  95  			E.F(
  96  				"`%s` is not a normalized listener address", addr,
  97  			)
  98  			continue
  99  		}
 100  		// Empty host or host of * on plan9 is both IPv4 and IPv6.
 101  		if host == "" || (host == "*" && runtime.GOOS == "plan9") {
 102  			ipv4Addrs = append(ipv4Addrs, addr)
 103  			// ipv6Addrs = append(ipv6Addrs, addr)
 104  			continue
 105  		}
 106  		// Remove the IPv6 zone from the host, if present. The zone prevents ParseIP from correctly parsing the IP
 107  		// address. ResolveIPAddr is intentionally not used here due to the possibility of leaking a DNS query over Tor
 108  		// if the host is a hostname and not an IP address.
 109  		zoneIndex := strings.Index(host, "%")
 110  		if zoneIndex != -1 {
 111  			host = host[:zoneIndex]
 112  		}
 113  		ip := net.ParseIP(host)
 114  		switch {
 115  		case ip == nil:
 116  			W.F("`%s` is not a valid IP address", host)
 117  		case ip.To4() == nil:
 118  			// ipv6Addrs = append(ipv6Addrs, addr)
 119  		default:
 120  			ipv4Addrs = append(ipv4Addrs, addr)
 121  		}
 122  	}
 123  	listeners := make(
 124  		[]net.Listener, 0,
 125  		// len(ipv6Addrs)+
 126  		len(ipv4Addrs),
 127  	)
 128  	for _, addr := range ipv4Addrs {
 129  		listener, e := listen("tcp4", addr)
 130  		if e != nil {
 131  			W.F(
 132  				"Can't listen on %s: %v", addr, e,
 133  			)
 134  			continue
 135  		}
 136  		listeners = append(listeners, listener)
 137  	}
 138  	// for _, addr := range ipv6Addrs {
 139  	// 	listener, e := listen("tcp6", addr)
 140  	// 	if e != nil  {
 141  	// 		Warnf(
 142  	// 			"Can't listen on %s: %v", addr, e,
 143  	// 		)
 144  	// 		continue
 145  	// 	}
 146  	// 	listeners = append(listeners, listener)
 147  	// }
 148  	return listeners
 149  }
 150  
 151  // OpenRPCKeyPair creates or loads the RPC TLS keypair specified by the
 152  // application config. This function respects the pod.Config.OneTimeTLSKey
 153  // setting.
 154  func OpenRPCKeyPair(config *config.Config) (tls.Certificate, error) {
 155  	// Chk for existence of the TLS key file. If one time TLS keys are enabled but a
 156  	// key already exists, this function should error since it's possible that a
 157  	// persistent certificate was copied to a remote machine. Otherwise, generate a
 158  	// new keypair when the key is missing. When generating new persistent keys,
 159  	// overwriting an existing cert is acceptable if the previous execution used a
 160  	// one time TLS key. Otherwise, both the cert and key should be read from disk.
 161  	// If the cert is missing, the read error will occur in LoadX509KeyPair.
 162  	_, e := os.Stat(config.RPCKey.V())
 163  	keyExists := !os.IsNotExist(e)
 164  	switch {
 165  	case config.OneTimeTLSKey.True() && keyExists:
 166  		if e = fmt.Errorf(
 167  			"one time TLS keys are enabled, but TLS key `%s` already exists", config.RPCKey.V(),
 168  		); E.Chk(e) {
 169  		}
 170  		return tls.Certificate{}, e
 171  	case config.OneTimeTLSKey.True():
 172  		return GenerateRPCKeyPair(config, false)
 173  	case !keyExists:
 174  		return GenerateRPCKeyPair(config, true)
 175  	default:
 176  		return tls.LoadX509KeyPair(config.RPCCert.V(), config.RPCKey.V())
 177  	}
 178  }
 179  func startRPCServers(cx *state.State, walletLoader *Loader) (*Server, error) {
 180  	T.Ln("startRPCServers")
 181  	var (
 182  		legacyServer *Server
 183  		walletListen = net.Listen
 184  		keyPair      tls.Certificate
 185  		e            error
 186  	)
 187  	if cx.Config.ClientTLS.False() {
 188  		I.Ln("server TLS is disabled - only legacy RPC may be used")
 189  	} else {
 190  		keyPair, e = OpenRPCKeyPair(cx.Config)
 191  		if e != nil {
 192  			return nil, e
 193  		}
 194  		// Change the standard net.Listen function to the tls one.
 195  		tlsConfig := &tls.Config{
 196  			Certificates:       []tls.Certificate{keyPair},
 197  			MinVersion:         tls.VersionTLS12,
 198  			NextProtos:         []string{"h2"}, // HTTP/2 over TLS
 199  			InsecureSkipVerify: cx.Config.TLSSkipVerify.True(),
 200  		}
 201  		walletListen = func(net string, laddr string) (net.Listener, error) {
 202  			return tls.Listen(net, laddr, tlsConfig)
 203  		}
 204  	}
 205  	if cx.Config.Username.V() == "" || cx.Config.Password.V() == "" {
 206  		I.Ln("legacy RPC server disabled (requires username and password)")
 207  	} else if len(cx.Config.WalletRPCListeners.S()) != 0 {
 208  		listeners := makeListeners(cx.Config.WalletRPCListeners.S(), walletListen)
 209  		if len(listeners) == 0 {
 210  			e := errors.New("failed to create listeners for legacy RPC server")
 211  			return nil, e
 212  		}
 213  		opts := Options{
 214  			Username:            cx.Config.Username.V(),
 215  			Password:            cx.Config.Password.V(),
 216  			MaxPOSTClients:      int64(cx.Config.WalletRPCMaxClients.V()),
 217  			MaxWebsocketClients: int64(cx.Config.WalletRPCMaxWebsockets.V()),
 218  		}
 219  		legacyServer = NewServer(&opts, walletLoader, listeners, nil)
 220  	}
 221  	// Error when no legacy RPC servers can be started.
 222  	if legacyServer == nil {
 223  		return nil, errors.New("no suitable RPC services can be started")
 224  	}
 225  	return legacyServer, nil
 226  }
 227  
 228  // startWalletRPCServices associates each of the (optionally-nil) RPC servers with a wallet to enable remote wallet
 229  // access. For the legacy JSON-RPC server it enables methods that require a loaded wallet.
 230  func startWalletRPCServices(wallet *Wallet, legacyServer *Server) {
 231  	if legacyServer != nil {
 232  		D.Ln("starting legacy wallet rpc server")
 233  		legacyServer.RegisterWallet(wallet)
 234  	}
 235  }
 236