Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CLI.md
Original file line number Diff line number Diff line change
Expand Up @@ -514,6 +514,7 @@ Create genesis configuration for a Linera deployment. Create initial user chains
* `--maximum-block-proposal-size <MAXIMUM_BLOCK_PROPOSAL_SIZE>` — Set the maximum size of a block proposal, in bytes. (This will overwrite value from `--policy-config`)
* `--maximum-bytes-read-per-block <MAXIMUM_BYTES_READ_PER_BLOCK>` — Set the maximum read data per block. (This will overwrite value from `--policy-config`)
* `--maximum-bytes-written-per-block <MAXIMUM_BYTES_WRITTEN_PER_BLOCK>` — Set the maximum write data per block. (This will overwrite value from `--policy-config`)
* `--http-allow-list <HTTP_ALLOW_LIST>` — Set the list of hosts that contracts and services can send HTTP requests to
* `--testing-prng-seed <TESTING_PRNG_SEED>` — Force this wallet to generate keys using a PRNG and a given seed. USE FOR TESTING ONLY
* `--network-name <NETWORK_NAME>` — A unique name to identify this network

Expand Down
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions examples/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion linera-chain/src/unit_tests/chain_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ async fn test_block_size_limit() {
let mut chain = ChainStateView::new(chain_id).await;

// The size of the executed valid block below.
let maximum_executed_block_size = 723;
let maximum_executed_block_size = 724;

// Initialize the chain.
let mut config = make_open_chain_config();
Expand Down
4 changes: 4 additions & 0 deletions linera-client/src/client_options.rs
Original file line number Diff line number Diff line change
Expand Up @@ -757,6 +757,10 @@ pub enum ClientCommand {
#[arg(long)]
maximum_bytes_written_per_block: Option<u64>,

/// Set the list of hosts that contracts and services can send HTTP requests to.
#[arg(long)]
http_allow_list: Option<Vec<String>>,

/// Force this wallet to generate keys using a PRNG and a given seed. USE FOR
/// TESTING ONLY.
#[arg(long)]
Expand Down
1 change: 1 addition & 0 deletions linera-execution/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ serde_json.workspace = true
tempfile = { workspace = true, optional = true }
thiserror.workspace = true
tracing = { workspace = true, features = ["log"] }
url.workspace = true
wasm-encoder = { workspace = true, optional = true }
wasm-instrument = { workspace = true, optional = true, features = ["sign_ext"] }
wasmparser = { workspace = true, optional = true }
Expand Down
22 changes: 19 additions & 3 deletions linera-execution/src/execution_state_actor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,15 @@ use linera_base::prometheus_util::{
};
use linera_base::{
data_types::{Amount, ApplicationPermissions, BlobContent, BlockHeight, Timestamp},
hex_debug, hex_vec_debug, http,
ensure, hex_debug, hex_vec_debug, http,
identifiers::{Account, AccountOwner, BlobId, ChainId, MessageId, Owner},
ownership::ChainOwnership,
};
use linera_views::{batch::Batch, context::Context, views::View};
use oneshot::Sender;
#[cfg(with_metrics)]
use prometheus::HistogramVec;
use reqwest::{header::HeaderMap, Client};
use reqwest::{header::HeaderMap, Client, Url};

use crate::{
system::{CreateApplicationResult, OpenChainConfig, Recipient},
Expand Down Expand Up @@ -379,8 +379,24 @@ where
.map(|http::Header { name, value }| Ok((name.parse()?, value.try_into()?)))
.collect::<Result<HeaderMap, ExecutionError>>()?;

let url = Url::parse(&request.url)?;
let host = url
.host_str()
.ok_or_else(|| ExecutionError::UnauthorizedHttpRequest(url.clone()))?;
Comment thread
deuszx marked this conversation as resolved.

let (_epoch, committee) = self
.system
.current_committee()
.ok_or_else(|| ExecutionError::UnauthorizedHttpRequest(url.clone()))?;
let allowed_hosts = &committee.policy().http_request_allow_list;

ensure!(
allowed_hosts.contains(host),
ExecutionError::UnauthorizedHttpRequest(url)
);

let response = Client::new()
.request(request.method.into(), request.url)
.request(request.method.into(), url)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we whitelist also methods?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think that's needed. We could make the allow list more complicated, mapping hosts to a set of allowed methods, but where do we draw the line? We could also limit headers, ports, etc. Right now I'm assuming that the allowed HTTP servers will be safe to communicated with.

.body(request.body)
.headers(headers)
.send()
Expand Down
4 changes: 4 additions & 0 deletions linera-execution/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -268,6 +268,10 @@ pub enum ExecutionError {
BlobTooLarge,
#[error("Bytecode exceeds size limit")]
BytecodeTooLarge,
#[error("Attempt to perform an HTTP request to an unauthorized host: {0:?}")]
UnauthorizedHttpRequest(reqwest::Url),
#[error("Attempt to perform an HTTP request to an invalid URL")]
InvalidUrlForHttpRequest(#[from] url::ParseError),
// TODO(#2127): Remove this error and the unstable-oracles feature once there are fees
// and enforced limits for all oracles.
#[error("Unstable oracles are disabled on this network.")]
Expand Down
13 changes: 10 additions & 3 deletions linera-execution/src/policy.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

//! This module contains types related to fees and pricing.

use std::fmt;
use std::{collections::BTreeSet, fmt};

use async_graphql::InputObject;
use linera_base::{
Expand Down Expand Up @@ -61,6 +61,8 @@ pub struct ResourceControlPolicy {
pub maximum_bytes_read_per_block: u64,
/// The maximum data to write per block
pub maximum_bytes_written_per_block: u64,
/// The list of hosts that contracts and services can send HTTP requests to.
pub http_request_allow_list: BTreeSet<String>,
}

impl fmt::Display for ResourceControlPolicy {
Expand All @@ -85,6 +87,7 @@ impl fmt::Display for ResourceControlPolicy {
maximum_block_proposal_size,
maximum_bytes_read_per_block,
maximum_bytes_written_per_block,
http_request_allow_list,
} = self;
write!(
f,
Expand All @@ -107,8 +110,10 @@ impl fmt::Display for ResourceControlPolicy {
{maximum_bytecode_size} maximum size of service and contract bytecode\n\
{maximum_block_proposal_size} maximum size of a block proposal\n\
{maximum_bytes_read_per_block} maximum number bytes read per block\n\
{maximum_bytes_written_per_block} maximum number bytes written per block",
)
{maximum_bytes_written_per_block} maximum number bytes written per block\n\
HTTP hosts allowed for contracts and services: {http_request_allow_list:#?}\n",
)?;
Ok(())
}
}

Expand Down Expand Up @@ -143,6 +148,7 @@ impl ResourceControlPolicy {
maximum_block_proposal_size: u64::MAX,
maximum_bytes_read_per_block: u64::MAX,
maximum_bytes_written_per_block: u64::MAX,
http_request_allow_list: BTreeSet::new(),
}
}

Expand Down Expand Up @@ -207,6 +213,7 @@ impl ResourceControlPolicy {
maximum_block_proposal_size: 13_000_000,
maximum_bytes_read_per_block: 100_000_000,
maximum_bytes_written_per_block: 10_000_000,
http_request_allow_list: BTreeSet::new(),
}
}

Expand Down
3 changes: 2 additions & 1 deletion linera-execution/tests/fee_consumption.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

#![allow(clippy::items_after_test_module)]

use std::{sync::Arc, vec};
use std::{collections::BTreeSet, sync::Arc, vec};

use linera_base::{
crypto::{AccountPublicKey, CryptoHash},
Expand Down Expand Up @@ -153,6 +153,7 @@ async fn test_fee_consumption(
maximum_block_proposal_size: 53,
maximum_bytes_read_per_block: 59,
maximum_bytes_written_per_block: 61,
http_request_allow_list: BTreeSet::new(),
};

let consumed_fees = spends
Expand Down
2 changes: 2 additions & 0 deletions linera-rpc/tests/snapshots/format__format.yaml.snap
Original file line number Diff line number Diff line change
Expand Up @@ -842,6 +842,8 @@ ResourceControlPolicy:
- maximum_block_proposal_size: U64
- maximum_bytes_read_per_block: U64
- maximum_bytes_written_per_block: U64
- http_request_allow_list:
SEQ: STR
Response:
STRUCT:
- status: U16
Expand Down
4 changes: 4 additions & 0 deletions linera-service-graphql-client/gql/service_schema.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -1098,6 +1098,10 @@ input ResourceControlPolicy {
The maximum data to write per block
"""
maximumBytesWrittenPerBlock: Int!
"""
The list of hosts that contracts and services can send HTTP requests to.
"""
httpRequestAllowList: [String!]!
}

"""
Expand Down
1 change: 1 addition & 0 deletions linera-service/src/cli_wrappers/local_kubernetes_net.rs
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,7 @@ impl LineraNetConfig for LocalKubernetesNetConfig {
self.num_other_initial_chains,
self.initial_amount,
self.policy_config,
Some(vec!["localhost".to_owned()]),
)
.await
.unwrap();
Expand Down
1 change: 1 addition & 0 deletions linera-service/src/cli_wrappers/local_net.rs
Original file line number Diff line number Diff line change
Expand Up @@ -297,6 +297,7 @@ impl LineraNetConfig for LocalNetConfig {
self.num_other_initial_chains,
self.initial_amount,
self.policy_config,
Some(vec!["localhost".to_owned()]),
)
.await
.unwrap();
Expand Down
5 changes: 4 additions & 1 deletion linera-service/src/cli_wrappers/wallet.rs
Original file line number Diff line number Diff line change
Expand Up @@ -238,6 +238,7 @@ impl ClientWrapper {
num_other_initial_chains: u32,
initial_funding: Amount,
policy_config: ResourceControlPolicyConfig,
http_allow_list: Option<Vec<String>>,
) -> Result<()> {
let mut command = self.command().await?;
command
Expand All @@ -252,7 +253,9 @@ impl ClientWrapper {
"--policy-config",
&policy_config.to_string().to_kebab_case(),
]);

if let Some(allow_list) = http_allow_list {
command.arg("--http-allow-list").arg(allow_list.join(","));
}
if let Some(seed) = self.testing_prng_seed {
command.arg("--testing-prng-seed").arg(seed.to_string());
}
Expand Down
12 changes: 11 additions & 1 deletion linera-service/src/linera/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,13 @@
#![deny(clippy::large_futures)]

use std::{
borrow::Cow, collections::HashMap, env, path::PathBuf, process, sync::Arc, time::Instant,
borrow::Cow,
collections::{BTreeSet, HashMap},
env,
path::PathBuf,
process,
sync::Arc,
time::Instant,
};

use anyhow::{anyhow, bail, ensure, Context};
Expand Down Expand Up @@ -1412,6 +1418,7 @@ async fn run(options: &ClientOptions) -> Result<i32, anyhow::Error> {
maximum_bytes_written_per_block,
testing_prng_seed,
network_name,
http_allow_list,
} => {
let start_time = Instant::now();
let committee_config: CommitteeConfig = util::read_json(committee_config_path)
Expand Down Expand Up @@ -1474,6 +1481,9 @@ async fn run(options: &ClientOptions) -> Result<i32, anyhow::Error> {
if let Some(maximum_bytes_written_per_block) = maximum_bytes_written_per_block {
policy.maximum_bytes_written_per_block = *maximum_bytes_written_per_block;
}
if let Some(http_allow_list) = http_allow_list {
policy.http_request_allow_list = BTreeSet::from_iter(http_allow_list.clone());
}
let timestamp = start_timestamp
.map(|st| {
let micros =
Expand Down
Loading