diff --git a/substrate/core/cli/src/lib.rs b/substrate/core/cli/src/lib.rs index d2c3cccebe..3ed7347b50 100644 --- a/substrate/core/cli/src/lib.rs +++ b/substrate/core/cli/src/lib.rs @@ -475,6 +475,11 @@ where config.rpc_ws = Some( parse_address(&format!("{}:{}", ws_interface, 9944), cli.ws_port)? ); + config.rpc_cors = cli.rpc_cors.unwrap_or_else(|| Some(vec![ + "http://localhost:*".into(), + "https://localhost:*".into(), + "https://polkadot.js.org".into() + ])); // Override telemetry if cli.no_telemetry { diff --git a/substrate/core/cli/src/params.rs b/substrate/core/cli/src/params.rs index 0c1c48eee1..d25eec2833 100644 --- a/substrate/core/cli/src/params.rs +++ b/substrate/core/cli/src/params.rs @@ -329,6 +329,13 @@ pub struct RunCmd { #[structopt(long = "ws-port", value_name = "PORT")] pub ws_port: Option, + /// Specify browser Origins allowed to access the HTTP & WS RPC servers. + /// It's a comma-separated list of origins (protocol://domain or special `null` value). + /// Value of `all` will disable origin validation. + /// Default is to allow localhost and https://polkadot.js.org origin. + #[structopt(long = "rpc-cors", value_name = "ORIGINS", parse(try_from_str = "parse_cors"))] + pub rpc_cors: Option>>, + /// Specify the pruning mode, a number of blocks to keep or 'archive'. Default is 256. #[structopt(long = "pruning", value_name = "PRUNING_MODE")] pub pruning: Option, @@ -470,6 +477,23 @@ fn parse_telemetry_endpoints(s: &str) -> Result<(String, u8), Box Result>, Box> { + let mut is_all = false; + let mut origins = Vec::new(); + for part in s.split(',') { + match part { + "all" | "*" => { + is_all = true; + break; + }, + other => origins.push(other.to_owned()), + } + } + + Ok(if is_all { None } else { Some(origins) }) +} + impl_augment_clap!(RunCmd); impl_get_log_filter!(RunCmd); diff --git a/substrate/core/rpc-servers/src/lib.rs b/substrate/core/rpc-servers/src/lib.rs index 939b2b93aa..b37895c503 100644 --- a/substrate/core/rpc-servers/src/lib.rs +++ b/substrate/core/rpc-servers/src/lib.rs @@ -57,13 +57,18 @@ pub fn rpc_handler( /// Start HTTP server listening on given address. pub fn start_http( addr: &std::net::SocketAddr, + cors: Option<&Vec>, io: RpcHandler, ) -> io::Result { http::ServerBuilder::new(io) .threads(4) .health_api(("/health", "system_health")) - .rest_api(http::RestApi::Unsecure) - .cors(http::DomainsValidation::Disabled) + .rest_api(if cors.is_some() { + http::RestApi::Secure + } else { + http::RestApi::Unsecure + }) + .cors(map_cors::(cors)) .max_request_body_size(MAX_PAYLOAD) .start_http(addr) } @@ -71,10 +76,12 @@ pub fn start_http( /// Start WS server listening on given address. pub fn start_ws( addr: &std::net::SocketAddr, + cors: Option<&Vec>, io: RpcHandler, ) -> io::Result { ws::ServerBuilder::with_meta_extractor(io, |context: &ws::RequestContext| Metadata::new(context.sender())) .max_payload(MAX_PAYLOAD) + .allowed_origins(map_cors(cors)) .start(addr) .map_err(|err| match err { ws::Error(ws::ErrorKind::Io(io), _) => io, @@ -85,3 +92,9 @@ pub fn start_ws( } }) } + +fn map_cors From<&'a str>>( + cors: Option<&Vec> +) -> http::DomainsValidation { + cors.map(|x| x.iter().map(AsRef::as_ref).map(Into::into).collect::>()).into() +} diff --git a/substrate/core/service/src/components.rs b/substrate/core/service/src/components.rs index eb37a69a14..ba27acb582 100644 --- a/substrate/core/service/src/components.rs +++ b/substrate/core/service/src/components.rs @@ -143,6 +143,7 @@ pub trait StartRPC { system_info: SystemInfo, rpc_http: Option, rpc_ws: Option, + rpc_cors: Option>, task_executor: TaskExecutor, transaction_pool: Arc>, ) -> error::Result; @@ -161,6 +162,7 @@ impl StartRPC for C where rpc_system_info: SystemInfo, rpc_http: Option, rpc_ws: Option, + rpc_cors: Option>, task_executor: TaskExecutor, transaction_pool: Arc>, ) -> error::Result { @@ -184,8 +186,8 @@ impl StartRPC for C where }; Ok(( - maybe_start_server(rpc_http, |address| rpc::start_http(address, handler()))?, - maybe_start_server(rpc_ws, |address| rpc::start_ws(address, handler()))?.map(Mutex::new), + maybe_start_server(rpc_http, |address| rpc::start_http(address, rpc_cors.as_ref(), handler()))?, + maybe_start_server(rpc_ws, |address| rpc::start_ws(address, rpc_cors.as_ref(), handler()))?.map(Mutex::new), )) } } diff --git a/substrate/core/service/src/config.rs b/substrate/core/service/src/config.rs index b7a3b8ba14..6f0f5032e1 100644 --- a/substrate/core/service/src/config.rs +++ b/substrate/core/service/src/config.rs @@ -64,6 +64,8 @@ pub struct Configuration { pub rpc_http: Option, /// RPC over Websockets binding address. `None` if disabled. pub rpc_ws: Option, + /// CORS settings for HTTP & WS servers. `None` if all origins are allowed. + pub rpc_cors: Option>, /// Telemetry service URL. `None` if disabled. pub telemetry_endpoints: Option, /// The default number of 64KB pages to allocate for Wasm execution @@ -97,6 +99,7 @@ impl Configuration Service { warn!("Using default protocol ID {:?} because none is configured in the \ chain specs", DEFAULT_PROTOCOL_ID ); - DEFAULT_PROTOCOL_ID + DEFAULT_PROTOCOL_ID } }.as_bytes(); let mut protocol_id = network::ProtocolId::default(); @@ -301,7 +301,7 @@ impl Service { }; let rpc = Components::RuntimeServices::start_rpc( client.clone(), network.clone(), has_bootnodes, system_info, config.rpc_http, - config.rpc_ws, task_executor.clone(), transaction_pool.clone(), + config.rpc_ws, config.rpc_cors.clone(), task_executor.clone(), transaction_pool.clone(), )?; // Telemetry diff --git a/substrate/core/service/test/src/lib.rs b/substrate/core/service/test/src/lib.rs index dcdae582fa..2d382c8ffa 100644 --- a/substrate/core/service/test/src/lib.rs +++ b/substrate/core/service/test/src/lib.rs @@ -120,6 +120,7 @@ fn node_config ( execution_strategies: Default::default(), rpc_http: None, rpc_ws: None, + rpc_cors: None, telemetry_endpoints: None, default_heap_pages: None, offchain_worker: false,