RPC: Improve snapshot path sanitization
This commit is contained in:
parent
5ae37b9675
commit
013daa8f47
|
@ -4311,6 +4311,7 @@ dependencies = [
|
|||
"jsonrpc-http-server",
|
||||
"jsonrpc-pubsub",
|
||||
"jsonrpc-ws-server",
|
||||
"libc",
|
||||
"log 0.4.11",
|
||||
"lru",
|
||||
"matches",
|
||||
|
@ -4359,6 +4360,7 @@ dependencies = [
|
|||
"solana-version",
|
||||
"solana-vote-program",
|
||||
"spl-token",
|
||||
"symlink",
|
||||
"systemstat",
|
||||
"tempfile",
|
||||
"thiserror",
|
||||
|
|
|
@ -34,6 +34,7 @@ jsonrpc-derive = "17.0.0"
|
|||
jsonrpc-http-server = "17.0.0"
|
||||
jsonrpc-pubsub = "17.0.0"
|
||||
jsonrpc-ws-server = "17.0.0"
|
||||
libc = "0.2.81"
|
||||
log = "0.4.11"
|
||||
lru = "0.6.1"
|
||||
miow = "0.2.2"
|
||||
|
@ -89,6 +90,7 @@ matches = "0.1.6"
|
|||
num_cpus = "1.13.0"
|
||||
reqwest = { version = "0.10.8", default-features = false, features = ["blocking", "rustls-tls", "json"] }
|
||||
serial_test = "0.4.0"
|
||||
symlink = "0.1.0"
|
||||
systemstat = "0.1.5"
|
||||
|
||||
[build-dependencies]
|
||||
|
|
|
@ -65,7 +65,7 @@ impl RpcRequestMiddleware {
|
|||
Self {
|
||||
ledger_path,
|
||||
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(),
|
||||
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 {
|
||||
let stem = path.split_at(1).1; // Drop leading '/' from path
|
||||
let filename = {
|
||||
|
@ -137,8 +157,7 @@ impl RpcRequestMiddleware {
|
|||
RequestMiddlewareAction::Respond {
|
||||
should_validate_hosts: true,
|
||||
response: Box::pin(async {
|
||||
// Stuck on tokio 0.2 until the jsonrpc crates upgrade
|
||||
match tokio_02::fs::File::open(filename).await {
|
||||
match Self::open_no_follow(filename).await {
|
||||
Err(_) => Ok(Self::internal_server_error()),
|
||||
Ok(file) => {
|
||||
let stream =
|
||||
|
@ -449,6 +468,7 @@ mod tests {
|
|||
};
|
||||
use solana_runtime::{bank::Bank, bank_forks::ArchiveFormat, snapshot_utils::SnapshotVersion};
|
||||
use solana_sdk::{genesis_config::ClusterType, signature::Signer};
|
||||
use std::io::Write;
|
||||
use std::net::{IpAddr, Ipv4Addr};
|
||||
|
||||
#[test]
|
||||
|
@ -566,15 +586,78 @@ mod tests {
|
|||
assert!(rrm_with_snapshot_config
|
||||
.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"
|
||||
));
|
||||
|
||||
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("🎣"));
|
||||
}
|
||||
|
||||
#[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]
|
||||
fn test_health_check_with_no_trusted_validators() {
|
||||
let rm = RpcRequestMiddleware::new(
|
||||
|
|
Loading…
Reference in New Issue