Allow to expose a subset of unsafe RPCs (#5233)

* sc-cli: Use type-safe constructors for RPC/Prometheus interfaces

* service: Simplify rpc handler creation

Could probably be further simplifies once [this][commit] lands.

[commit]: https://github.com/paritytech/jsonrpc/commit/20485387ed06a48f1a70bf4d609a7cde6cf0accf

* service: Streamline some HTTP & WS server start logic

* client: Introduce a simple RPC policy mechanism

* rpc/system: Check unsafe RPCs

* rpc/offchain: Check unsafe RPCs

* rpc/author: Check unsafe RPCs
This commit is contained in:
Igor Matuszewski
2020-04-20 11:03:58 +02:00
committed by GitHub
parent d05dc090a8
commit 4b1f7d187f
20 changed files with 281 additions and 95 deletions
+6
View File
@@ -279,6 +279,12 @@ macro_rules! substrate_cli_subcommands {
}
}
fn unsafe_rpc_expose(&self) -> $crate::Result<bool> {
match self {
$($enum::$variant(cmd) => cmd.unsafe_rpc_expose()),*
}
}
fn rpc_ws_max_connections(&self) -> $crate::Result<::std::option::Option<usize>> {
match self {
$($enum::$variant(cmd) => cmd.rpc_ws_max_connections()),*
+42 -41
View File
@@ -27,7 +27,7 @@ use sc_service::{
ChainSpec, Role,
};
use sc_telemetry::TelemetryEndpoints;
use std::net::SocketAddr;
use std::net::{IpAddr, Ipv4Addr, SocketAddr};
use structopt::{clap::arg_enum, StructOpt};
arg_enum! {
@@ -93,6 +93,14 @@ pub struct RunCmd {
#[structopt(long = "unsafe-rpc-external")]
pub unsafe_rpc_external: bool,
/// Don't deny potentially unsafe RPCs when listening on external interfaces.
///
/// Default is false. This allows exposing RPC methods publicly (same as `--unsafe-{rpc,ws}-external` )
/// but will allow doing so even on validator nodes, which is prohibited by default.
/// Please do this if you know what you're doing.
#[structopt(long = "unsafe-rpc-expose")]
pub unsafe_rpc_expose: bool,
/// Listen to all Websocket interfaces.
///
/// Default is local. Note: not all RPC methods are safe to be exposed publicly. Use an RPC proxy
@@ -361,22 +369,19 @@ impl CliConfiguration for RunCmd {
}
fn prometheus_config(&self) -> Result<Option<PrometheusConfig>> {
if self.no_prometheus {
Ok(None)
Ok(if self.no_prometheus {
None
} else {
let prometheus_interface: &str = if self.prometheus_external {
"0.0.0.0"
let interface = if self.prometheus_external {
Ipv4Addr::UNSPECIFIED
} else {
"127.0.0.1"
Ipv4Addr::LOCALHOST
};
Ok(Some(PrometheusConfig::new_with_default_registry(
parse_address(
&format!("{}:{}", prometheus_interface, 9615),
self.prometheus_port,
)?,
)))
}
Some(PrometheusConfig::new_with_default_registry(
SocketAddr::new(interface.into(), self.prometheus_port.unwrap_or(9615))
))
})
}
fn disable_grandpa(&self) -> Result<bool> {
@@ -409,23 +414,29 @@ impl CliConfiguration for RunCmd {
}
fn rpc_http(&self) -> Result<Option<SocketAddr>> {
let rpc_interface: &str =
interface_str(self.rpc_external, self.unsafe_rpc_external, self.validator)?;
let interface = rpc_interface(
self.rpc_external,
self.unsafe_rpc_external,
self.unsafe_rpc_expose,
self.validator
)?;
Ok(Some(parse_address(
&format!("{}:{}", rpc_interface, 9933),
self.rpc_port,
)?))
Ok(Some(SocketAddr::new(interface, self.rpc_port.unwrap_or(9933))))
}
fn rpc_ws(&self) -> Result<Option<SocketAddr>> {
let ws_interface: &str =
interface_str(self.ws_external, self.unsafe_ws_external, self.validator)?;
let interface = rpc_interface(
self.ws_external,
self.unsafe_ws_external,
self.unsafe_rpc_expose,
self.validator
)?;
Ok(Some(parse_address(
&format!("{}:{}", ws_interface, 9944),
self.ws_port,
)?))
Ok(Some(SocketAddr::new(interface, self.ws_port.unwrap_or(9944))))
}
fn unsafe_rpc_expose(&self) -> Result<bool> {
Ok(self.unsafe_rpc_expose)
}
fn offchain_worker(&self, role: &Role) -> Result<bool> {
@@ -468,23 +479,13 @@ pub fn is_node_name_valid(_name: &str) -> std::result::Result<(), &str> {
Ok(())
}
fn parse_address(address: &str, port: Option<u16>) -> std::result::Result<SocketAddr, String> {
let mut address: SocketAddr = address
.parse()
.map_err(|_| format!("Invalid address: {}", address))?;
if let Some(port) = port {
address.set_port(port);
}
Ok(address)
}
fn interface_str(
fn rpc_interface(
is_external: bool,
is_unsafe_external: bool,
is_unsafe_rpc_expose: bool,
is_validator: bool,
) -> Result<&'static str> {
if is_external && is_validator {
) -> Result<IpAddr> {
if is_external && is_validator && !is_unsafe_rpc_expose {
return Err(Error::Input(
"--rpc-external and --ws-external options shouldn't be \
used if the node is running as a validator. Use `--unsafe-rpc-external` if you understand \
@@ -499,9 +500,9 @@ fn interface_str(
available set of RPC methods."
);
Ok("0.0.0.0")
Ok(Ipv4Addr::UNSPECIFIED.into())
} else {
Ok("127.0.0.1")
Ok(Ipv4Addr::LOCALHOST.into())
}
}
+8
View File
@@ -249,6 +249,13 @@ pub trait CliConfiguration: Sized {
Ok(Default::default())
}
/// Returns `Ok(true) if potentially unsafe RPC is to be exposed.
///
/// By default this is `false`.
fn unsafe_rpc_expose(&self) -> Result<bool> {
Ok(Default::default())
}
/// Get the RPC websockets maximum connections (`None` if unlimited).
///
/// By default this is `None`.
@@ -419,6 +426,7 @@ pub trait CliConfiguration: Sized {
execution_strategies: self.execution_strategies(is_dev)?,
rpc_http: self.rpc_http()?,
rpc_ws: self.rpc_ws()?,
unsafe_rpc_expose: self.unsafe_rpc_expose()?,
rpc_ws_max_connections: self.rpc_ws_max_connections()?,
rpc_cors: self.rpc_cors(is_dev)?,
prometheus_config: self.prometheus_config()?,