mirror of
https://github.com/bitcoin/bitcoin.git
synced 2025-11-10 22:18:54 +01:00
Add RPC Whitelist Feature from #12248
This commit is contained in:
@@ -15,8 +15,13 @@
|
||||
#include <util/translation.h>
|
||||
#include <walletinitinterface.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <iterator>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <stdio.h>
|
||||
#include <set>
|
||||
#include <string>
|
||||
|
||||
#include <boost/algorithm/string.hpp> // boost::trim
|
||||
|
||||
@@ -64,6 +69,9 @@ private:
|
||||
static std::string strRPCUserColonPass;
|
||||
/* Stored RPC timer interface (for unregistration) */
|
||||
static std::unique_ptr<HTTPRPCTimerInterface> httpRPCTimerInterface;
|
||||
/* RPC Auth Whitelist */
|
||||
static std::map<std::string, std::set<std::string>> g_rpc_whitelist;
|
||||
static bool g_rpc_whitelist_default = false;
|
||||
|
||||
static void JSONErrorReply(HTTPRequest* req, const UniValue& objError, const UniValue& id)
|
||||
{
|
||||
@@ -183,18 +191,45 @@ static bool HTTPReq_JSONRPC(HTTPRequest* req, const std::string &)
|
||||
jreq.URI = req->GetURI();
|
||||
|
||||
std::string strReply;
|
||||
// singleton request
|
||||
if (valRequest.isObject()) {
|
||||
jreq.parse(valRequest);
|
||||
bool user_has_whitelist = g_rpc_whitelist.count(jreq.authUser);
|
||||
if (!user_has_whitelist && g_rpc_whitelist_default) {
|
||||
LogPrintf("RPC User %s not allowed to call any methods\n", jreq.authUser);
|
||||
req->WriteReply(HTTP_FORBIDDEN);
|
||||
return false;
|
||||
|
||||
// singleton request
|
||||
} else if (valRequest.isObject()) {
|
||||
jreq.parse(valRequest);
|
||||
if (user_has_whitelist && !g_rpc_whitelist[jreq.authUser].count(jreq.strMethod)) {
|
||||
LogPrintf("RPC User %s not allowed to call method %s\n", jreq.authUser, jreq.strMethod);
|
||||
req->WriteReply(HTTP_FORBIDDEN);
|
||||
return false;
|
||||
}
|
||||
UniValue result = tableRPC.execute(jreq);
|
||||
|
||||
// Send reply
|
||||
strReply = JSONRPCReply(result, NullUniValue, jreq.id);
|
||||
|
||||
// array of requests
|
||||
} else if (valRequest.isArray())
|
||||
} else if (valRequest.isArray()) {
|
||||
if (user_has_whitelist) {
|
||||
for (unsigned int reqIdx = 0; reqIdx < valRequest.size(); reqIdx++) {
|
||||
if (!valRequest[reqIdx].isObject()) {
|
||||
throw JSONRPCError(RPC_INVALID_REQUEST, "Invalid Request object");
|
||||
} else {
|
||||
const UniValue& request = valRequest[reqIdx].get_obj();
|
||||
// Parse method
|
||||
std::string strMethod = find_value(request, "method").get_str();
|
||||
if (!g_rpc_whitelist[jreq.authUser].count(strMethod)) {
|
||||
LogPrintf("RPC User %s not allowed to call method %s\n", jreq.authUser, strMethod);
|
||||
req->WriteReply(HTTP_FORBIDDEN);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
strReply = JSONRPCExecBatch(jreq, valRequest.get_array());
|
||||
}
|
||||
else
|
||||
throw JSONRPCError(RPC_PARSE_ERROR, "Top-level object parse error");
|
||||
|
||||
@@ -229,6 +264,27 @@ static bool InitRPCAuthentication()
|
||||
{
|
||||
LogPrintf("Using rpcauth authentication.\n");
|
||||
}
|
||||
|
||||
g_rpc_whitelist_default = gArgs.GetBoolArg("-rpcwhitelistdefault", gArgs.IsArgSet("-rpcwhitelist"));
|
||||
for (const std::string& strRPCWhitelist : gArgs.GetArgs("-rpcwhitelist")) {
|
||||
auto pos = strRPCWhitelist.find(':');
|
||||
std::string strUser = strRPCWhitelist.substr(0, pos);
|
||||
bool intersect = g_rpc_whitelist.count(strUser);
|
||||
std::set<std::string>& whitelist = g_rpc_whitelist[strUser];
|
||||
if (pos != std::string::npos) {
|
||||
std::string strWhitelist = strRPCWhitelist.substr(pos + 1);
|
||||
std::set<std::string> new_whitelist;
|
||||
boost::split(new_whitelist, strWhitelist, boost::is_any_of(", "));
|
||||
if (intersect) {
|
||||
std::set<std::string> tmp_whitelist;
|
||||
std::set_intersection(new_whitelist.begin(), new_whitelist.end(),
|
||||
whitelist.begin(), whitelist.end(), std::inserter(tmp_whitelist, tmp_whitelist.end()));
|
||||
new_whitelist = std::move(tmp_whitelist);
|
||||
}
|
||||
whitelist = std::move(new_whitelist);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -534,6 +534,8 @@ void SetupServerArgs()
|
||||
gArgs.AddArg("-rpcservertimeout=<n>", strprintf("Timeout during HTTP requests (default: %d)", DEFAULT_HTTP_SERVER_TIMEOUT), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::RPC);
|
||||
gArgs.AddArg("-rpcthreads=<n>", strprintf("Set the number of threads to service RPC calls (default: %d)", DEFAULT_HTTP_THREADS), ArgsManager::ALLOW_ANY, OptionsCategory::RPC);
|
||||
gArgs.AddArg("-rpcuser=<user>", "Username for JSON-RPC connections", ArgsManager::ALLOW_ANY, OptionsCategory::RPC);
|
||||
gArgs.AddArg("-rpcwhitelist=<whitelist>", "Set a whitelist to filter incoming RPC calls for a specific user. The field <whitelist> comes in the format: <USERNAME>:<rpc 1>,<rpc 2>,...,<rpc n>. If multiple whitelists are set for a given user, they are set-intersected. See -rpcwhitelistdefault documentation for information on default whitelist behavior.", ArgsManager::ALLOW_ANY, OptionsCategory::RPC);
|
||||
gArgs.AddArg("-rpcwhitelistdefault", "Sets default behavior for rpc whitelisting. Unless rpcwhitelistdefault is set to 0, if any -rpcwhitelist is set, the rpc server acts as if all rpc users are subject to empty-unless-otherwise-specified whitelists. If rpcwhitelistdefault is set to 1 and no -rpcwhitelist is set, rpc server acts as if all rpc users are subject to empty whitelists.", ArgsManager::ALLOW_BOOL, OptionsCategory::RPC);
|
||||
gArgs.AddArg("-rpcworkqueue=<n>", strprintf("Set the depth of the work queue to service RPC calls (default: %d)", DEFAULT_HTTP_WORKQUEUE), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::RPC);
|
||||
gArgs.AddArg("-server", "Accept command line and JSON-RPC commands", ArgsManager::ALLOW_ANY, OptionsCategory::RPC);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user