Add option for one time TLS keys.
This option prevents the RPC server TLS key from ever being written to disk. This is performed by generating a new certificate pair each startup and writing (possibly overwriting) the certificate but not the key. Closes #359.
This commit is contained in:
parent
97963b47ce
commit
567752ea9b
3 changed files with 47 additions and 20 deletions
|
@ -94,6 +94,7 @@ type config struct {
|
||||||
// aren't considered legacy.
|
// aren't considered legacy.
|
||||||
RPCCert string `long:"rpccert" description:"File containing the certificate file"`
|
RPCCert string `long:"rpccert" description:"File containing the certificate file"`
|
||||||
RPCKey string `long:"rpckey" description:"File containing the certificate key"`
|
RPCKey string `long:"rpckey" description:"File containing the certificate key"`
|
||||||
|
OneTimeTLSKey bool `long:"onetimetlskey" description:"Generate a new TLS certpair at startup, but only write the certificate to disk"`
|
||||||
DisableServerTLS bool `long:"noservertls" description:"Disable TLS for the RPC server -- NOTE: This is only allowed if the RPC server is bound to localhost"`
|
DisableServerTLS bool `long:"noservertls" description:"Disable TLS for the RPC server -- NOTE: This is only allowed if the RPC server is bound to localhost"`
|
||||||
LegacyRPCListeners []string `long:"rpclisten" description:"Listen for legacy RPC connections on this interface/port (default port: 18332, mainnet: 8332, simnet: 18554)"`
|
LegacyRPCListeners []string `long:"rpclisten" description:"Listen for legacy RPC connections on this interface/port (default port: 18332, mainnet: 8332, simnet: 18554)"`
|
||||||
LegacyRPCMaxClients int64 `long:"rpcmaxclients" description:"Max number of legacy RPC clients for standard connections"`
|
LegacyRPCMaxClients int64 `long:"rpcmaxclients" description:"Max number of legacy RPC clients for standard connections"`
|
||||||
|
|
47
rpcserver.go
47
rpcserver.go
|
@ -19,6 +19,7 @@ package main
|
||||||
import (
|
import (
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
"errors"
|
"errors"
|
||||||
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"net"
|
"net"
|
||||||
"os"
|
"os"
|
||||||
|
@ -36,23 +37,36 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
// openRPCKeyPair creates or loads the RPC TLS keypair specified by the
|
// openRPCKeyPair creates or loads the RPC TLS keypair specified by the
|
||||||
// application config.
|
// application config. This function respects the cfg.OneTimeTLSKey setting.
|
||||||
func openRPCKeyPair() (tls.Certificate, error) {
|
func openRPCKeyPair() (tls.Certificate, error) {
|
||||||
// Check for existence of cert file and key file. Generate a new
|
// Check for existence of the TLS key file. If one time TLS keys are
|
||||||
// keypair if both are missing. If one exists but not the other, the
|
// enabled but a key already exists, this function should error since
|
||||||
// error will occur in LoadX509KeyPair.
|
// it's possible that a persistent certificate was copied to a remote
|
||||||
_, e1 := os.Stat(cfg.RPCKey)
|
// machine. Otherwise, generate a new keypair when the key is missing.
|
||||||
_, e2 := os.Stat(cfg.RPCCert)
|
// When generating new persistent keys, overwriting an existing cert is
|
||||||
if os.IsNotExist(e1) && os.IsNotExist(e2) {
|
// acceptable if the previous execution used a one time TLS key.
|
||||||
return generateRPCKeyPair()
|
// Otherwise, both the cert and key should be read from disk. If the
|
||||||
}
|
// cert is missing, the read error will occur in LoadX509KeyPair.
|
||||||
|
_, e := os.Stat(cfg.RPCKey)
|
||||||
|
keyExists := !os.IsNotExist(e)
|
||||||
|
switch {
|
||||||
|
case cfg.OneTimeTLSKey && keyExists:
|
||||||
|
err := fmt.Errorf("one time TLS keys are enabled, but TLS key "+
|
||||||
|
"`%s` already exists", cfg.RPCKey)
|
||||||
|
return tls.Certificate{}, err
|
||||||
|
case cfg.OneTimeTLSKey:
|
||||||
|
return generateRPCKeyPair(false)
|
||||||
|
case !keyExists:
|
||||||
|
return generateRPCKeyPair(true)
|
||||||
|
default:
|
||||||
return tls.LoadX509KeyPair(cfg.RPCCert, cfg.RPCKey)
|
return tls.LoadX509KeyPair(cfg.RPCCert, cfg.RPCKey)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// generateRPCKeyPair generates a new RPC TLS keypair and writes the pair in PEM
|
// generateRPCKeyPair generates a new RPC TLS keypair and writes the cert and
|
||||||
// format to the paths specified by the config. If successful, the new keypair
|
// possibly also the key in PEM format to the paths specified by the config. If
|
||||||
// is returned.
|
// successful, the new keypair is returned.
|
||||||
func generateRPCKeyPair() (tls.Certificate, error) {
|
func generateRPCKeyPair(writeKey bool) (tls.Certificate, error) {
|
||||||
log.Infof("Generating TLS certificates...")
|
log.Infof("Generating TLS certificates...")
|
||||||
|
|
||||||
// Create directories for cert and key files if they do not yet exist.
|
// Create directories for cert and key files if they do not yet exist.
|
||||||
|
@ -79,19 +93,22 @@ func generateRPCKeyPair() (tls.Certificate, error) {
|
||||||
return tls.Certificate{}, err
|
return tls.Certificate{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Write cert and key files.
|
// Write cert and (potentially) the key files.
|
||||||
err = ioutil.WriteFile(cfg.RPCCert, cert, 0600)
|
err = ioutil.WriteFile(cfg.RPCCert, cert, 0600)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return tls.Certificate{}, err
|
return tls.Certificate{}, err
|
||||||
}
|
}
|
||||||
|
if writeKey {
|
||||||
err = ioutil.WriteFile(cfg.RPCKey, key, 0600)
|
err = ioutil.WriteFile(cfg.RPCKey, key, 0600)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
rmErr := os.Remove(cfg.RPCCert)
|
rmErr := os.Remove(cfg.RPCCert)
|
||||||
if rmErr != nil {
|
if rmErr != nil {
|
||||||
log.Warnf("Cannot remove written certificates: %v", rmErr)
|
log.Warnf("Cannot remove written certificates: %v",
|
||||||
|
rmErr)
|
||||||
}
|
}
|
||||||
return tls.Certificate{}, err
|
return tls.Certificate{}, err
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
log.Info("Done generating TLS certificates")
|
log.Info("Done generating TLS certificates")
|
||||||
return keyPair, nil
|
return keyPair, nil
|
||||||
|
|
|
@ -50,6 +50,15 @@
|
||||||
; rpccert=~/.btcwallet/rpc.cert
|
; rpccert=~/.btcwallet/rpc.cert
|
||||||
; rpckey=~/.btcwallet/rpc.key
|
; rpckey=~/.btcwallet/rpc.key
|
||||||
|
|
||||||
|
; Enable one time TLS keys. This option results in the process generating
|
||||||
|
; a new certificate pair each startup, writing only the certificate file
|
||||||
|
; to disk. This is a more secure option for clients that only interact with
|
||||||
|
; a local wallet process where persistent certs are not needed.
|
||||||
|
;
|
||||||
|
; This option will error at startup if the key specified by the rpckey option
|
||||||
|
; already exists.
|
||||||
|
; onetimetlskey=0
|
||||||
|
|
||||||
; Specify the interfaces for the RPC server listen on. One rpclisten address
|
; Specify the interfaces for the RPC server listen on. One rpclisten address
|
||||||
; per line. Multiple rpclisten options may be set in the same configuration,
|
; per line. Multiple rpclisten options may be set in the same configuration,
|
||||||
; and each will be used to listen for connections. NOTE: The default port is
|
; and each will be used to listen for connections. NOTE: The default port is
|
||||||
|
|
Loading…
Reference in a new issue