Allow disabling RPC server TLS for localhost only.
This commit introduces a new flag, --noservertls, which can be used to disable TLS for the RPC server. However, the flag can only be used when the RPC server is bound to localhost interfaces. This is intended to prevent the situation where someone decides they want to expose the RPC server to the web for remote management/access, but forgot they have TLS disabled.
This commit is contained in:
parent
44fbd30e22
commit
469a6f86eb
2 changed files with 61 additions and 16 deletions
37
config.go
37
config.go
|
@ -69,6 +69,7 @@ type config struct {
|
|||
RPCKey string `long:"rpckey" description:"File containing the certificate key"`
|
||||
RPCMaxClients int64 `long:"rpcmaxclients" description:"Max number of RPC clients for standard connections"`
|
||||
RPCMaxWebsockets int64 `long:"rpcmaxwebsockets" description:"Max number of RPC websocket connections"`
|
||||
DisableServerTLS bool `long:"noservertls" description:"Disable TLS for the RPC server -- NOTE: This is only allowed if the RPC server is bound to localhost"`
|
||||
MainNet bool `long:"mainnet" description:"Use the main Bitcoin network (default testnet3)"`
|
||||
SimNet bool `long:"simnet" description:"Use the simulation test network (default testnet3)"`
|
||||
KeypoolSize uint `short:"k" long:"keypoolsize" description:"DEPRECATED -- Maximum number of addresses in keypool"`
|
||||
|
@ -265,9 +266,11 @@ func loadConfig() (*config, []string, error) {
|
|||
}
|
||||
|
||||
// Show the version and exit if the version flag was specified.
|
||||
funcName := "loadConfig"
|
||||
appName := filepath.Base(os.Args[0])
|
||||
appName = strings.TrimSuffix(appName, filepath.Ext(appName))
|
||||
usageMessage := fmt.Sprintf("Use %s -h to show usage", appName)
|
||||
if preCfg.ShowVersion {
|
||||
appName := filepath.Base(os.Args[0])
|
||||
appName = strings.TrimSuffix(appName, filepath.Ext(appName))
|
||||
fmt.Println(appName, "version", version())
|
||||
os.Exit(0)
|
||||
}
|
||||
|
@ -363,6 +366,11 @@ func loadConfig() (*config, []string, error) {
|
|||
// Add default port to connect flag if missing.
|
||||
cfg.RPCConnect = normalizeAddress(cfg.RPCConnect, activeNet.btcdPort)
|
||||
|
||||
localhostListeners := map[string]struct{}{
|
||||
"localhost": struct{}{},
|
||||
"127.0.0.1": struct{}{},
|
||||
"::1": struct{}{},
|
||||
}
|
||||
// If CAFile is unset, choose either the copy or local btcd cert.
|
||||
if cfg.CAFile == "" {
|
||||
cfg.CAFile = filepath.Join(cfg.DataDir, defaultCAFilename)
|
||||
|
@ -406,6 +414,31 @@ func loadConfig() (*config, []string, error) {
|
|||
cfg.SvrListeners = normalizeAddresses(cfg.SvrListeners,
|
||||
activeNet.svrPort)
|
||||
|
||||
// Only allow server TLS to be disabled if the RPC is bound to localhost
|
||||
// addresses.
|
||||
if cfg.DisableServerTLS {
|
||||
for _, addr := range cfg.SvrListeners {
|
||||
host, _, err := net.SplitHostPort(addr)
|
||||
if err != nil {
|
||||
str := "%s: RPC listen interface '%s' is " +
|
||||
"invalid: %v"
|
||||
err := fmt.Errorf(str, funcName, addr, err)
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
fmt.Fprintln(os.Stderr, usageMessage)
|
||||
return nil, nil, err
|
||||
}
|
||||
if _, ok := localhostListeners[host]; !ok {
|
||||
str := "%s: the --noservertls option may not be used " +
|
||||
"when binding RPC to non localhost " +
|
||||
"addresses: %s"
|
||||
err := fmt.Errorf(str, funcName, addr)
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
fmt.Fprintln(os.Stderr, usageMessage)
|
||||
return nil, nil, err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Expand environment variable and leading ~ for filepaths.
|
||||
cfg.CAFile = cleanAndExpandPath(cfg.CAFile)
|
||||
|
||||
|
|
40
rpcserver.go
40
rpcserver.go
|
@ -318,29 +318,41 @@ func newRPCServer(listenAddrs []string, maxPost, maxWebsockets int64) (*rpcServe
|
|||
quit: make(chan struct{}),
|
||||
}
|
||||
|
||||
// Check for existence of cert file and key file
|
||||
if !fileExists(cfg.RPCKey) && !fileExists(cfg.RPCCert) {
|
||||
// if both files do not exist, we generate them.
|
||||
err := genCertPair(cfg.RPCCert, cfg.RPCKey)
|
||||
// Setup TLS if not disabled.
|
||||
listenFunc := net.Listen
|
||||
if !cfg.DisableServerTLS {
|
||||
// Check for existence of cert file and key file
|
||||
if !fileExists(cfg.RPCKey) && !fileExists(cfg.RPCCert) {
|
||||
// if both files do not exist, we generate them.
|
||||
err := genCertPair(cfg.RPCCert, cfg.RPCKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
keypair, err := tls.LoadX509KeyPair(cfg.RPCCert, cfg.RPCKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
keypair, err := tls.LoadX509KeyPair(cfg.RPCCert, cfg.RPCKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
tlsConfig := tls.Config{
|
||||
Certificates: []tls.Certificate{keypair},
|
||||
MinVersion: tls.VersionTLS12,
|
||||
tlsConfig := tls.Config{
|
||||
Certificates: []tls.Certificate{keypair},
|
||||
MinVersion: tls.VersionTLS12,
|
||||
}
|
||||
|
||||
// Change the standard net.Listen function to the tls one.
|
||||
listenFunc = func(net string, laddr string) (net.Listener, error) {
|
||||
return tls.Listen(net, laddr, &tlsConfig)
|
||||
}
|
||||
}
|
||||
|
||||
ipv4ListenAddrs, ipv6ListenAddrs, err := parseListeners(listenAddrs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
listeners := make([]net.Listener, 0,
|
||||
len(ipv6ListenAddrs)+len(ipv4ListenAddrs))
|
||||
for _, addr := range ipv4ListenAddrs {
|
||||
listener, err := tls.Listen("tcp4", addr, &tlsConfig)
|
||||
listener, err := listenFunc("tcp4", addr)
|
||||
if err != nil {
|
||||
log.Warnf("RPCS: Can't listen on %s: %v", addr,
|
||||
err)
|
||||
|
@ -350,7 +362,7 @@ func newRPCServer(listenAddrs []string, maxPost, maxWebsockets int64) (*rpcServe
|
|||
}
|
||||
|
||||
for _, addr := range ipv6ListenAddrs {
|
||||
listener, err := tls.Listen("tcp6", addr, &tlsConfig)
|
||||
listener, err := listenFunc("tcp6", addr)
|
||||
if err != nil {
|
||||
log.Warnf("RPCS: Can't listen on %s: %v", addr,
|
||||
err)
|
||||
|
|
Loading…
Reference in a new issue