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:
Josh Rickmar 2016-02-11 00:00:32 -05:00
parent 97963b47ce
commit 567752ea9b
3 changed files with 47 additions and 20 deletions

View file

@ -94,6 +94,7 @@ type config struct {
// aren't considered legacy.
RPCCert string `long:"rpccert" description:"File containing the certificate file"`
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"`
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"`

View file

@ -19,6 +19,7 @@ package main
import (
"crypto/tls"
"errors"
"fmt"
"io/ioutil"
"net"
"os"
@ -36,23 +37,36 @@ import (
)
// 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) {
// Check for existence of cert file and key file. Generate a new
// keypair if both are missing. If one exists but not the other, the
// error will occur in LoadX509KeyPair.
_, e1 := os.Stat(cfg.RPCKey)
_, e2 := os.Stat(cfg.RPCCert)
if os.IsNotExist(e1) && os.IsNotExist(e2) {
return generateRPCKeyPair()
}
// Check for existence of the TLS key file. If one time TLS keys are
// enabled but a key already exists, this function should error since
// it's possible that a persistent certificate was copied to a remote
// machine. Otherwise, generate a new keypair when the key is missing.
// When generating new persistent keys, overwriting an existing cert is
// acceptable if the previous execution used a one time TLS key.
// 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)
}
}
// generateRPCKeyPair generates a new RPC TLS keypair and writes the pair in PEM
// format to the paths specified by the config. If successful, the new keypair
// is returned.
func generateRPCKeyPair() (tls.Certificate, error) {
// generateRPCKeyPair generates a new RPC TLS keypair and writes the cert and
// possibly also the key in PEM format to the paths specified by the config. If
// successful, the new keypair is returned.
func generateRPCKeyPair(writeKey bool) (tls.Certificate, error) {
log.Infof("Generating TLS certificates...")
// 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
}
// Write cert and key files.
// Write cert and (potentially) the key files.
err = ioutil.WriteFile(cfg.RPCCert, cert, 0600)
if err != nil {
return tls.Certificate{}, err
}
if writeKey {
err = ioutil.WriteFile(cfg.RPCKey, key, 0600)
if err != nil {
rmErr := os.Remove(cfg.RPCCert)
if rmErr != nil {
log.Warnf("Cannot remove written certificates: %v", rmErr)
log.Warnf("Cannot remove written certificates: %v",
rmErr)
}
return tls.Certificate{}, err
}
}
log.Info("Done generating TLS certificates")
return keyPair, nil

View file

@ -50,6 +50,15 @@
; rpccert=~/.btcwallet/rpc.cert
; 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
; 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