More CLI settings for IPFS API (#4608)

* TEMP: Bind to 0.0.0.0, don't check Origin

* More CLI options for IPFS

* CORS and Hosts filtering

* Allow current interface as origin

* Correctly handle CORS settings

* fix grumbles
This commit is contained in:
Maciej Hirsz
2017-02-24 10:32:42 +01:00
committed by Gav Wood
parent 9b5bcb81fd
commit f97e775498
11 changed files with 285 additions and 103 deletions

View File

@@ -76,6 +76,9 @@ path = "$HOME/.parity/secretstore"
[ipfs]
enable = false
port = 5001
interface = "local"
cors = "null"
hosts = ["none"]
[mining]
author = "0xdeadbeefcafe0000000000000000000000000001"

View File

@@ -96,7 +96,7 @@ usage! {
// -- Account Options
flag_unlock: Option<String> = None,
or |c: &Config| otry!(c.account).unlock.clone().map(|vec| Some(vec.join(","))),
or |c: &Config| otry!(c.account).unlock.as_ref().map(|vec| Some(vec.join(","))),
flag_password: Vec<String> = Vec::new(),
or |c: &Config| otry!(c.account).password.clone(),
flag_keys_iterations: u32 = 10240u32,
@@ -138,7 +138,7 @@ usage! {
flag_network_id: Option<u64> = None,
or |c: &Config| otry!(c.network).id.clone().map(Some),
flag_bootnodes: Option<String> = None,
or |c: &Config| otry!(c.network).bootnodes.clone().map(|vec| Some(vec.join(","))),
or |c: &Config| otry!(c.network).bootnodes.as_ref().map(|vec| Some(vec.join(","))),
flag_no_discovery: bool = false,
or |c: &Config| otry!(c.network).discovery.map(|d| !d).clone(),
flag_node_key: Option<String> = None,
@@ -160,9 +160,9 @@ usage! {
flag_jsonrpc_cors: Option<String> = None,
or |c: &Config| otry!(c.rpc).cors.clone().map(Some),
flag_jsonrpc_apis: String = "web3,eth,net,parity,traces,rpc",
or |c: &Config| otry!(c.rpc).apis.clone().map(|vec| vec.join(",")),
or |c: &Config| otry!(c.rpc).apis.as_ref().map(|vec| vec.join(",")),
flag_jsonrpc_hosts: String = "none",
or |c: &Config| otry!(c.rpc).hosts.clone().map(|vec| vec.join(",")),
or |c: &Config| otry!(c.rpc).hosts.as_ref().map(|vec| vec.join(",")),
// IPC
flag_no_ipc: bool = false,
@@ -170,7 +170,7 @@ usage! {
flag_ipc_path: String = "$BASE/jsonrpc.ipc",
or |c: &Config| otry!(c.ipc).path.clone(),
flag_ipc_apis: String = "web3,eth,net,parity,parity_accounts,traces,rpc",
or |c: &Config| otry!(c.ipc).apis.clone().map(|vec| vec.join(",")),
or |c: &Config| otry!(c.ipc).apis.as_ref().map(|vec| vec.join(",")),
// DAPPS
flag_no_dapps: bool = false,
@@ -180,7 +180,7 @@ usage! {
flag_dapps_interface: String = "local",
or |c: &Config| otry!(c.dapps).interface.clone(),
flag_dapps_hosts: String = "none",
or |c: &Config| otry!(c.dapps).hosts.clone().map(|vec| vec.join(",")),
or |c: &Config| otry!(c.dapps).hosts.as_ref().map(|vec| vec.join(",")),
flag_dapps_path: String = "$BASE/dapps",
or |c: &Config| otry!(c.dapps).path.clone(),
flag_dapps_user: Option<String> = None,
@@ -204,6 +204,12 @@ usage! {
or |c: &Config| otry!(c.ipfs).enable.clone(),
flag_ipfs_api_port: u16 = 5001u16,
or |c: &Config| otry!(c.ipfs).port.clone(),
flag_ipfs_api_interface: String = "local",
or |c: &Config| otry!(c.ipfs).interface.clone(),
flag_ipfs_api_cors: Option<String> = None,
or |c: &Config| otry!(c.ipfs).cors.clone().map(Some),
flag_ipfs_api_hosts: String = "none",
or |c: &Config| otry!(c.ipfs).hosts.as_ref().map(|vec| vec.join(",")),
// -- Sealing/Mining Options
flag_author: Option<String> = None,
@@ -249,7 +255,7 @@ usage! {
flag_remove_solved: bool = false,
or |c: &Config| otry!(c.mining).remove_solved.clone(),
flag_notify_work: Option<String> = None,
or |c: &Config| otry!(c.mining).notify_work.clone().map(|vec| Some(vec.join(","))),
or |c: &Config| otry!(c.mining).notify_work.as_ref().map(|vec| Some(vec.join(","))),
flag_refuse_service_transactions: bool = false,
or |c: &Config| otry!(c.mining).refuse_service_transactions.clone(),
@@ -439,6 +445,9 @@ struct SecretStore {
struct Ipfs {
enable: Option<bool>,
port: Option<u16>,
interface: Option<String>,
cors: Option<String>,
hosts: Option<Vec<String>>,
}
#[derive(Default, Debug, PartialEq, RustcDecodable)]
@@ -678,6 +687,9 @@ mod tests {
// IPFS
flag_ipfs_api: false,
flag_ipfs_api_port: 5001u16,
flag_ipfs_api_interface: "local".into(),
flag_ipfs_api_cors: Some("null".into()),
flag_ipfs_api_hosts: "none".into(),
// -- Sealing/Mining Options
flag_author: Some("0xdeadbeefcafe0000000000000000000000000001".into()),
@@ -872,7 +884,10 @@ mod tests {
}),
ipfs: Some(Ipfs {
enable: Some(false),
port: Some(5001)
port: Some(5001),
interface: None,
cors: None,
hosts: None,
}),
mining: Some(Mining {
author: Some("0xdeadbeefcafe0000000000000000000000000001".into()),

View File

@@ -178,6 +178,17 @@ API and Console Options:
--ipfs-api Enable IPFS-compatible HTTP API. (default: {flag_ipfs_api})
--ipfs-api-port PORT Configure on which port the IPFS HTTP API should listen.
(default: {flag_ipfs_api_port})
--ipfs-api-interface IP Specify the hostname portion of the IPFS API server,
IP should be an interface's IP address or local.
(default: {flag_ipfs_api_interface})
--ipfs-api-cors URL Specify CORS header for IPFS API responses.
(default: {flag_ipfs_api_cors:?})
--ipfs-api-hosts HOSTS List of allowed Host header values. This option will
validate the Host header sent by the browser, it
is additional security against some attack
vectors. Special options: "all", "none"
(default: {flag_ipfs_api_hosts}).
Secret Store Options:
--no-secretstore Disable Secret Store functionality. (default: {flag_no_secretstore})

View File

@@ -558,6 +558,9 @@ impl Configuration {
IpfsConfiguration {
enabled: self.args.flag_ipfs_api,
port: self.args.flag_ipfs_api_port,
interface: self.ipfs_interface(),
cors: self.ipfs_cors(),
hosts: self.ipfs_hosts(),
}
}
@@ -693,29 +696,39 @@ impl Configuration {
apis
}
fn cors(cors: Option<&String>) -> Option<Vec<String>> {
cors.map(|ref c| c.split(',').map(Into::into).collect())
}
fn rpc_cors(&self) -> Option<Vec<String>> {
let cors = self.args.flag_jsonrpc_cors.clone().or(self.args.flag_rpccorsdomain.clone());
cors.map(|c| c.split(',').map(|s| s.to_owned()).collect())
let cors = self.args.flag_jsonrpc_cors.as_ref().or(self.args.flag_rpccorsdomain.as_ref());
Self::cors(cors)
}
fn ipfs_cors(&self) -> Option<Vec<String>> {
Self::cors(self.args.flag_ipfs_api_cors.as_ref())
}
fn hosts(hosts: &str) -> Option<Vec<String>> {
match hosts {
"none" => return Some(Vec::new()),
"all" => return None,
_ => {}
}
let hosts = hosts.split(',').map(Into::into).collect();
Some(hosts)
}
fn rpc_hosts(&self) -> Option<Vec<String>> {
match self.args.flag_jsonrpc_hosts.as_ref() {
"none" => return Some(Vec::new()),
"all" => return None,
_ => {}
}
let hosts = self.args.flag_jsonrpc_hosts.split(',').map(|h| h.into()).collect();
Some(hosts)
Self::hosts(&self.args.flag_jsonrpc_hosts)
}
fn dapps_hosts(&self) -> Option<Vec<String>> {
match self.args.flag_dapps_hosts.as_ref() {
"none" => return Some(Vec::new()),
"all" => return None,
_ => {}
}
let hosts = self.args.flag_dapps_hosts.split(',').map(|h| h.into()).collect();
Some(hosts)
Self::hosts(&self.args.flag_dapps_hosts)
}
fn ipfs_hosts(&self) -> Option<Vec<String>> {
Self::hosts(&self.args.flag_ipfs_api_hosts)
}
fn ipc_config(&self) -> Result<IpcConfiguration, String> {
@@ -850,14 +863,18 @@ impl Configuration {
}.into()
}
fn rpc_interface(&self) -> String {
match self.network_settings().rpc_interface.as_str() {
fn interface(interface: &str) -> String {
match interface {
"all" => "0.0.0.0",
"local" => "127.0.0.1",
x => x,
}.into()
}
fn rpc_interface(&self) -> String {
Self::interface(&self.network_settings().rpc_interface)
}
fn dapps_interface(&self) -> String {
match self.args.flag_dapps_interface.as_str() {
"local" => "127.0.0.1",
@@ -865,6 +882,10 @@ impl Configuration {
}.into()
}
fn ipfs_interface(&self) -> String {
Self::interface(&self.args.flag_ipfs_api_interface)
}
fn secretstore_interface(&self) -> String {
match self.args.flag_secretstore_interface.as_str() {
"local" => "127.0.0.1",
@@ -873,11 +894,7 @@ impl Configuration {
}
fn stratum_interface(&self) -> String {
match self.args.flag_stratum_interface.as_str() {
"local" => "127.0.0.1",
"all" => "0.0.0.0",
x => x,
}.into()
Self::interface(&self.args.flag_stratum_interface)
}
fn dapps_enabled(&self) -> bool {
@@ -1273,6 +1290,38 @@ mod tests {
assert_eq!(conf3.dapps_hosts(), Some(vec!["ethcore.io".into(), "something.io".into()]));
}
#[test]
fn should_parse_ipfs_hosts() {
// given
// when
let conf0 = parse(&["parity"]);
let conf1 = parse(&["parity", "--ipfs-api-hosts", "none"]);
let conf2 = parse(&["parity", "--ipfs-api-hosts", "all"]);
let conf3 = parse(&["parity", "--ipfs-api-hosts", "ethcore.io,something.io"]);
// then
assert_eq!(conf0.ipfs_hosts(), Some(Vec::new()));
assert_eq!(conf1.ipfs_hosts(), Some(Vec::new()));
assert_eq!(conf2.ipfs_hosts(), None);
assert_eq!(conf3.ipfs_hosts(), Some(vec!["ethcore.io".into(), "something.io".into()]));
}
#[test]
fn should_parse_ipfs_cors() {
// given
// when
let conf0 = parse(&["parity"]);
let conf1 = parse(&["parity", "--ipfs-api-cors", "*"]);
let conf2 = parse(&["parity", "--ipfs-api-cors", "http://ethcore.io,http://something.io"]);
// then
assert_eq!(conf0.ipfs_cors(), None);
assert_eq!(conf1.ipfs_cors(), Some(vec!["*".into()]));
assert_eq!(conf2.ipfs_cors(), Some(vec!["http://ethcore.io".into(),"http://something.io".into()]));
}
#[test]
fn should_disable_signer_in_geth_compat() {
// given

View File

@@ -1,9 +1,16 @@
pub use parity_ipfs_api::start_server;
use std::sync::Arc;
use parity_ipfs_api;
use parity_ipfs_api::error::ServerError;
use ethcore::client::BlockChainClient;
use hyper::server::Listening;
#[derive(Debug, PartialEq, Clone)]
pub struct Configuration {
pub enabled: bool,
pub port: u16,
pub interface: String,
pub cors: Option<Vec<String>>,
pub hosts: Option<Vec<String>>,
}
impl Default for Configuration {
@@ -11,6 +18,23 @@ impl Default for Configuration {
Configuration {
enabled: false,
port: 5001,
interface: "127.0.0.1".into(),
cors: None,
hosts: Some(Vec::new()),
}
}
}
pub fn start_server(conf: Configuration, client: Arc<BlockChainClient>) -> Result<Option<Listening>, ServerError> {
if !conf.enabled {
return Ok(None);
}
parity_ipfs_api::start_server(
conf.port,
conf.interface,
conf.cors,
conf.hosts,
client
).map(Some)
}

View File

@@ -471,10 +471,7 @@ pub fn execute(cmd: RunCmd, can_restart: bool, logger: Arc<RotatingLogger>) -> R
let secretstore_key_server = secretstore::start(cmd.secretstore_conf.clone(), secretstore_deps);
// the ipfs server
let ipfs_server = match cmd.ipfs_conf.enabled {
true => Some(ipfs::start_server(cmd.ipfs_conf.port, client.clone())?),
false => None,
};
let ipfs_server = ipfs::start_server(cmd.ipfs_conf.clone(), client.clone())?;
// the informant
let informant = Arc::new(Informant::new(