mirror of
https://github.com/Xahau/xahaud.git
synced 2025-11-30 15:35:49 +00:00
Better admin IP management in .cfg (RIPD-820):
* Deprecate rpc_admin_allow section from configuration file
* New port-specific setting 'admin':
* Comma-separated list of IP addresses that are allowed administrative
privileges (subject to username & password authentication if configured)
* 127.0.0.1 is no longer a default admin IP.
* 0.0.0.0 may be specified to indicate "any IP" but cannot be combined
with other IP addresses.
This commit is contained in:
committed by
Vinnie Falco
parent
97623d20c5
commit
6d79004d4f
@@ -187,13 +187,21 @@
|
||||
# using HTTP's Basic Authentication headers when making outbound HTTP/S
|
||||
# requests.
|
||||
#
|
||||
# admin = no | allow
|
||||
# admin = [ IP, IP, IP, ... ]
|
||||
#
|
||||
# Controls whether or not administrative commands are allowed. These
|
||||
# commands may be issued over http, https, ws, or wss if configured
|
||||
# on the port. If unspecified, the default is to not allow
|
||||
# A comma-separated list of IP addresses.
|
||||
#
|
||||
# When set, grants administrative command access to the specified IP
|
||||
# addresses. These commands may be issued over http, https, ws, or wss
|
||||
# if configured on the port. If unspecified, the default is to not allow
|
||||
# administrative commands.
|
||||
#
|
||||
# *SECURITY WARNING*
|
||||
# 0.0.0.0 may be specified to allow access from any IP address. It must
|
||||
# be the only address specified and cannot be combined with other IPs.
|
||||
# Use of this address can compromise server security, please consider its
|
||||
# use carefully.
|
||||
#
|
||||
# admin_user = <text>
|
||||
# admin_password = <text>
|
||||
#
|
||||
@@ -233,15 +241,6 @@
|
||||
#
|
||||
#
|
||||
#
|
||||
# [rpc_admin_allow]
|
||||
#
|
||||
# Specify a list of IP addresses allowed to have admin access. One per line.
|
||||
# If you want to test the output of non-admin commands add this section and
|
||||
# just put an ip address not under your control.
|
||||
# Defaults to 127.0.0.1.
|
||||
#
|
||||
#
|
||||
#
|
||||
# [rpc_startup]
|
||||
#
|
||||
# Specify a list of RPC commands to run at startup.
|
||||
@@ -865,7 +864,7 @@ port_wss_admin
|
||||
[port_rpc]
|
||||
port = 5005
|
||||
ip = 127.0.0.1
|
||||
admin = allow
|
||||
admin = 127.0.0.1
|
||||
protocol = https
|
||||
|
||||
[port_peer]
|
||||
@@ -876,7 +875,7 @@ protocol = peer
|
||||
[port_wss_admin]
|
||||
port = 6006
|
||||
ip = 127.0.0.1
|
||||
admin = allow
|
||||
admin = 127.0.0.1
|
||||
protocol = wss
|
||||
|
||||
#[port_ws_public]
|
||||
|
||||
@@ -139,6 +139,14 @@ public:
|
||||
return m_v6;
|
||||
}
|
||||
|
||||
/** Returns `true` if this address represents 0.0.0.0 */
|
||||
bool
|
||||
is_any () const
|
||||
{
|
||||
return is_v4 () ? m_v4 == IP::AddressV4::any ()
|
||||
: false; // m_v6 == IP::AddressV6::any();
|
||||
}
|
||||
|
||||
template <class Hasher>
|
||||
friend
|
||||
void
|
||||
|
||||
@@ -213,7 +213,6 @@ public:
|
||||
int WEBSOCKET_PING_FREQ;
|
||||
|
||||
// RPC parameters
|
||||
std::vector<beast::IP::Endpoint> RPC_ADMIN_ALLOW;
|
||||
Json::Value RPC_STARTUP;
|
||||
|
||||
// Path searching
|
||||
|
||||
@@ -57,7 +57,6 @@ struct ConfigSection
|
||||
#define SECTION_PATH_SEARCH_MAX "path_search_max"
|
||||
#define SECTION_PEER_PRIVATE "peer_private"
|
||||
#define SECTION_PEERS_MAX "peers_max"
|
||||
#define SECTION_RPC_ADMIN_ALLOW "rpc_admin_allow"
|
||||
#define SECTION_RPC_STARTUP "rpc_startup"
|
||||
#define SECTION_SMS_FROM "sms_from"
|
||||
#define SECTION_SMS_KEY "sms_key"
|
||||
|
||||
@@ -201,8 +201,6 @@ Config::Config ()
|
||||
|
||||
WEBSOCKET_PING_FREQ = (5 * 60);
|
||||
|
||||
RPC_ADMIN_ALLOW.push_back (beast::IP::Endpoint::from_string("127.0.0.1"));
|
||||
|
||||
PEER_PRIVATE = false;
|
||||
PEERS_MAX = 0; // indicates "use default"
|
||||
|
||||
@@ -431,14 +429,6 @@ void Config::loadFromString (std::string const& fileContents)
|
||||
if (getSingleSection (secConfig, SECTION_PEERS_MAX, strTemp))
|
||||
PEERS_MAX = beast::lexicalCastThrow <int> (strTemp);
|
||||
|
||||
if (auto s = getIniFileSection (secConfig, SECTION_RPC_ADMIN_ALLOW))
|
||||
{
|
||||
std::vector<beast::IP::Endpoint> parsedAddresses;
|
||||
parseAddresses (parsedAddresses, (*s).cbegin(), (*s).cend());
|
||||
RPC_ADMIN_ALLOW.insert (RPC_ADMIN_ALLOW.end(),
|
||||
parsedAddresses.cbegin (), parsedAddresses.cend ());
|
||||
}
|
||||
|
||||
if (getSingleSection (secConfig, SECTION_NODE_SIZE, strTemp))
|
||||
{
|
||||
if (strTemp == "tiny")
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
#ifndef RIPPLE_SERVER_PORT_H_INCLUDED
|
||||
#define RIPPLE_SERVER_PORT_H_INCLUDED
|
||||
|
||||
#include <beast/net/IPEndpoint.h>
|
||||
#include <beast/utility/ci_char_traits.h>
|
||||
#include <boost/asio/ip/address.hpp>
|
||||
#include <cstdint>
|
||||
@@ -39,7 +40,7 @@ struct Port
|
||||
boost::asio::ip::address ip;
|
||||
std::uint16_t port = 0;
|
||||
std::set<std::string, beast::ci_less> protocol;
|
||||
bool allow_admin = false;
|
||||
std::vector<beast::IP::Address> admin_ip;
|
||||
std::string user;
|
||||
std::string password;
|
||||
std::string admin_user;
|
||||
@@ -97,11 +98,16 @@ inline
|
||||
std::ostream&
|
||||
operator<< (std::ostream& os, Port const& p)
|
||||
{
|
||||
os <<
|
||||
"'" << p.name <<
|
||||
"' (ip=" << p.ip << ":" << p.port <<
|
||||
(p.allow_admin ? ", admin, " : ", ") <<
|
||||
p.protocols() << ")";
|
||||
os << "'" << p.name << "' (ip=" << p.ip << ":" << p.port << ", ";
|
||||
|
||||
if (! p.admin_ip.empty ())
|
||||
{
|
||||
os << "admin IPs:";
|
||||
for (auto const& ip : p.admin_ip)
|
||||
os << ip.to_string () << ", ";
|
||||
}
|
||||
|
||||
os << p.protocols () << ")";
|
||||
return os;
|
||||
}
|
||||
|
||||
|
||||
@@ -45,8 +45,7 @@ enum class Role
|
||||
*/
|
||||
Role
|
||||
requestRole (Role const& required, HTTP::Port const& port,
|
||||
Json::Value const& jsonRPC, beast::IP::Endpoint const& remoteIp,
|
||||
std::vector<beast::IP::Endpoint> const& admin_allow);
|
||||
Json::Value const& jsonRPC, beast::IP::Endpoint const& remoteIp);
|
||||
|
||||
} // ripple
|
||||
|
||||
|
||||
@@ -26,7 +26,7 @@ bool
|
||||
passwordUnrequiredOrSentCorrect (HTTP::Port const& port,
|
||||
Json::Value const& params) {
|
||||
|
||||
assert(port.allow_admin);
|
||||
assert(! port.admin_ip.empty ());
|
||||
bool const passwordRequired = (!port.admin_user.empty() ||
|
||||
!port.admin_password.empty());
|
||||
|
||||
@@ -38,29 +38,28 @@ passwordUnrequiredOrSentCorrect (HTTP::Port const& port,
|
||||
}
|
||||
|
||||
bool
|
||||
ipAllowed (beast::IP::Endpoint const& remoteIp,
|
||||
std::vector<beast::IP::Endpoint> const& adminAllow)
|
||||
ipAllowed (beast::IP::Address const& remoteIp,
|
||||
std::vector<beast::IP::Address> const& adminIp)
|
||||
{
|
||||
return std::find(adminAllow.begin(), adminAllow.end(),
|
||||
remoteIp.at_port(0)) != adminAllow.end();
|
||||
return std::find_if (adminIp.begin (), adminIp.end (),
|
||||
[&remoteIp](beast::IP::Address const& ip) { return ip.is_any () ||
|
||||
ip == remoteIp; }) != adminIp.end ();
|
||||
}
|
||||
|
||||
bool
|
||||
isAdmin (HTTP::Port const& port, Json::Value const& params,
|
||||
beast::IP::Endpoint const& remoteIp,
|
||||
std::vector<beast::IP::Endpoint> const& adminAllow)
|
||||
beast::IP::Address const& remoteIp)
|
||||
{
|
||||
return (port.allow_admin && ipAllowed(remoteIp, adminAllow)) &&
|
||||
passwordUnrequiredOrSentCorrect(port, params);
|
||||
return ipAllowed (remoteIp, port.admin_ip) &&
|
||||
passwordUnrequiredOrSentCorrect (port, params);
|
||||
}
|
||||
|
||||
Role
|
||||
requestRole (Role const& required, HTTP::Port const& port,
|
||||
Json::Value const& params, beast::IP::Endpoint const& remoteIp,
|
||||
std::vector<beast::IP::Endpoint> const& adminAllow)
|
||||
Json::Value const& params, beast::IP::Endpoint const& remoteIp)
|
||||
{
|
||||
Role role (Role::GUEST);
|
||||
if (isAdmin(port, params, remoteIp, adminAllow))
|
||||
if (isAdmin(port, params, remoteIp.address ()))
|
||||
role = Role::ADMIN;
|
||||
if (required == Role::ADMIN && role != required)
|
||||
role = Role::FORBID;
|
||||
|
||||
@@ -291,7 +291,6 @@ ServerHandlerImp::processRequest (
|
||||
}
|
||||
|
||||
/* ---------------------------------------------------------------------- */
|
||||
auto const& admin_allow = getConfig().RPC_ADMIN_ALLOW;
|
||||
auto role = Role::FORBID;
|
||||
auto required = RPC::roleRequired(id.asString());
|
||||
|
||||
@@ -300,12 +299,12 @@ ServerHandlerImp::processRequest (
|
||||
jsonRPC["params"][Json::UInt(0)].isObject())
|
||||
{
|
||||
role = requestRole(required, port, jsonRPC["params"][Json::UInt(0)],
|
||||
remoteIPAddress, admin_allow);
|
||||
remoteIPAddress);
|
||||
}
|
||||
else
|
||||
{
|
||||
role = requestRole(required, port, Json::objectValue,
|
||||
remoteIPAddress, admin_allow);
|
||||
remoteIPAddress);
|
||||
}
|
||||
|
||||
Resource::Consumer usage;
|
||||
@@ -521,7 +520,7 @@ struct ParsedPort
|
||||
|
||||
boost::optional<boost::asio::ip::address> ip;
|
||||
boost::optional<std::uint16_t> port;
|
||||
boost::optional<bool> allow_admin;
|
||||
boost::optional<std::vector<beast::IP::Address>> admin_ip;
|
||||
};
|
||||
|
||||
void
|
||||
@@ -579,19 +578,35 @@ parse_Port (ParsedPort& port, Section const& section, std::ostream& log)
|
||||
auto const result = section.find("admin");
|
||||
if (result.second)
|
||||
{
|
||||
if (result.first == "no")
|
||||
std::stringstream ss (result.first);
|
||||
std::string ip;
|
||||
bool has_any (false);
|
||||
|
||||
port.admin_ip.emplace ();
|
||||
while (std::getline (ss, ip, ','))
|
||||
{
|
||||
port.allow_admin = false;
|
||||
}
|
||||
else if (result.first == "allow")
|
||||
{
|
||||
port.allow_admin = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
log << "Invalid value '" << result.first <<
|
||||
"' for key 'admin' in [" << section.name() << "]\n";
|
||||
throw std::exception();
|
||||
beast::IP::Address const addr(
|
||||
beast::IP::Endpoint::from_string_altform (ip).address ());
|
||||
|
||||
if (addr.is_any ())
|
||||
{
|
||||
has_any = true;
|
||||
}
|
||||
else if (is_unspecified (addr))
|
||||
{
|
||||
log << "Invalid value '" << ip << "' for key 'admin' in ["
|
||||
<< section.name() << "]\n";
|
||||
throw std::exception ();
|
||||
}
|
||||
|
||||
if (has_any && ! port.admin_ip->empty ())
|
||||
{
|
||||
log << "IP specified with 0.0.0.0 '" << ip <<
|
||||
"' for key 'admin' in [" << section.name () << "]\n";
|
||||
throw std::exception ();
|
||||
}
|
||||
|
||||
port.admin_ip->emplace_back (addr);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -629,11 +644,9 @@ to_Port(ParsedPort const& parsed, std::ostream& log)
|
||||
throw std::exception();
|
||||
}
|
||||
p.port = *parsed.port;
|
||||
|
||||
if (! parsed.allow_admin)
|
||||
p.allow_admin = false;
|
||||
else
|
||||
p.allow_admin = *parsed.allow_admin;
|
||||
|
||||
if (parsed.admin_ip)
|
||||
p.admin_ip = *parsed.admin_ip;
|
||||
|
||||
if (parsed.protocol.empty())
|
||||
{
|
||||
|
||||
@@ -100,7 +100,7 @@ public:
|
||||
void setPingTimer ();
|
||||
|
||||
private:
|
||||
HTTP::Port const& port_;
|
||||
HTTP::Port const& m_port;
|
||||
Resource::Manager& m_resourceManager;
|
||||
Resource::Consumer m_usage;
|
||||
bool const m_isPublic;
|
||||
@@ -129,7 +129,7 @@ ConnectionImpl <WebSocket>::ConnectionImpl (
|
||||
boost::asio::io_service& io_service)
|
||||
: InfoSub (source, // usage
|
||||
resourceManager.newInboundEndpoint (remoteAddress))
|
||||
, port_ (handler.port())
|
||||
, m_port (handler.port ())
|
||||
, m_resourceManager (resourceManager)
|
||||
, m_isPublic (handler.getPublic ())
|
||||
, m_remoteAddress (remoteAddress)
|
||||
@@ -262,8 +262,7 @@ Json::Value ConnectionImpl <WebSocket>::invokeCommand (Json::Value& jvRequest)
|
||||
Json::Value jvResult (Json::objectValue);
|
||||
|
||||
auto required = RPC::roleRequired (jvRequest[jss::command].asString());
|
||||
Role const role = requestRole (required, port_, jvRequest, m_remoteAddress,
|
||||
getConfig().RPC_ADMIN_ALLOW);
|
||||
Role const role = requestRole (required, m_port, jvRequest, m_remoteAddress);
|
||||
|
||||
if (Role::FORBID == role)
|
||||
{
|
||||
|
||||
@@ -107,7 +107,7 @@ public:
|
||||
|
||||
bool getPublic()
|
||||
{
|
||||
return port().allow_admin;
|
||||
return ! port ().admin_ip.empty ();
|
||||
};
|
||||
|
||||
void send (connection_ptr const& cpClient, message_ptr const& mpMessage)
|
||||
|
||||
@@ -60,10 +60,7 @@ private:
|
||||
void run () override
|
||||
{
|
||||
WriteLog (lsWARNING, WebSocket)
|
||||
<< "Websocket: '" << desc_.port.name
|
||||
<< "' creating endpoint " << desc_.port.ip.to_string()
|
||||
<< ":" << std::to_string(desc_.port.port)
|
||||
<< (desc_.port.allow_admin ? "(Admin)" : "");
|
||||
<< "Websocket: creating endpoint " << desc_.port;
|
||||
|
||||
auto handler = WebSocket::makeHandler (desc_);
|
||||
{
|
||||
@@ -72,10 +69,7 @@ private:
|
||||
}
|
||||
|
||||
WriteLog (lsWARNING, WebSocket)
|
||||
<< "Websocket: '" << desc_.port.name
|
||||
<< "' listening on " << desc_.port.ip.to_string()
|
||||
<< ":" << std::to_string(desc_.port.port)
|
||||
<< (desc_.port.allow_admin ? "(Admin)" : "");
|
||||
<< "Websocket: listening on " << desc_.port;
|
||||
|
||||
listen();
|
||||
{
|
||||
@@ -84,26 +78,17 @@ private:
|
||||
}
|
||||
|
||||
WriteLog (lsWARNING, WebSocket)
|
||||
<< "Websocket: '" << desc_.port.name
|
||||
<< "' finished listening on " << desc_.port.ip.to_string()
|
||||
<< ":" << std::to_string(desc_.port.port)
|
||||
<< (desc_.port.allow_admin ? "(Admin)" : "");
|
||||
<< "Websocket: finished listening on " << desc_.port;
|
||||
|
||||
stopped ();
|
||||
WriteLog (lsWARNING, WebSocket)
|
||||
<< "Websocket: '" << desc_.port.name
|
||||
<< "' stopped on " << desc_.port.ip.to_string()
|
||||
<< ":" << std::to_string(desc_.port.port)
|
||||
<< (desc_.port.allow_admin ? "(Admin)" : "");
|
||||
<< "Websocket: stopped on " << desc_.port;
|
||||
}
|
||||
|
||||
void onStop () override
|
||||
{
|
||||
WriteLog (lsWARNING, WebSocket)
|
||||
<< "Websocket: '" << desc_.port.name
|
||||
<< "' onStop " << desc_.port.ip.to_string()
|
||||
<< ":" << std::to_string(desc_.port.port)
|
||||
<< (desc_.port.allow_admin ? "(Admin)" : "");
|
||||
<< "Websocket: onStop " << desc_.port;
|
||||
|
||||
typename WebSocket::EndpointPtr endpoint;
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user