Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

rpc server: add rate limiting middleware #3301

Merged
merged 81 commits into from
Feb 17, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
81 commits
Select commit Hold shift + click to select a range
fb263ce
jsonrpsee v0.20.3
niklasad1 Aug 30, 2023
989ad97
RpcHandlers: fix panic in tokio::mpsc
niklasad1 Oct 30, 2023
310227c
fix host filtering
niklasad1 Oct 30, 2023
635e6f9
Update substrate/client/rpc-spec-v2/src/chain_head/chain_head.rs
niklasad1 Oct 31, 2023
97335eb
fix nits
niklasad1 Oct 31, 2023
49dcc5f
Merge remote-tracking branch 'origin/master' into na-jsonrpsee-v0.20
niklasad1 Oct 31, 2023
875319c
Update substrate/client/rpc-spec-v2/src/chain_head/chain_head.rs
niklasad1 Nov 1, 2023
98da3fc
cargo fmt
niklasad1 Nov 1, 2023
4be7c05
Merge remote-tracking branch 'origin/master' into na-jsonrpsee-v0.20
niklasad1 Nov 1, 2023
780cd6e
rpc rate limit PoC
niklasad1 Nov 2, 2023
395848c
add metrics support
niklasad1 Nov 3, 2023
eaaf70a
more work
niklasad1 Nov 6, 2023
f83d26a
cleanup
niklasad1 Nov 7, 2023
c520780
fix warnings
niklasad1 Nov 7, 2023
2f52aad
Merge remote-tracking branch 'origin/master' into na-jsonrpsee-middle…
niklasad1 Nov 8, 2023
9fb1404
fix bad merge
niklasad1 Nov 8, 2023
6a128be
patch jsonrpsee_core as well
niklasad1 Nov 8, 2023
1730922
option layer for metrics
niklasad1 Nov 9, 2023
0f1c200
Merge remote-tracking branch 'origin/master' into na-jsonrpsee-middle…
niklasad1 Nov 9, 2023
234f515
add CLI param --rpc-rate-limit
niklasad1 Nov 10, 2023
cb2e6bb
Merge remote-tracking branch 'origin/master' into na-jsonrpsee-middle…
niklasad1 Nov 10, 2023
d66415f
fix nits
niklasad1 Nov 10, 2023
4fb4e78
fix test build
niklasad1 Nov 10, 2023
242a4cb
update jsonrpsee
niklasad1 Nov 13, 2023
d2895ba
update jsonrpsee again
niklasad1 Dec 5, 2023
79d7b6f
Merge remote-tracking branch 'origin/master' into na-jsonrpsee-middle…
niklasad1 Dec 6, 2023
423018c
Merge remote-tracking branch 'origin/master' into na-jsonrpsee-middle…
niklasad1 Dec 6, 2023
615e72a
update jsonrpsee again
niklasad1 Dec 8, 2023
46f7e23
Merge remote-tracking branch 'origin/master' into na-jsonrpsee-middle…
niklasad1 Dec 8, 2023
ace52dc
fix nits
niklasad1 Dec 8, 2023
bd78593
fix more merge nits
niklasad1 Dec 8, 2023
c128075
fix cumulus
niklasad1 Dec 8, 2023
271307a
fix more nits
niklasad1 Dec 9, 2023
284da03
Merge branch 'master' into na-jsonrpsee-middleware-poc
niklasad1 Dec 11, 2023
15439db
Merge remote-tracking branch 'origin/master' into na-jsonrpsee-middle…
niklasad1 Jan 24, 2024
3f7e94b
jsonrpsee v0.22
niklasad1 Feb 6, 2024
250f109
fix race in chainhead
niklasad1 Feb 6, 2024
8bc7fd7
fix some nits
niklasad1 Feb 6, 2024
b0769b7
fix bad Cargo.toml fmt
niklasad1 Feb 6, 2024
3d16db8
make test compile
niklasad1 Feb 6, 2024
a7032d9
fix tests
niklasad1 Feb 7, 2024
be5052a
jsonrpsee v0.22
niklasad1 Feb 7, 2024
6326b34
fix more nits
niklasad1 Feb 7, 2024
dc856ac
Merge remote-tracking branch 'origin/master' into na-bump-jsonrpsee-v…
niklasad1 Feb 7, 2024
6d088e1
Update cumulus/client/relay-chain-interface/Cargo.toml
niklasad1 Feb 7, 2024
b77a992
update Cargo.lock
niklasad1 Feb 7, 2024
4802cc4
fix rustdocs
niklasad1 Feb 7, 2024
485393f
remove needless box
niklasad1 Feb 8, 2024
bdc42f6
address grumbles
niklasad1 Feb 8, 2024
8bf3636
Update substrate/client/rpc-servers/src/lib.rs
niklasad1 Feb 8, 2024
3a78c54
clarify breaking ws pings
niklasad1 Feb 8, 2024
118480d
fix nits
niklasad1 Feb 8, 2024
104dc2d
Merge remote-tracking branch 'origin/master' into na-bump-jsonrpsee-v…
niklasad1 Feb 9, 2024
e310c9d
Merge remote-tracking branch 'origin/master' into na-jsonrpsee-rate-l…
niklasad1 Feb 12, 2024
9407c78
Merge remote-tracking branch 'origin/na-bump-jsonrpsee-v0.22' into na…
niklasad1 Feb 12, 2024
0f77b36
move to governor crate for rate-limiting
niklasad1 Feb 12, 2024
776be60
add API to inject custom middleware
niklasad1 Feb 13, 2024
0380d9a
Merge branch 'master' into na-jsonrpsee-rate-limit-middleware
niklasad1 Feb 14, 2024
7958229
rewrite me
niklasad1 Feb 14, 2024
4d92963
cleanup
niklasad1 Feb 14, 2024
3a9231d
cargo fmt
niklasad1 Feb 14, 2024
9f60cd5
fix prdoc
niklasad1 Feb 14, 2024
d1daf61
fix build again
niklasad1 Feb 14, 2024
6085873
Merge remote-tracking branch 'origin/master' into na-jsonrpsee-rate-l…
niklasad1 Feb 15, 2024
d2c6154
Update substrate/client/rpc-servers/src/middleware/mod.rs
niklasad1 Feb 15, 2024
7804d43
Update Cargo.toml
niklasad1 Feb 15, 2024
ca2d1f9
fix nits
niklasad1 Feb 15, 2024
e1c293e
more nits
niklasad1 Feb 15, 2024
2fccb5c
Update substrate/client/service/src/config.rs
niklasad1 Feb 15, 2024
de24325
Update prdoc/pr_3301.prdoc
niklasad1 Feb 15, 2024
7b5c885
cli: change to NonZeroU32
niklasad1 Feb 15, 2024
0fdc928
Merge remote-tracking branch 'origin/na-jsonrpsee-rate-limit-middlewa…
niklasad1 Feb 15, 2024
94a7ac9
cargo fmt
niklasad1 Feb 15, 2024
c2f0b6d
Update substrate/client/rpc-servers/src/middleware/rate_limit.rs
niklasad1 Feb 15, 2024
16273a4
address grumbles
niklasad1 Feb 15, 2024
d4841fd
Merge remote-tracking branch 'origin/na-jsonrpsee-rate-limit-middlewa…
niklasad1 Feb 15, 2024
970d5a4
address grumbles
niklasad1 Feb 15, 2024
c1c195a
grumbles: register metrics middleware first
niklasad1 Feb 15, 2024
4bc1b94
cargo fmt
niklasad1 Feb 15, 2024
2451e27
Merge branch 'master' into na-jsonrpsee-rate-limit-middleware
niklasad1 Feb 16, 2024
7fc7e11
Update substrate/client/cli/src/commands/run_cmd.rs
niklasad1 Feb 16, 2024
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
65 changes: 65 additions & 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 cumulus/test/service/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -801,6 +801,7 @@ pub fn node_config(
rpc_max_subs_per_conn: Default::default(),
rpc_port: 9945,
rpc_message_buffer_capacity: Default::default(),
rpc_rate_limit: None,
prometheus_config: None,
telemetry_endpoints: None,
default_heap_pages: None,
Expand Down
1 change: 1 addition & 0 deletions polkadot/node/test/service/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,7 @@ pub fn node_config(
rpc_max_subs_per_conn: Default::default(),
rpc_port: 9944,
rpc_message_buffer_capacity: Default::default(),
rpc_rate_limit: None,
prometheus_config: None,
telemetry_endpoints: None,
default_heap_pages: None,
Expand Down
11 changes: 11 additions & 0 deletions prdoc/pr_3301.prdoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# Schema: Polkadot SDK PRDoc Schema (prdoc) v1.0.0
# See doc at https://raw.githubusercontent.com/paritytech/polkadot-sdk/master/prdoc/schema_user.json

title: rpc server add rate limiting.

doc:
- audience: Node Operator
description: |
Add rate limiting for RPC server which can be utilized by the CLI `--rpc-rate-limit <calls per minute>`
The rate-limiting is disabled by default.
crates: [ ]
1 change: 1 addition & 0 deletions substrate/bin/node/cli/benches/block_production.rs
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ fn new_node(tokio_handle: Handle) -> node_cli::service::NewFullBase {
rpc_max_subs_per_conn: Default::default(),
rpc_port: 9944,
rpc_message_buffer_capacity: Default::default(),
rpc_rate_limit: None,
prometheus_config: None,
telemetry_endpoints: None,
default_heap_pages: None,
Expand Down
1 change: 1 addition & 0 deletions substrate/bin/node/cli/benches/transaction_pool.rs
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ fn new_node(tokio_handle: Handle) -> node_cli::service::NewFullBase {
rpc_max_subs_per_conn: Default::default(),
rpc_port: 9944,
rpc_message_buffer_capacity: Default::default(),
rpc_rate_limit: None,
prometheus_config: None,
telemetry_endpoints: None,
default_heap_pages: None,
Expand Down
18 changes: 17 additions & 1 deletion substrate/client/cli/src/commands/run_cmd.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,10 @@ use sc_service::{
ChainSpec, Role,
};
use sc_telemetry::TelemetryEndpoints;
use std::net::{IpAddr, Ipv4Addr, SocketAddr};
use std::{
net::{IpAddr, Ipv4Addr, SocketAddr},
num::NonZeroU32,
};

/// The `run` command used to run a node.
#[derive(Debug, Clone, Parser)]
Expand Down Expand Up @@ -82,6 +85,15 @@ pub struct RunCmd {
)]
pub rpc_methods: RpcMethods,

/// RPC rate limiting (calls/minute) for each connection.
///
/// This is disabled by default.
///
/// For example `--rpc-rate-limit 10` will maximum allow
/// 10 calls per minute per connection.
#[arg(long)]
pub rpc_rate_limit: Option<NonZeroU32>,

/// Set the maximum RPC request payload size for both HTTP and WS in megabytes.
#[arg(long, default_value_t = RPC_DEFAULT_MAX_REQUEST_SIZE_MB)]
pub rpc_max_request_size: u32,
Expand Down Expand Up @@ -399,6 +411,10 @@ impl CliConfiguration for RunCmd {
Ok(self.rpc_max_subscriptions_per_connection)
}

fn rpc_rate_limit(&self) -> Result<Option<NonZeroU32>> {
Ok(self.rpc_rate_limit)
}

fn transaction_pool(&self, is_dev: bool) -> Result<TransactionPoolOptions> {
Ok(self.pool_config.transaction_pool(is_dev))
}
Expand Down
8 changes: 7 additions & 1 deletion substrate/client/cli/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ use sc_service::{
BlocksPruning, ChainSpec, TracingReceiver,
};
use sc_tracing::logging::LoggerBuilder;
use std::{net::SocketAddr, path::PathBuf};
use std::{net::SocketAddr, num::NonZeroU32, path::PathBuf};

/// The maximum number of characters for a node name.
pub(crate) const NODE_NAME_MAX_LENGTH: usize = 64;
Expand Down Expand Up @@ -338,6 +338,11 @@ pub trait CliConfiguration<DCV: DefaultConfigurationValues = ()>: Sized {
Ok(RPC_DEFAULT_MESSAGE_CAPACITY_PER_CONN)
}

/// Rate limit calls per minute.
fn rpc_rate_limit(&self) -> Result<Option<NonZeroU32>> {
Ok(None)
}

/// Get the prometheus configuration (`None` if disabled)
///
/// By default this is `None`.
Expand Down Expand Up @@ -510,6 +515,7 @@ pub trait CliConfiguration<DCV: DefaultConfigurationValues = ()>: Sized {
rpc_max_subs_per_conn: self.rpc_max_subscriptions_per_connection()?,
rpc_port: DCV::rpc_listen_port(),
rpc_message_buffer_capacity: self.rpc_buffer_capacity_per_connection()?,
rpc_rate_limit: self.rpc_rate_limit()?,
prometheus_config: self
.prometheus_config(DCV::prometheus_listen_port(), &chain_spec)?,
telemetry_endpoints,
Expand Down
1 change: 1 addition & 0 deletions substrate/client/cli/src/runner.rs
Original file line number Diff line number Diff line change
Expand Up @@ -271,6 +271,7 @@ mod tests {
rpc_max_subs_per_conn: Default::default(),
rpc_message_buffer_capacity: Default::default(),
rpc_port: 9944,
rpc_rate_limit: None,
prometheus_config: None,
telemetry_endpoints: None,
default_heap_pages: None,
Expand Down
1 change: 1 addition & 0 deletions substrate/client/rpc-servers/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -27,3 +27,4 @@ http = "0.2.8"
hyper = "0.14.27"
futures = "0.3.29"
pin-project = "1.1.3"
governor = "0.6.0"
56 changes: 35 additions & 21 deletions substrate/client/rpc-servers/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,9 @@

pub mod middleware;

use std::{convert::Infallible, error::Error as StdError, net::SocketAddr, time::Duration};
use std::{
convert::Infallible, error::Error as StdError, net::SocketAddr, num::NonZeroU32, time::Duration,
};

use http::header::HeaderValue;
use hyper::{
Expand All @@ -31,10 +33,7 @@ use hyper::{
};
use jsonrpsee::{
server::{
middleware::{
http::{HostFilterLayer, ProxyGetRequestLayer},
rpc::RpcServiceBuilder,
},
middleware::http::{HostFilterLayer, ProxyGetRequestLayer},
stop_channel, ws, PingConfig, StopHandle, TowerServiceBuilder,
},
Methods, RpcModule,
Expand All @@ -43,11 +42,14 @@ use tokio::net::TcpListener;
use tower::Service;
use tower_http::cors::{AllowOrigin, CorsLayer};

pub use jsonrpsee::core::{
id_providers::{RandomIntegerIdProvider, RandomStringIdProvider},
traits::IdProvider,
pub use jsonrpsee::{
core::{
id_providers::{RandomIntegerIdProvider, RandomStringIdProvider},
traits::IdProvider,
},
server::middleware::rpc::RpcServiceBuilder,
};
pub use middleware::{MetricsLayer, RpcMetrics};
pub use middleware::{MetricsLayer, RateLimitLayer, RpcMetrics};

const MEGABYTE: u32 = 1024 * 1024;

Expand Down Expand Up @@ -79,12 +81,26 @@ pub struct Config<'a, M: Send + Sync + 'static> {
pub id_provider: Option<Box<dyn IdProvider>>,
/// Tokio runtime handle.
pub tokio_handle: tokio::runtime::Handle,
/// Rate limit calls per minute.
pub rate_limit: Option<NonZeroU32>,
}

#[derive(Debug, Clone)]
struct PerConnection<RpcMiddleware, HttpMiddleware> {
methods: Methods,
stop_handle: StopHandle,
metrics: Option<RpcMetrics>,
tokio_handle: tokio::runtime::Handle,
service_builder: TowerServiceBuilder<RpcMiddleware, HttpMiddleware>,
}

/// Start RPC server listening on given address.
pub async fn start_server<M: Send + Sync + 'static>(
pub async fn start_server<M>(
config: Config<'_, M>,
) -> Result<Server, Box<dyn StdError + Send + Sync>> {
) -> Result<Server, Box<dyn StdError + Send + Sync>>
where
M: Send + Sync,
{
let Config {
addrs,
cors,
Expand All @@ -97,6 +113,7 @@ pub async fn start_server<M: Send + Sync + 'static>(
id_provider,
tokio_handle,
rpc_api,
rate_limit,
} = config;

let std_listener = TcpListener::bind(addrs.as_slice()).await?.into_std()?;
Expand Down Expand Up @@ -153,7 +170,13 @@ pub async fn start_server<M: Send + Sync + 'static>(
let transport_label = if is_websocket { "ws" } else { "http" };

let metrics = metrics.map(|m| MetricsLayer::new(m, transport_label));
let rpc_middleware = RpcServiceBuilder::new().option_layer(metrics.clone());
let rate_limit = rate_limit.map(|r| RateLimitLayer::per_minute(r));

// NOTE: The metrics needs to run first to include rate-limited calls in the
// metrics.
let rpc_middleware =
RpcServiceBuilder::new().option_layer(metrics.clone()).option_layer(rate_limit);

let mut svc =
service_builder.set_rpc_middleware(rpc_middleware).build(methods, stop_handle);

Expand Down Expand Up @@ -245,12 +268,3 @@ fn format_cors(maybe_cors: Option<&Vec<String>>) -> String {
format!("{:?}", ["*"])
}
}

#[derive(Clone)]
struct PerConnection<RpcMiddleware, HttpMiddleware> {
methods: Methods,
stop_handle: StopHandle,
metrics: Option<RpcMetrics>,
tokio_handle: tokio::runtime::Handle,
service_builder: TowerServiceBuilder<RpcMiddleware, HttpMiddleware>,
}
4 changes: 4 additions & 0 deletions substrate/client/rpc-servers/src/middleware/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,10 @@

//! JSON-RPC specific middleware.

/// Grafana metrics middleware.
pub mod metrics;
/// Rate limit middleware.
pub mod rate_limit;

pub use metrics::*;
pub use rate_limit::*;
Loading
Loading