From c13a0c192c44cc6a60576b0651a4c14f065afd09 Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Thu, 22 Nov 2018 10:51:39 +0100 Subject: [PATCH] Merge #14532: Never bind INADDR_ANY by default, and warn when doing so explicitly 27c44ef9c61f64d941ab82ec232a68141a2fde90 rpcbind: Warn about exposing RPC to untrusted networks (Luke Dashjr) d6a1287481428d982dc03be3a6d9aeef8398f468 CNetAddr: Add IsBindAny method to check for INADDR_ANY (Luke Dashjr) 3615003952ffbc814bdb53d9d0e45790f152bd2f net: Always default rpcbind to localhost, never "all interfaces" (Luke Dashjr) Pull request description: A disturbingly large number of listening nodes appear to be also exposing their RPC server to the public internet. To attempt to mitigate this: * Only ever bind localhost by default, even if `rpcallowip` is specified. (A warning is given if `rpcallowip` is specified without `rpcbind`, since it doesn't really make sense to do.) * Warn about exposing the RPC server to untrusted networks if the user explicitly binds to any INADDR_ANY address. * Include a warning about untrusted networks in the `--help` documentation for `rpcbind`. Tree-SHA512: 755bbca3db416a31393672eccf6675a5ee4d1eb1812cba73ebb4ff8c6b855ecc5df4c692566e9aa7b0f7d4dce6fedb9c0e9f3c265b9663aca36c4a6ba5efdbd4 --- src/httpserver.cpp | 12 ++++++++---- src/init.cpp | 2 +- src/netaddress.cpp | 10 ++++++++++ src/netaddress.h | 1 + 4 files changed, 20 insertions(+), 5 deletions(-) diff --git a/src/httpserver.cpp b/src/httpserver.cpp index 28c61c059f4027..28be2de19ed32c 100644 --- a/src/httpserver.cpp +++ b/src/httpserver.cpp @@ -300,9 +300,12 @@ static bool HTTPBindAddresses(struct evhttp* http) std::vector > endpoints; // Determine what addresses to bind to - if (!gArgs.IsArgSet("-rpcallowip")) { // Default to loopback if not allowing external IPs + if (!(gArgs.IsArgSet("-rpcallowip") && gArgs.IsArgSet("-rpcbind"))) { // Default to loopback if not allowing external IPs endpoints.push_back(std::make_pair("::1", defaultPort)); endpoints.push_back(std::make_pair("127.0.0.1", defaultPort)); + if (gArgs.IsArgSet("-rpcallowip")) { + LogPrintf("WARNING: option -rpcallowip was specified without -rpcbind; this doesn't usually make sense\n"); + } if (gArgs.IsArgSet("-rpcbind")) { LogPrintf("WARNING: option -rpcbind was ignored because -rpcallowip was not specified, refusing to allow everyone to connect\n"); } @@ -313,9 +316,6 @@ static bool HTTPBindAddresses(struct evhttp* http) SplitHostPort(strRPCBind, port, host); endpoints.push_back(std::make_pair(host, port)); } - } else { // No specific bind address specified, bind to any - endpoints.push_back(std::make_pair("::", defaultPort)); - endpoints.push_back(std::make_pair("0.0.0.0", defaultPort)); } // Bind addresses @@ -323,6 +323,10 @@ static bool HTTPBindAddresses(struct evhttp* http) LogPrint(BCLog::HTTP, "Binding RPC on address %s port %i\n", i->first, i->second); evhttp_bound_socket *bind_handle = evhttp_bind_socket_with_handle(http, i->first.empty() ? nullptr : i->first.c_str(), i->second); if (bind_handle) { + CNetAddr addr; + if (i->first.empty() || (LookupHost(i->first.c_str(), addr, false) && addr.IsBindAny())) { + LogPrintf("WARNING: the RPC server is not safe to expose to untrusted networks such as the public internet\n"); + } boundSockets.push_back(bind_handle); } else { LogPrintf("Binding RPC on address %s port %i failed.\n", i->first, i->second); diff --git a/src/init.cpp b/src/init.cpp index ec4b9705d0a6d2..201e485a4c1178 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -648,7 +648,7 @@ std::string HelpMessage(HelpMessageMode mode) strUsage += HelpMessageGroup(_("RPC server options:")); strUsage += HelpMessageOpt("-server", _("Accept command line and JSON-RPC commands")); strUsage += HelpMessageOpt("-rest", strprintf(_("Accept public REST requests (default: %u)"), DEFAULT_REST_ENABLE)); - strUsage += HelpMessageOpt("-rpcbind=[:port]", _("Bind to given address to listen for JSON-RPC connections. This option is ignored unless -rpcallowip is also passed. Port is optional and overrides -rpcport. Use [host]:port notation for IPv6. This option can be specified multiple times (default: 127.0.0.1 and ::1 i.e., localhost, or if -rpcallowip has been specified, 0.0.0.0 and :: i.e., all addresses)")); + strUsage += HelpMessageOpt("-rpcbind=[:port]", _("Bind to given address to listen for JSON-RPC connections. Do not expose the RPC server to untrusted networks such as the public internet! This option is ignored unless -rpcallowip is also passed. Port is optional and overrides -rpcport. Use [host]:port notation for IPv6. This option can be specified multiple times (default: 127.0.0.1 and ::1 i.e., localhost, or if -rpcallowip has been specified, 0.0.0.0 and :: i.e., all addresses)")); strUsage += HelpMessageOpt("-rpccookiefile=", _("Location of the auth cookie. Relative paths will be prefixed by a net-specific datadir location. (default: data dir)")); strUsage += HelpMessageOpt("-rpcuser=", _("Username for JSON-RPC connections")); strUsage += HelpMessageOpt("-rpcpassword=", _("Password for JSON-RPC connections")); diff --git a/src/netaddress.cpp b/src/netaddress.cpp index a7a3fc5b6a763f..6fa7077b124b34 100644 --- a/src/netaddress.cpp +++ b/src/netaddress.cpp @@ -86,6 +86,16 @@ unsigned int CNetAddr::GetByte(int n) const return ip[15-n]; } +bool CNetAddr::IsBindAny() const +{ + const int cmplen = IsIPv4() ? 4 : 16; + for (int i = 0; i < cmplen; ++i) { + if (GetByte(i)) return false; + } + + return true; +} + bool CNetAddr::IsIPv4() const { return (memcmp(ip, pchIPv4, sizeof(pchIPv4)) == 0); diff --git a/src/netaddress.h b/src/netaddress.h index 45ac636359964e..50f52ee3696f5e 100644 --- a/src/netaddress.h +++ b/src/netaddress.h @@ -56,6 +56,7 @@ class CNetAddr bool SetInternal(const std::string& name); bool SetSpecial(const std::string &strName); // for Tor addresses + bool IsBindAny() const; // INADDR_ANY equivalent bool IsIPv4() const; // IPv4 mapped address (::FFFF:0:0/96, 0.0.0.0/0) bool IsIPv6() const; // IPv6 address (not mapped IPv4, not Tor) bool IsRFC1918() const; // IPv4 private networks (10.0.0.0/8, 192.168.0.0/16, 172.16.0.0/12)