-
Notifications
You must be signed in to change notification settings - Fork 161
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: filter RPC methods from daemon (#5254)
- Loading branch information
1 parent
76a9ade
commit e959888
Showing
13 changed files
with
456 additions
and
125 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -65,6 +65,7 @@ M2 | |
macOS | ||
Mainnet | ||
mainnet | ||
namespace | ||
NV22 | ||
NV23 | ||
NV24 | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,108 @@ | ||
--- | ||
title: RPC methods filtering | ||
--- | ||
|
||
# RPC methods filtering | ||
|
||
## Why filter RPC methods? | ||
|
||
When running a Filecoin node, you might want to restrict the RPC methods that are available to the clients. This can be useful for security reasons, to limit the exposure of the node to the internet, or to reduce the load on the node by disabling unnecessary methods. | ||
|
||
:::note | ||
[JWT authentication](../knowledge_base/jwt_handling.md) is a different way to restrict access to the node. It allows you to authorize certain operations on the node using JWTs. However, JWT restrictions are hard-coded in the node and cannot be changed dynamically. If you want to make sure that a certain read-only method is not available to the clients, you can use the method filtering feature. | ||
|
||
The methods are first filtered by the method filtering feature, and then the JWT authentication is applied. If a method is disallowed by the method filtering, the JWT token will not be checked for this method. | ||
::: | ||
|
||
## How to filter RPC methods | ||
|
||
You need to run `forest` with the `--rpc-filter-list <PATH-TO-FILTER-LIST>` argument. If the filter list is not provided, all methods are allowed by default. | ||
|
||
### Example | ||
|
||
In this example, will disallow the `Filecoin.ChainExport` method which is used to export the chain to a file. This method should not be available to the clients due to its impact (compute, disk space, etc.) on the node. | ||
|
||
1. Create a filter list file, for example, `filter-list.txt`: | ||
|
||
```plaintext | ||
# Disabling the snapshot exporting | ||
!Filecoin.ChainExport | ||
``` | ||
|
||
2. Run `forest` with the `--rpc-filter-list` argument: | ||
|
||
```shell | ||
forest --chain calibnet --encrypt-keystore false --rpc-filter-list filter-list.txt | ||
``` | ||
|
||
3. Try to export the snapshot using the `forest-cli`: | ||
|
||
```shell | ||
forest-cli snapshot-export | ||
``` | ||
|
||
You should see the following error: | ||
|
||
```console | ||
Getting ready to export... | ||
Error: ErrorObject { code: ServerError(403), message: "Forbidden", data: None } | ||
|
||
Caused by: | ||
ErrorObject { code: ServerError(403), message: "Forbidden", data: None } | ||
``` | ||
|
||
## Filter list format | ||
|
||
The filter list is a text file where each line represents a method that should be allowed or disallowed. The format is as follows: | ||
|
||
- `!` at the beginning of the line means that the method is disallowed. | ||
- `#` at the beginning of the line is a comment and is ignored. | ||
- no prefix means that all the methods containing this name are allowed. | ||
|
||
If there is a single allowed method (no prefix), all non-matching methods are disallowed by default. | ||
|
||
:::warning | ||
Some methods have aliases, so you need to filter all of them. This is most prominent in the `Filecoin.Eth.*` namespace. They are implemented for compatibility with Lotus, see [here](https://github.com/filecoin-project/lotus/blob/a9718c841e1fced8afc6e9fee2db2a2b565acc42/api/eth_aliases.go). | ||
::: | ||
|
||
## Example filter lists | ||
|
||
Allow only the `Filecoin.StateCall` method. All other methods are disallowed: | ||
|
||
```plaintext | ||
Filecoin.StateCall | ||
``` | ||
|
||
Disallow the `Filecoin.ChainExport` method. All other methods are allowed: | ||
|
||
```plaintext | ||
!Filecoin.ChainExport | ||
``` | ||
|
||
Disallow the `Filecoin.EthGasPrice`, `Filecoin.EthEstimateGas`, and their aliases. All other methods are allowed: | ||
|
||
```plaintext | ||
!Filecoin.EthGasPrice | ||
!eth_gasPrice | ||
!Filecoin.EthEstimateGas | ||
!eth_estimateGas | ||
``` | ||
|
||
Allow all the methods in the `Filecoin.Chain` namespace. Disallow the `Filecoin.ChainExport` method. This will allow methods such as `Filecoin.ChainGetTipSet` and `Filecoin.ChainGetBlock` but disallow the `Filecoin.ChainExport` method: | ||
|
||
```plaintext | ||
Filecoin.Chain | ||
!Filecoin.ChainExport | ||
``` | ||
|
||
## Public RPC node recommendations | ||
|
||
If you are running a public RPC node, it is recommended to filter certain methods (even those not requiring a JWT token) to reduce the load on the node and to improve security. Here is a list of methods that you might want to consider filtering: | ||
|
||
```plaintext | ||
# Creates a snapshot of the chain and writes it to a file. Very resource-intensive. | ||
!Filecoin.ChainExport | ||
# Potentially resource-intensive. | ||
!Filecoin.EthCall | ||
!eth_call | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,72 @@ | ||
#!/bin/bash | ||
set -euxo pipefail | ||
|
||
# This script tests RPC on a stateless node. This is done to avoid downloading the snapshot for this test and speed up the CI. | ||
|
||
source "$(dirname "$0")/harness.sh" | ||
|
||
# Run a stateless node with a filter list as an argument. | ||
function forest_run_node_stateless_detached_with_filter_list { | ||
pkill -9 forest || true | ||
local filter_list=$1 | ||
|
||
$FOREST_PATH --detach --chain calibnet --encrypt-keystore false --log-dir "$LOG_DIRECTORY" --save-token ./admin_token --skip-load-actors --stateless --rpc-filter-list "$filter_list" | ||
|
||
ADMIN_TOKEN=$(cat admin_token) | ||
FULLNODE_API_INFO="$ADMIN_TOKEN:/ip4/127.0.0.1/tcp/2345/http" | ||
|
||
export ADMIN_TOKEN | ||
export FULLNODE_API_INFO | ||
} | ||
|
||
# Tests the RPC method `Filecoin.ChainHead` and checks if the status code matches the expected code. | ||
function test_rpc { | ||
local expected_code=$1 | ||
|
||
# Test the RPC, get status code | ||
status_code=$(curl --silent -X POST -H "Content-Type: application/json" \ | ||
--data '{"jsonrpc":"2.0","id":2,"method":"Filecoin.ChainHead","params": [ ] }' \ | ||
"http://127.0.0.1:2345/rpc/v1" | jq '.error.code') | ||
|
||
# check if the expected code is returned | ||
if [ "$status_code" != "$expected_code" ]; then | ||
echo "Expected status code $expected_code, got $status_code" | ||
exit 1 | ||
fi | ||
} | ||
|
||
# No filter list - all RPCs are allowed. This is the default behavior. | ||
|
||
cat <<- EOF > "$TMP_DIR"/filter-list | ||
# Cthulhu fhtagn | ||
EOF | ||
|
||
forest_run_node_stateless_detached_with_filter_list "$TMP_DIR/filter-list" | ||
test_rpc null # null means there is no error | ||
|
||
# Filter list with the `ChainHead` RPC disallowed. Should return 403. | ||
|
||
cat <<- EOF > "$TMP_DIR"/filter-list | ||
!Filecoin.ChainHead | ||
EOF | ||
|
||
forest_run_node_stateless_detached_with_filter_list "$TMP_DIR/filter-list" | ||
test_rpc 403 | ||
|
||
# Filter list with a single other RPC allowed. `ChainHead` should be disallowed and return 403. | ||
# Note - this method is required for the test harness. | ||
cat <<- EOF > "$TMP_DIR"/filter-list | ||
Filecoin.Shutdown | ||
EOF | ||
|
||
forest_run_node_stateless_detached_with_filter_list "$TMP_DIR/filter-list" | ||
test_rpc 403 | ||
|
||
# Filter list with a single other RPC allowed, along with `ChainHead`. Should succeed. | ||
cat <<- EOF > "$TMP_DIR"/filter-list | ||
Filecoin.Shutdown | ||
Filecoin.ChainHead | ||
EOF | ||
|
||
forest_run_node_stateless_detached_with_filter_list "$TMP_DIR/filter-list" | ||
test_rpc null # null means there is no error |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,71 @@ | ||
// Copyright 2019-2025 ChainSafe Systems | ||
// SPDX-License-Identifier: Apache-2.0, MIT | ||
|
||
use std::sync::Arc; | ||
|
||
use futures::future::BoxFuture; | ||
use futures::FutureExt; | ||
use jsonrpsee::server::middleware::rpc::RpcServiceT; | ||
use jsonrpsee::types::ErrorObject; | ||
use jsonrpsee::MethodResponse; | ||
use tower::Layer; | ||
|
||
use super::FilterList; | ||
|
||
/// JSON-RPC middleware layer for filtering RPC methods based on their name. | ||
#[derive(Clone, Default)] | ||
pub(super) struct FilterLayer { | ||
filter_list: Arc<FilterList>, | ||
} | ||
|
||
impl FilterLayer { | ||
pub fn new(filter_list: FilterList) -> Self { | ||
Self { | ||
filter_list: Arc::new(filter_list), | ||
} | ||
} | ||
} | ||
|
||
impl<S> Layer<S> for FilterLayer { | ||
type Service = Filtering<S>; | ||
|
||
fn layer(&self, service: S) -> Self::Service { | ||
Filtering { | ||
service, | ||
filter_list: self.filter_list.clone(), | ||
} | ||
} | ||
} | ||
|
||
#[derive(Clone)] | ||
pub(super) struct Filtering<S> { | ||
service: S, | ||
filter_list: Arc<FilterList>, | ||
} | ||
|
||
impl<'a, S> RpcServiceT<'a> for Filtering<S> | ||
where | ||
S: RpcServiceT<'a> + Send + Sync + Clone + 'static, | ||
{ | ||
type Future = BoxFuture<'a, MethodResponse>; | ||
|
||
fn call(&self, req: jsonrpsee::types::Request<'a>) -> Self::Future { | ||
let service = self.service.clone(); | ||
let authorized = self.filter_list.authorize(req.method_name()); | ||
async move { | ||
if authorized { | ||
service.call(req).await | ||
} else { | ||
MethodResponse::error( | ||
req.id(), | ||
ErrorObject::borrowed( | ||
http::StatusCode::FORBIDDEN.as_u16() as _, | ||
"Forbidden", | ||
None, | ||
), | ||
) | ||
} | ||
} | ||
.boxed() | ||
} | ||
} |
Oops, something went wrong.