RPC: Improve snapshot path sanitization
This commit is contained in:
committed by
Michael Vines
parent
5ae37b9675
commit
013daa8f47
2
Cargo.lock
generated
2
Cargo.lock
generated
@ -4311,6 +4311,7 @@ dependencies = [
|
|||||||
"jsonrpc-http-server",
|
"jsonrpc-http-server",
|
||||||
"jsonrpc-pubsub",
|
"jsonrpc-pubsub",
|
||||||
"jsonrpc-ws-server",
|
"jsonrpc-ws-server",
|
||||||
|
"libc",
|
||||||
"log 0.4.11",
|
"log 0.4.11",
|
||||||
"lru",
|
"lru",
|
||||||
"matches",
|
"matches",
|
||||||
@ -4359,6 +4360,7 @@ dependencies = [
|
|||||||
"solana-version",
|
"solana-version",
|
||||||
"solana-vote-program",
|
"solana-vote-program",
|
||||||
"spl-token",
|
"spl-token",
|
||||||
|
"symlink",
|
||||||
"systemstat",
|
"systemstat",
|
||||||
"tempfile",
|
"tempfile",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
|
@ -34,6 +34,7 @@ jsonrpc-derive = "17.0.0"
|
|||||||
jsonrpc-http-server = "17.0.0"
|
jsonrpc-http-server = "17.0.0"
|
||||||
jsonrpc-pubsub = "17.0.0"
|
jsonrpc-pubsub = "17.0.0"
|
||||||
jsonrpc-ws-server = "17.0.0"
|
jsonrpc-ws-server = "17.0.0"
|
||||||
|
libc = "0.2.81"
|
||||||
log = "0.4.11"
|
log = "0.4.11"
|
||||||
lru = "0.6.1"
|
lru = "0.6.1"
|
||||||
miow = "0.2.2"
|
miow = "0.2.2"
|
||||||
@ -89,6 +90,7 @@ matches = "0.1.6"
|
|||||||
num_cpus = "1.13.0"
|
num_cpus = "1.13.0"
|
||||||
reqwest = { version = "0.10.8", default-features = false, features = ["blocking", "rustls-tls", "json"] }
|
reqwest = { version = "0.10.8", default-features = false, features = ["blocking", "rustls-tls", "json"] }
|
||||||
serial_test = "0.4.0"
|
serial_test = "0.4.0"
|
||||||
|
symlink = "0.1.0"
|
||||||
systemstat = "0.1.5"
|
systemstat = "0.1.5"
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
|
@ -65,7 +65,7 @@ impl RpcRequestMiddleware {
|
|||||||
Self {
|
Self {
|
||||||
ledger_path,
|
ledger_path,
|
||||||
snapshot_archive_path_regex: Regex::new(
|
snapshot_archive_path_regex: Regex::new(
|
||||||
r"/snapshot-\d+-[[:alnum:]]+\.(tar|tar\.bz2|tar\.zst|tar\.gz)$",
|
r"^/snapshot-\d+-[[:alnum:]]+\.(tar|tar\.bz2|tar\.zst|tar\.gz)$",
|
||||||
)
|
)
|
||||||
.unwrap(),
|
.unwrap(),
|
||||||
snapshot_config,
|
snapshot_config,
|
||||||
@ -110,6 +110,26 @@ impl RpcRequestMiddleware {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(unix)]
|
||||||
|
async fn open_no_follow(path: impl AsRef<Path>) -> std::io::Result<tokio_02::fs::File> {
|
||||||
|
// Stuck on tokio 0.2 until the jsonrpc crates upgrade
|
||||||
|
use tokio_02::fs::os::unix::OpenOptionsExt;
|
||||||
|
tokio_02::fs::OpenOptions::new()
|
||||||
|
.read(true)
|
||||||
|
.write(false)
|
||||||
|
.create(false)
|
||||||
|
.custom_flags(libc::O_NOFOLLOW)
|
||||||
|
.open(path)
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(unix))]
|
||||||
|
async fn open_no_follow(path: impl AsRef<Path>) -> std::io::Result<tokio_02::fs::File> {
|
||||||
|
// TODO: Is there any way to achieve the same on Windows?
|
||||||
|
// Stuck on tokio 0.2 until the jsonrpc crates upgrade
|
||||||
|
tokio_02::fs::File::open(path).await
|
||||||
|
}
|
||||||
|
|
||||||
fn process_file_get(&self, path: &str) -> RequestMiddlewareAction {
|
fn process_file_get(&self, path: &str) -> RequestMiddlewareAction {
|
||||||
let stem = path.split_at(1).1; // Drop leading '/' from path
|
let stem = path.split_at(1).1; // Drop leading '/' from path
|
||||||
let filename = {
|
let filename = {
|
||||||
@ -137,8 +157,7 @@ impl RpcRequestMiddleware {
|
|||||||
RequestMiddlewareAction::Respond {
|
RequestMiddlewareAction::Respond {
|
||||||
should_validate_hosts: true,
|
should_validate_hosts: true,
|
||||||
response: Box::pin(async {
|
response: Box::pin(async {
|
||||||
// Stuck on tokio 0.2 until the jsonrpc crates upgrade
|
match Self::open_no_follow(filename).await {
|
||||||
match tokio_02::fs::File::open(filename).await {
|
|
||||||
Err(_) => Ok(Self::internal_server_error()),
|
Err(_) => Ok(Self::internal_server_error()),
|
||||||
Ok(file) => {
|
Ok(file) => {
|
||||||
let stream =
|
let stream =
|
||||||
@ -449,6 +468,7 @@ mod tests {
|
|||||||
};
|
};
|
||||||
use solana_runtime::{bank::Bank, bank_forks::ArchiveFormat, snapshot_utils::SnapshotVersion};
|
use solana_runtime::{bank::Bank, bank_forks::ArchiveFormat, snapshot_utils::SnapshotVersion};
|
||||||
use solana_sdk::{genesis_config::ClusterType, signature::Signer};
|
use solana_sdk::{genesis_config::ClusterType, signature::Signer};
|
||||||
|
use std::io::Write;
|
||||||
use std::net::{IpAddr, Ipv4Addr};
|
use std::net::{IpAddr, Ipv4Addr};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -566,15 +586,78 @@ mod tests {
|
|||||||
assert!(rrm_with_snapshot_config
|
assert!(rrm_with_snapshot_config
|
||||||
.is_file_get_path("/snapshot-100-AvFf9oS8A8U78HdjT9YG2sTTThLHJZmhaMn2g8vkWYnr.tar"));
|
.is_file_get_path("/snapshot-100-AvFf9oS8A8U78HdjT9YG2sTTThLHJZmhaMn2g8vkWYnr.tar"));
|
||||||
|
|
||||||
assert!(!rrm.is_file_get_path(
|
assert!(!rrm_with_snapshot_config.is_file_get_path(
|
||||||
"/snapshot-notaslotnumber-AvFf9oS8A8U78HdjT9YG2sTTThLHJZmhaMn2g8vkWYnr.tar.bz2"
|
"/snapshot-notaslotnumber-AvFf9oS8A8U78HdjT9YG2sTTThLHJZmhaMn2g8vkWYnr.tar.bz2"
|
||||||
));
|
));
|
||||||
|
|
||||||
|
assert!(!rrm_with_snapshot_config.is_file_get_path("../../../test/snapshot-123-xxx.tar"));
|
||||||
|
|
||||||
assert!(!rrm.is_file_get_path("/"));
|
assert!(!rrm.is_file_get_path("/"));
|
||||||
assert!(!rrm.is_file_get_path(".."));
|
assert!(!rrm.is_file_get_path(".."));
|
||||||
assert!(!rrm.is_file_get_path("🎣"));
|
assert!(!rrm.is_file_get_path("🎣"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_process_file_get() {
|
||||||
|
let mut runtime = tokio_02::runtime::Runtime::new().unwrap();
|
||||||
|
|
||||||
|
let ledger_path = get_tmp_ledger_path!();
|
||||||
|
std::fs::create_dir(&ledger_path).unwrap();
|
||||||
|
|
||||||
|
let genesis_path = ledger_path.join("genesis.tar.bz2");
|
||||||
|
let rrm = RpcRequestMiddleware::new(
|
||||||
|
ledger_path.clone(),
|
||||||
|
None,
|
||||||
|
create_bank_forks(),
|
||||||
|
RpcHealth::stub(),
|
||||||
|
);
|
||||||
|
|
||||||
|
// File does not exist => request should fail.
|
||||||
|
let action = rrm.process_file_get("/genesis.tar.bz2");
|
||||||
|
if let RequestMiddlewareAction::Respond { response, .. } = action {
|
||||||
|
let response = runtime.block_on(response);
|
||||||
|
let response = response.unwrap();
|
||||||
|
assert_ne!(response.status(), 200);
|
||||||
|
} else {
|
||||||
|
panic!("Unexpected RequestMiddlewareAction variant");
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
let mut file = std::fs::File::create(&genesis_path).unwrap();
|
||||||
|
file.write_all(b"should be ok").unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Normal file exist => request should succeed.
|
||||||
|
let action = rrm.process_file_get("/genesis.tar.bz2");
|
||||||
|
if let RequestMiddlewareAction::Respond { response, .. } = action {
|
||||||
|
let response = runtime.block_on(response);
|
||||||
|
let response = response.unwrap();
|
||||||
|
assert_eq!(response.status(), 200);
|
||||||
|
} else {
|
||||||
|
panic!("Unexpected RequestMiddlewareAction variant");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(unix)]
|
||||||
|
{
|
||||||
|
std::fs::remove_file(&genesis_path).unwrap();
|
||||||
|
{
|
||||||
|
let mut file = std::fs::File::create(ledger_path.join("wrong")).unwrap();
|
||||||
|
file.write_all(b"wrong file").unwrap();
|
||||||
|
}
|
||||||
|
symlink::symlink_file("wrong", &genesis_path).unwrap();
|
||||||
|
|
||||||
|
// File is a symbolic link => request should fail.
|
||||||
|
let action = rrm.process_file_get("/genesis.tar.bz2");
|
||||||
|
if let RequestMiddlewareAction::Respond { response, .. } = action {
|
||||||
|
let response = runtime.block_on(response);
|
||||||
|
let response = response.unwrap();
|
||||||
|
assert_ne!(response.status(), 200);
|
||||||
|
} else {
|
||||||
|
panic!("Unexpected RequestMiddlewareAction variant");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_health_check_with_no_trusted_validators() {
|
fn test_health_check_with_no_trusted_validators() {
|
||||||
let rm = RpcRequestMiddleware::new(
|
let rm = RpcRequestMiddleware::new(
|
||||||
|
Reference in New Issue
Block a user