dapps-hosts configuration
This commit is contained in:
parent
3e07135df3
commit
0baa8a53a5
1
Cargo.lock
generated
1
Cargo.lock
generated
@ -290,7 +290,6 @@ version = "1.4.0"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"clippy 0.0.85 (registry+https://github.com/rust-lang/crates.io-index)",
|
"clippy 0.0.85 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"ethabi 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"ethabi 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"ethcore-devtools 1.4.0",
|
|
||||||
"ethcore-rpc 1.4.0",
|
"ethcore-rpc 1.4.0",
|
||||||
"ethcore-util 1.4.0",
|
"ethcore-util 1.4.0",
|
||||||
"hyper 0.9.4 (git+https://github.com/ethcore/hyper)",
|
"hyper 0.9.4 (git+https://github.com/ethcore/hyper)",
|
||||||
|
@ -108,14 +108,28 @@ impl ServerBuilder {
|
|||||||
|
|
||||||
/// Asynchronously start server with no authentication,
|
/// Asynchronously start server with no authentication,
|
||||||
/// returns result with `Server` handle on success or an error.
|
/// returns result with `Server` handle on success or an error.
|
||||||
pub fn start_unsecure_http(&self, addr: &SocketAddr) -> Result<Server, ServerError> {
|
pub fn start_unsecured_http(&self, addr: &SocketAddr, hosts: Option<Vec<String>>) -> Result<Server, ServerError> {
|
||||||
Server::start_http(addr, NoAuth, self.handler.clone(), self.dapps_path.clone(), self.registrar.clone())
|
Server::start_http(
|
||||||
|
addr,
|
||||||
|
hosts,
|
||||||
|
NoAuth,
|
||||||
|
self.handler.clone(),
|
||||||
|
self.dapps_path.clone(),
|
||||||
|
self.registrar.clone()
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Asynchronously start server with `HTTP Basic Authentication`,
|
/// Asynchronously start server with `HTTP Basic Authentication`,
|
||||||
/// return result with `Server` handle on success or an error.
|
/// return result with `Server` handle on success or an error.
|
||||||
pub fn start_basic_auth_http(&self, addr: &SocketAddr, username: &str, password: &str) -> Result<Server, ServerError> {
|
pub fn start_basic_auth_http(&self, addr: &SocketAddr, hosts: Option<Vec<String>>, username: &str, password: &str) -> Result<Server, ServerError> {
|
||||||
Server::start_http(addr, HttpBasicAuth::single_user(username, password), self.handler.clone(), self.dapps_path.clone(), self.registrar.clone())
|
Server::start_http(
|
||||||
|
addr,
|
||||||
|
hosts,
|
||||||
|
HttpBasicAuth::single_user(username, password),
|
||||||
|
self.handler.clone(),
|
||||||
|
self.dapps_path.clone(),
|
||||||
|
self.registrar.clone()
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -126,8 +140,24 @@ pub struct Server {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Server {
|
impl Server {
|
||||||
|
/// Returns a list of allowed hosts or `None` if all hosts are allowed.
|
||||||
|
fn allowed_hosts(hosts: Option<Vec<String>>, bind_address: String) -> Option<Vec<String>> {
|
||||||
|
let mut allowed = Vec::new();
|
||||||
|
|
||||||
|
match hosts {
|
||||||
|
Some(hosts) => allowed.extend_from_slice(&hosts),
|
||||||
|
None => return None,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add localhost domain as valid too if listening on loopback interface.
|
||||||
|
allowed.push(bind_address.replace("127.0.0.1", "localhost").into());
|
||||||
|
allowed.push(bind_address.into());
|
||||||
|
Some(allowed)
|
||||||
|
}
|
||||||
|
|
||||||
fn start_http<A: Authorization + 'static>(
|
fn start_http<A: Authorization + 'static>(
|
||||||
addr: &SocketAddr,
|
addr: &SocketAddr,
|
||||||
|
hosts: Option<Vec<String>>,
|
||||||
authorization: A,
|
authorization: A,
|
||||||
handler: Arc<IoHandler>,
|
handler: Arc<IoHandler>,
|
||||||
dapps_path: String,
|
dapps_path: String,
|
||||||
@ -144,7 +174,7 @@ impl Server {
|
|||||||
special.insert(router::SpecialEndpoint::Utils, apps::utils());
|
special.insert(router::SpecialEndpoint::Utils, apps::utils());
|
||||||
special
|
special
|
||||||
});
|
});
|
||||||
let bind_address = format!("{}", addr);
|
let hosts = Self::allowed_hosts(hosts, format!("{}", addr));
|
||||||
|
|
||||||
try!(hyper::Server::http(addr))
|
try!(hyper::Server::http(addr))
|
||||||
.handle(move |ctrl| router::Router::new(
|
.handle(move |ctrl| router::Router::new(
|
||||||
@ -154,7 +184,7 @@ impl Server {
|
|||||||
endpoints.clone(),
|
endpoints.clone(),
|
||||||
special.clone(),
|
special.clone(),
|
||||||
authorization.clone(),
|
authorization.clone(),
|
||||||
bind_address.clone(),
|
hosts.clone(),
|
||||||
))
|
))
|
||||||
.map(|(l, srv)| {
|
.map(|(l, srv)| {
|
||||||
|
|
||||||
@ -207,3 +237,23 @@ pub fn random_filename() -> String {
|
|||||||
rng.gen_ascii_chars().take(12).collect()
|
rng.gen_ascii_chars().take(12).collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::Server;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn should_return_allowed_hosts() {
|
||||||
|
// given
|
||||||
|
let bind_address = "127.0.0.1".to_owned();
|
||||||
|
|
||||||
|
// when
|
||||||
|
let all = Server::allowed_hosts(None, bind_address.clone());
|
||||||
|
let address = Server::allowed_hosts(Some(Vec::new()), bind_address.clone());
|
||||||
|
let some = Server::allowed_hosts(Some(vec!["ethcore.io".into()]), bind_address.clone());
|
||||||
|
|
||||||
|
// then
|
||||||
|
assert_eq!(all, None);
|
||||||
|
assert_eq!(address, Some(vec!["localhost".into(), "127.0.0.1".into()]));
|
||||||
|
assert_eq!(some, Some(vec!["ethcore.io".into(), "localhost".into(), "127.0.0.1".into()]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -22,13 +22,11 @@ use hyper::net::HttpStream;
|
|||||||
use jsonrpc_http_server::{is_host_header_valid};
|
use jsonrpc_http_server::{is_host_header_valid};
|
||||||
use handlers::ContentHandler;
|
use handlers::ContentHandler;
|
||||||
|
|
||||||
pub fn is_valid(request: &server::Request<HttpStream>, bind_address: &str, endpoints: Vec<String>) -> bool {
|
pub fn is_valid(request: &server::Request<HttpStream>, allowed_hosts: &[String], endpoints: Vec<String>) -> bool {
|
||||||
let mut endpoints = endpoints.into_iter()
|
let mut endpoints = endpoints.iter()
|
||||||
.map(|endpoint| format!("{}{}", endpoint, DAPPS_DOMAIN))
|
.map(|endpoint| format!("{}{}", endpoint, DAPPS_DOMAIN))
|
||||||
.collect::<Vec<String>>();
|
.collect::<Vec<String>>();
|
||||||
// Add localhost domain as valid too if listening on loopback interface.
|
endpoints.extend_from_slice(allowed_hosts);
|
||||||
endpoints.push(bind_address.replace("127.0.0.1", "localhost").into());
|
|
||||||
endpoints.push(bind_address.into());
|
|
||||||
|
|
||||||
let header_valid = is_host_header_valid(request, &endpoints);
|
let header_valid = is_host_header_valid(request, &endpoints);
|
||||||
|
|
||||||
|
@ -48,7 +48,7 @@ pub struct Router<A: Authorization + 'static> {
|
|||||||
fetch: Arc<AppFetcher>,
|
fetch: Arc<AppFetcher>,
|
||||||
special: Arc<HashMap<SpecialEndpoint, Box<Endpoint>>>,
|
special: Arc<HashMap<SpecialEndpoint, Box<Endpoint>>>,
|
||||||
authorization: Arc<A>,
|
authorization: Arc<A>,
|
||||||
bind_address: String,
|
allowed_hosts: Option<Vec<String>>,
|
||||||
handler: Box<server::Handler<HttpStream> + Send>,
|
handler: Box<server::Handler<HttpStream> + Send>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -56,9 +56,11 @@ impl<A: Authorization + 'static> server::Handler<HttpStream> for Router<A> {
|
|||||||
|
|
||||||
fn on_request(&mut self, req: server::Request<HttpStream>) -> Next {
|
fn on_request(&mut self, req: server::Request<HttpStream>) -> Next {
|
||||||
// Validate Host header
|
// Validate Host header
|
||||||
if !host_validation::is_valid(&req, &self.bind_address, self.endpoints.keys().cloned().collect()) {
|
if let Some(ref hosts) = self.allowed_hosts {
|
||||||
self.handler = host_validation::host_invalid_response();
|
if !host_validation::is_valid(&req, hosts, self.endpoints.keys().cloned().collect()) {
|
||||||
return self.handler.on_request(req);
|
self.handler = host_validation::host_invalid_response();
|
||||||
|
return self.handler.on_request(req);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check authorization
|
// Check authorization
|
||||||
@ -125,7 +127,7 @@ impl<A: Authorization> Router<A> {
|
|||||||
endpoints: Arc<Endpoints>,
|
endpoints: Arc<Endpoints>,
|
||||||
special: Arc<HashMap<SpecialEndpoint, Box<Endpoint>>>,
|
special: Arc<HashMap<SpecialEndpoint, Box<Endpoint>>>,
|
||||||
authorization: Arc<A>,
|
authorization: Arc<A>,
|
||||||
bind_address: String,
|
allowed_hosts: Option<Vec<String>>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
|
|
||||||
let handler = special.get(&SpecialEndpoint::Rpc).unwrap().to_handler(EndpointPath::default());
|
let handler = special.get(&SpecialEndpoint::Rpc).unwrap().to_handler(EndpointPath::default());
|
||||||
@ -136,7 +138,7 @@ impl<A: Authorization> Router<A> {
|
|||||||
fetch: app_fetcher,
|
fetch: app_fetcher,
|
||||||
special: special,
|
special: special,
|
||||||
authorization: authorization,
|
authorization: authorization,
|
||||||
bind_address: bind_address,
|
allowed_hosts: allowed_hosts,
|
||||||
handler: handler,
|
handler: handler,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -134,6 +134,11 @@ API and Console Options:
|
|||||||
--dapps-interface IP Specify the hostname portion of the Dapps
|
--dapps-interface IP Specify the hostname portion of the Dapps
|
||||||
server, IP should be an interface's IP address,
|
server, IP should be an interface's IP address,
|
||||||
or local [default: local].
|
or local [default: local].
|
||||||
|
--dapps-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: none].
|
||||||
--dapps-user USERNAME Specify username for Dapps server. It will be
|
--dapps-user USERNAME Specify username for Dapps server. It will be
|
||||||
used in HTTP Basic Authentication Scheme.
|
used in HTTP Basic Authentication Scheme.
|
||||||
If --dapps-pass is not specified you will be
|
If --dapps-pass is not specified you will be
|
||||||
@ -346,6 +351,7 @@ pub struct Args {
|
|||||||
pub flag_no_dapps: bool,
|
pub flag_no_dapps: bool,
|
||||||
pub flag_dapps_port: u16,
|
pub flag_dapps_port: u16,
|
||||||
pub flag_dapps_interface: String,
|
pub flag_dapps_interface: String,
|
||||||
|
pub flag_dapps_hosts: String,
|
||||||
pub flag_dapps_user: Option<String>,
|
pub flag_dapps_user: Option<String>,
|
||||||
pub flag_dapps_pass: Option<String>,
|
pub flag_dapps_pass: Option<String>,
|
||||||
pub flag_dapps_path: String,
|
pub flag_dapps_path: String,
|
||||||
|
@ -356,6 +356,7 @@ impl Configuration {
|
|||||||
enabled: self.dapps_enabled(),
|
enabled: self.dapps_enabled(),
|
||||||
interface: self.dapps_interface(),
|
interface: self.dapps_interface(),
|
||||||
port: self.args.flag_dapps_port,
|
port: self.args.flag_dapps_port,
|
||||||
|
hosts: self.dapps_hosts(),
|
||||||
user: self.args.flag_dapps_user.clone(),
|
user: self.args.flag_dapps_user.clone(),
|
||||||
pass: self.args.flag_dapps_pass.clone(),
|
pass: self.args.flag_dapps_pass.clone(),
|
||||||
dapps_path: self.directories().dapps,
|
dapps_path: self.directories().dapps,
|
||||||
@ -485,6 +486,16 @@ impl Configuration {
|
|||||||
Some(hosts)
|
Some(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)
|
||||||
|
}
|
||||||
|
|
||||||
fn ipc_config(&self) -> Result<IpcConfiguration, String> {
|
fn ipc_config(&self) -> Result<IpcConfiguration, String> {
|
||||||
let conf = IpcConfiguration {
|
let conf = IpcConfiguration {
|
||||||
enabled: !(self.args.flag_ipcdisable || self.args.flag_ipc_off || self.args.flag_no_ipc),
|
enabled: !(self.args.flag_ipcdisable || self.args.flag_ipc_off || self.args.flag_no_ipc),
|
||||||
@ -860,6 +871,23 @@ mod tests {
|
|||||||
assert_eq!(conf3.rpc_hosts(), Some(vec!["ethcore.io".into(), "something.io".into()]));
|
assert_eq!(conf3.rpc_hosts(), Some(vec!["ethcore.io".into(), "something.io".into()]));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn should_parse_dapps_hosts() {
|
||||||
|
// given
|
||||||
|
|
||||||
|
// when
|
||||||
|
let conf0 = parse(&["parity"]);
|
||||||
|
let conf1 = parse(&["parity", "--dapps-hosts", "none"]);
|
||||||
|
let conf2 = parse(&["parity", "--dapps-hosts", "all"]);
|
||||||
|
let conf3 = parse(&["parity", "--dapps-hosts", "ethcore.io,something.io"]);
|
||||||
|
|
||||||
|
// then
|
||||||
|
assert_eq!(conf0.dapps_hosts(), Some(Vec::new()));
|
||||||
|
assert_eq!(conf1.dapps_hosts(), Some(Vec::new()));
|
||||||
|
assert_eq!(conf2.dapps_hosts(), None);
|
||||||
|
assert_eq!(conf3.dapps_hosts(), Some(vec!["ethcore.io".into(), "something.io".into()]));
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn should_disable_signer_in_geth_compat() {
|
fn should_disable_signer_in_geth_compat() {
|
||||||
// given
|
// given
|
||||||
|
@ -25,6 +25,7 @@ pub struct Configuration {
|
|||||||
pub enabled: bool,
|
pub enabled: bool,
|
||||||
pub interface: String,
|
pub interface: String,
|
||||||
pub port: u16,
|
pub port: u16,
|
||||||
|
pub hosts: Option<Vec<String>>,
|
||||||
pub user: Option<String>,
|
pub user: Option<String>,
|
||||||
pub pass: Option<String>,
|
pub pass: Option<String>,
|
||||||
pub dapps_path: String,
|
pub dapps_path: String,
|
||||||
@ -36,6 +37,7 @@ impl Default for Configuration {
|
|||||||
enabled: true,
|
enabled: true,
|
||||||
interface: "127.0.0.1".into(),
|
interface: "127.0.0.1".into(),
|
||||||
port: 8080,
|
port: 8080,
|
||||||
|
hosts: Some(Vec::new()),
|
||||||
user: None,
|
user: None,
|
||||||
pass: None,
|
pass: None,
|
||||||
dapps_path: replace_home("$HOME/.parity/dapps"),
|
dapps_path: replace_home("$HOME/.parity/dapps"),
|
||||||
@ -68,7 +70,7 @@ pub fn new(configuration: Configuration, deps: Dependencies) -> Result<Option<We
|
|||||||
(username.to_owned(), password)
|
(username.to_owned(), password)
|
||||||
});
|
});
|
||||||
|
|
||||||
Ok(Some(try!(setup_dapps_server(deps, configuration.dapps_path, &addr, auth))))
|
Ok(Some(try!(setup_dapps_server(deps, configuration.dapps_path, &addr, configuration.hosts, auth))))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub use self::server::WebappServer;
|
pub use self::server::WebappServer;
|
||||||
@ -84,6 +86,7 @@ mod server {
|
|||||||
_deps: Dependencies,
|
_deps: Dependencies,
|
||||||
_dapps_path: String,
|
_dapps_path: String,
|
||||||
_url: &SocketAddr,
|
_url: &SocketAddr,
|
||||||
|
_allowed_hosts: Option<Vec<String>>,
|
||||||
_auth: Option<(String, String)>,
|
_auth: Option<(String, String)>,
|
||||||
) -> Result<WebappServer, String> {
|
) -> Result<WebappServer, String> {
|
||||||
Err("Your Parity version has been compiled without WebApps support.".into())
|
Err("Your Parity version has been compiled without WebApps support.".into())
|
||||||
@ -109,6 +112,7 @@ mod server {
|
|||||||
deps: Dependencies,
|
deps: Dependencies,
|
||||||
dapps_path: String,
|
dapps_path: String,
|
||||||
url: &SocketAddr,
|
url: &SocketAddr,
|
||||||
|
allowed_hosts: Option<Vec<String>>,
|
||||||
auth: Option<(String, String)>
|
auth: Option<(String, String)>
|
||||||
) -> Result<WebappServer, String> {
|
) -> Result<WebappServer, String> {
|
||||||
use ethcore_dapps as dapps;
|
use ethcore_dapps as dapps;
|
||||||
@ -119,10 +123,10 @@ mod server {
|
|||||||
let server = rpc_apis::setup_rpc(server, deps.apis.clone(), rpc_apis::ApiSet::UnsafeContext);
|
let server = rpc_apis::setup_rpc(server, deps.apis.clone(), rpc_apis::ApiSet::UnsafeContext);
|
||||||
let start_result = match auth {
|
let start_result = match auth {
|
||||||
None => {
|
None => {
|
||||||
server.start_unsecure_http(url)
|
server.start_unsecured_http(url, allowed_hosts)
|
||||||
},
|
},
|
||||||
Some((username, password)) => {
|
Some((username, password)) => {
|
||||||
server.start_basic_auth_http(url, &username, &password)
|
server.start_basic_auth_http(url, allowed_hosts, &username, &password)
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user