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 = [
|
||||
"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)",
|
||||
"ethcore-devtools 1.4.0",
|
||||
"ethcore-rpc 1.4.0",
|
||||
"ethcore-util 1.4.0",
|
||||
"hyper 0.9.4 (git+https://github.com/ethcore/hyper)",
|
||||
|
@ -108,14 +108,28 @@ impl ServerBuilder {
|
||||
|
||||
/// Asynchronously start server with no authentication,
|
||||
/// returns result with `Server` handle on success or an error.
|
||||
pub fn start_unsecure_http(&self, addr: &SocketAddr) -> Result<Server, ServerError> {
|
||||
Server::start_http(addr, NoAuth, self.handler.clone(), self.dapps_path.clone(), self.registrar.clone())
|
||||
pub fn start_unsecured_http(&self, addr: &SocketAddr, hosts: Option<Vec<String>>) -> Result<Server, ServerError> {
|
||||
Server::start_http(
|
||||
addr,
|
||||
hosts,
|
||||
NoAuth,
|
||||
self.handler.clone(),
|
||||
self.dapps_path.clone(),
|
||||
self.registrar.clone()
|
||||
)
|
||||
}
|
||||
|
||||
/// Asynchronously start server with `HTTP Basic Authentication`,
|
||||
/// 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> {
|
||||
Server::start_http(addr, HttpBasicAuth::single_user(username, password), self.handler.clone(), self.dapps_path.clone(), self.registrar.clone())
|
||||
pub fn start_basic_auth_http(&self, addr: &SocketAddr, hosts: Option<Vec<String>>, username: &str, password: &str) -> Result<Server, ServerError> {
|
||||
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 {
|
||||
/// 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>(
|
||||
addr: &SocketAddr,
|
||||
hosts: Option<Vec<String>>,
|
||||
authorization: A,
|
||||
handler: Arc<IoHandler>,
|
||||
dapps_path: String,
|
||||
@ -144,7 +174,7 @@ impl Server {
|
||||
special.insert(router::SpecialEndpoint::Utils, apps::utils());
|
||||
special
|
||||
});
|
||||
let bind_address = format!("{}", addr);
|
||||
let hosts = Self::allowed_hosts(hosts, format!("{}", addr));
|
||||
|
||||
try!(hyper::Server::http(addr))
|
||||
.handle(move |ctrl| router::Router::new(
|
||||
@ -154,7 +184,7 @@ impl Server {
|
||||
endpoints.clone(),
|
||||
special.clone(),
|
||||
authorization.clone(),
|
||||
bind_address.clone(),
|
||||
hosts.clone(),
|
||||
))
|
||||
.map(|(l, srv)| {
|
||||
|
||||
@ -207,3 +237,23 @@ pub fn random_filename() -> String {
|
||||
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 handlers::ContentHandler;
|
||||
|
||||
pub fn is_valid(request: &server::Request<HttpStream>, bind_address: &str, endpoints: Vec<String>) -> bool {
|
||||
let mut endpoints = endpoints.into_iter()
|
||||
pub fn is_valid(request: &server::Request<HttpStream>, allowed_hosts: &[String], endpoints: Vec<String>) -> bool {
|
||||
let mut endpoints = endpoints.iter()
|
||||
.map(|endpoint| format!("{}{}", endpoint, DAPPS_DOMAIN))
|
||||
.collect::<Vec<String>>();
|
||||
// Add localhost domain as valid too if listening on loopback interface.
|
||||
endpoints.push(bind_address.replace("127.0.0.1", "localhost").into());
|
||||
endpoints.push(bind_address.into());
|
||||
endpoints.extend_from_slice(allowed_hosts);
|
||||
|
||||
let header_valid = is_host_header_valid(request, &endpoints);
|
||||
|
||||
|
@ -48,7 +48,7 @@ pub struct Router<A: Authorization + 'static> {
|
||||
fetch: Arc<AppFetcher>,
|
||||
special: Arc<HashMap<SpecialEndpoint, Box<Endpoint>>>,
|
||||
authorization: Arc<A>,
|
||||
bind_address: String,
|
||||
allowed_hosts: Option<Vec<String>>,
|
||||
handler: Box<server::Handler<HttpStream> + Send>,
|
||||
}
|
||||
|
||||
@ -56,10 +56,12 @@ impl<A: Authorization + 'static> server::Handler<HttpStream> for Router<A> {
|
||||
|
||||
fn on_request(&mut self, req: server::Request<HttpStream>) -> Next {
|
||||
// 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 {
|
||||
if !host_validation::is_valid(&req, hosts, self.endpoints.keys().cloned().collect()) {
|
||||
self.handler = host_validation::host_invalid_response();
|
||||
return self.handler.on_request(req);
|
||||
}
|
||||
}
|
||||
|
||||
// Check authorization
|
||||
let auth = self.authorization.is_authorized(&req);
|
||||
@ -125,7 +127,7 @@ impl<A: Authorization> Router<A> {
|
||||
endpoints: Arc<Endpoints>,
|
||||
special: Arc<HashMap<SpecialEndpoint, Box<Endpoint>>>,
|
||||
authorization: Arc<A>,
|
||||
bind_address: String,
|
||||
allowed_hosts: Option<Vec<String>>,
|
||||
) -> Self {
|
||||
|
||||
let handler = special.get(&SpecialEndpoint::Rpc).unwrap().to_handler(EndpointPath::default());
|
||||
@ -136,7 +138,7 @@ impl<A: Authorization> Router<A> {
|
||||
fetch: app_fetcher,
|
||||
special: special,
|
||||
authorization: authorization,
|
||||
bind_address: bind_address,
|
||||
allowed_hosts: allowed_hosts,
|
||||
handler: handler,
|
||||
}
|
||||
}
|
||||
|
@ -134,6 +134,11 @@ API and Console Options:
|
||||
--dapps-interface IP Specify the hostname portion of the Dapps
|
||||
server, IP should be an interface's IP address,
|
||||
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
|
||||
used in HTTP Basic Authentication Scheme.
|
||||
If --dapps-pass is not specified you will be
|
||||
@ -346,6 +351,7 @@ pub struct Args {
|
||||
pub flag_no_dapps: bool,
|
||||
pub flag_dapps_port: u16,
|
||||
pub flag_dapps_interface: String,
|
||||
pub flag_dapps_hosts: String,
|
||||
pub flag_dapps_user: Option<String>,
|
||||
pub flag_dapps_pass: Option<String>,
|
||||
pub flag_dapps_path: String,
|
||||
|
@ -356,6 +356,7 @@ impl Configuration {
|
||||
enabled: self.dapps_enabled(),
|
||||
interface: self.dapps_interface(),
|
||||
port: self.args.flag_dapps_port,
|
||||
hosts: self.dapps_hosts(),
|
||||
user: self.args.flag_dapps_user.clone(),
|
||||
pass: self.args.flag_dapps_pass.clone(),
|
||||
dapps_path: self.directories().dapps,
|
||||
@ -485,6 +486,16 @@ impl Configuration {
|
||||
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> {
|
||||
let conf = IpcConfiguration {
|
||||
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()]));
|
||||
}
|
||||
|
||||
#[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]
|
||||
fn should_disable_signer_in_geth_compat() {
|
||||
// given
|
||||
|
@ -25,6 +25,7 @@ pub struct Configuration {
|
||||
pub enabled: bool,
|
||||
pub interface: String,
|
||||
pub port: u16,
|
||||
pub hosts: Option<Vec<String>>,
|
||||
pub user: Option<String>,
|
||||
pub pass: Option<String>,
|
||||
pub dapps_path: String,
|
||||
@ -36,6 +37,7 @@ impl Default for Configuration {
|
||||
enabled: true,
|
||||
interface: "127.0.0.1".into(),
|
||||
port: 8080,
|
||||
hosts: Some(Vec::new()),
|
||||
user: None,
|
||||
pass: None,
|
||||
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)
|
||||
});
|
||||
|
||||
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;
|
||||
@ -84,6 +86,7 @@ mod server {
|
||||
_deps: Dependencies,
|
||||
_dapps_path: String,
|
||||
_url: &SocketAddr,
|
||||
_allowed_hosts: Option<Vec<String>>,
|
||||
_auth: Option<(String, String)>,
|
||||
) -> Result<WebappServer, String> {
|
||||
Err("Your Parity version has been compiled without WebApps support.".into())
|
||||
@ -109,6 +112,7 @@ mod server {
|
||||
deps: Dependencies,
|
||||
dapps_path: String,
|
||||
url: &SocketAddr,
|
||||
allowed_hosts: Option<Vec<String>>,
|
||||
auth: Option<(String, String)>
|
||||
) -> Result<WebappServer, String> {
|
||||
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 start_result = match auth {
|
||||
None => {
|
||||
server.start_unsecure_http(url)
|
||||
server.start_unsecured_http(url, allowed_hosts)
|
||||
},
|
||||
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