diff --git a/config.go b/config.go index 62fcaea6d..5cf52c7ac 100644 --- a/config.go +++ b/config.go @@ -82,6 +82,7 @@ type config struct { RPCMaxClients int `long:"rpcmaxclients" description:"Max number of RPC clients for standard connections"` RPCMaxWebsockets int `long:"rpcmaxwebsockets" description:"Max number of RPC websocket connections"` DisableRPC bool `long:"norpc" description:"Disable built-in RPC server -- NOTE: The RPC server is disabled by default if no rpcuser/rpcpass is specified"` + DisableTLS bool `long:"notls" description:"Disable TLS for the RPC server -- NOTE: This is only allowed if the RPC server is bound to localhost"` DisableDNSSeed bool `long:"nodnsseed" description:"Disable DNS seeding for peers"` ExternalIPs []string `long:"externalip" description:"Add an ip to the list of local addresses we claim to listen on to peers"` Proxy string `long:"proxy" description:"Connect via SOCKS5 proxy (eg. 127.0.0.1:9050)"` @@ -540,7 +541,6 @@ func loadConfig() (*config, []string, error) { addr = net.JoinHostPort(addr, activeNetParams.rpcPort) cfg.RPCListeners = append(cfg.RPCListeners, addr) } - } // Limit the max block size to a sane value. @@ -624,6 +624,36 @@ func loadConfig() (*config, []string, error) { cfg.RPCListeners = normalizeAddresses(cfg.RPCListeners, activeNetParams.rpcPort) + // Only allow TLS to be disabled if the RPC is bound to localhost + // addresses. + if !cfg.DisableRPC && cfg.DisableTLS { + allowedTLSListeners := map[string]struct{}{ + "localhost": struct{}{}, + "127.0.0.1": struct{}{}, + "::1": struct{}{}, + } + for _, addr := range cfg.RPCListeners { + 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 := allowedTLSListeners[host]; !ok { + str := "%s: the --notls 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 + } + } + } + // Add default port to all added peer addresses if needed and remove // duplicate addresses. cfg.AddPeers = normalizeAddresses(cfg.AddPeers, diff --git a/doc.go b/doc.go index f42484b8f..80e929570 100644 --- a/doc.go +++ b/doc.go @@ -48,6 +48,8 @@ Application Options: --norpc Disable built-in RPC server -- NOTE: The RPC server is disabled by default if no rpcuser/rpcpass is specified + --notls Disable TLS for the RPC server -- NOTE: This is only + allowed if the RPC server is bound to localhost --nodnsseed Disable DNS seeding for peers --externalip: Add an ip to the list of local addresses we claim to listen on to peers diff --git a/rpcserver.go b/rpcserver.go index 140d1bfb5..22249be5c 100644 --- a/rpcserver.go +++ b/rpcserver.go @@ -524,22 +524,31 @@ func newRPCServer(listenAddrs []string, s *server) (*rpcServer, error) { } rpc.ntfnMgr = newWsNotificationManager(&rpc) - // 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.DisableTLS { + // Generate the TLS cert and key file if both don't already + // exist. + if !fileExists(cfg.RPCKey) && !fileExists(cfg.RPCCert) { + 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) + } } // TODO(oga) this code is similar to that in server, should be @@ -551,20 +560,18 @@ func newRPCServer(listenAddrs []string, s *server) (*rpcServer, error) { 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 { - rpcsLog.Warnf("Can't listen on %s: %v", addr, - err) + rpcsLog.Warnf("Can't listen on %s: %v", addr, err) continue } listeners = append(listeners, listener) } for _, addr := range ipv6ListenAddrs { - listener, err := tls.Listen("tcp6", addr, &tlsConfig) + listener, err := listenFunc("tcp6", addr) if err != nil { - rpcsLog.Warnf("Can't listen on %s: %v", addr, - err) + rpcsLog.Warnf("Can't listen on %s: %v", addr, err) continue } listeners = append(listeners, listener) diff --git a/sample-btcd.conf b/sample-btcd.conf index ef0a1de89..90dd31dbd 100644 --- a/sample-btcd.conf +++ b/sample-btcd.conf @@ -193,6 +193,11 @@ ; server without having to remove credentials from the config file. ; norpc=1 +; Use the following setting to disable TLS for the RPC server. NOTE: This +; option only works if the RPC server is bound to localhost interfaces (which is +; the default). +; notls=1 + ; ------------------------------------------------------------------------------ ; Coin Generation (Mining) Settings - The following options control the