0.3.3: Ununsafe binhost (server) and update deps

Signed-off-by: Ivan Bushchik <ivabus@ivabus.dev>
This commit is contained in:
Ivan Bushchik 2024-03-17 18:13:10 +03:00
parent debff608d2
commit 652196c247
No known key found for this signature in database
GPG key ID: 2F16FBF3262E090C
2 changed files with 102 additions and 112 deletions

View file

@ -1,19 +1,20 @@
[package] [package]
name = "binhost" name = "binhost"
version = "0.3.2" version = "0.3.3"
edition = "2021" edition = "2021"
license = "MIT" license = "MIT"
repository = "https://github.com/ivabus/binhost" repository = "https://github.com/ivabus/binhost"
description = "HTTP server to easily serve files" description = "HTTP server to easily serve files"
[dependencies] [dependencies]
clap = { version = "4.4.18", features = ["derive"] } clap = { version = "4.5.3", features = ["derive"] }
rocket = "0.5.0" rocket = "0.5.0"
serde = { version = "1.0", features = ["derive"] } serde = { version = "1.0", features = ["derive"] }
sha2 = { version = "0.10" } sha2 = { version = "0.10" }
ed25519-compact = { version = "2.0.6", default-features = false, features = [ ed25519-compact = { version = "2.1.1", default-features = false, features = [
"random", "random",
"self-verify", "self-verify",
"std", "std",
] } ] }
once_cell = "1.19"

View file

@ -1,5 +1,7 @@
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
#![forbid(unsafe_code)]
#[macro_use] #[macro_use]
extern crate rocket; extern crate rocket;
@ -8,10 +10,12 @@ use std::time::Instant;
use clap::Parser; use clap::Parser;
use ed25519_compact::{KeyPair, Noise}; use ed25519_compact::{KeyPair, Noise};
use once_cell::sync::Lazy;
use rocket::figment::Figment; use rocket::figment::Figment;
use rocket::fs::{FileServer, NamedFile}; use rocket::fs::{FileServer, NamedFile};
use rocket::http::Status; use rocket::http::Status;
use rocket::response::content::RawText; use rocket::response::content::RawText;
use rocket::tokio::sync::RwLock;
use sha2::digest::FixedOutput; use sha2::digest::FixedOutput;
use sha2::Digest; use sha2::Digest;
@ -19,17 +23,62 @@ use structs::*;
mod structs; mod structs;
static mut BINS: Option<(HashMap<String, Bin>, Instant)> = None; static BINS: Lazy<RwLock<(HashMap<String, Bin>, Instant)>> =
static mut MANIFEST: Option<Vec<u8>> = None; Lazy::new(|| RwLock::new((get_bins(&Args::parse()), Instant::now())));
static mut KEYPAIR: Option<KeyPair> = None; static KEYPAIR: Lazy<KeyPair> = Lazy::new(|| {
println!("Generating keypair");
let kp = KeyPair::generate();
println!(
"Keypair generated. Public key: {}",
kp.pk.iter().map(|x| format!("{:x}", x)).collect::<Vec<String>>().join("")
);
kp
});
static MANIFEST: Lazy<Vec<u8>> = Lazy::new(|| {
let args = Args::parse();
println!("Generating manifest");
let mut manifest: Vec<u8> = Vec::new();
let mut bin_pub_key: Vec<u8> = KEYPAIR.pk.to_vec();
manifest.append(&mut bin_pub_key);
let mut runners = 0;
for element in std::fs::read_dir(args.runners_dir).unwrap() {
let en = element.unwrap();
if en.path().is_file() {
let mut hasher = sha2::Sha256::new();
hasher.update(std::fs::read(en.path()).unwrap().as_slice());
let mut contents = Vec::from(
format!(
"{:x} {}\n",
hasher.finalize_fixed(),
en.path().file_name().unwrap().to_str().unwrap()
)
.as_bytes(),
);
runners += 1;
manifest.append(&mut contents);
}
}
let mut hasher = sha2::Sha256::new();
hasher.update(&manifest);
println!(
"Manifest generated with {} runners and SHA256: {:x}",
runners,
hasher.finalize_fixed()
);
manifest
});
static WEB_SH: &str = include_str!("../web.sh"); static WEB_SH: &str = include_str!("../web.sh");
static HASH_CALCULATION_SH: &str = ""; static HASH_CALCULATION_SH: &str = "";
fn reload_bins(bins: (&mut HashMap<String, Bin>, &mut Instant), args: &Args) { async fn reload_bins(args: &Args) {
if (Instant::now() - *bins.1).as_secs() > args.refresh { let (bins, time) = &mut *BINS.write().await;
*bins.0 = get_bins(args); if (Instant::now() - *time).as_secs() > args.refresh {
*bins.1 = Instant::now(); *bins = get_bins(args);
*time = Instant::now();
} }
} }
@ -71,16 +120,14 @@ fn format_platform_list(bin: &Bin) -> String {
async fn index() -> RawText<String> { async fn index() -> RawText<String> {
let args = Args::parse(); let args = Args::parse();
let mut ret = String::new(); let mut ret = String::new();
unsafe {
if let Some(manifest) = &MANIFEST {
let mut hasher = sha2::Sha256::new(); let mut hasher = sha2::Sha256::new();
hasher.update(manifest); hasher.update(&*MANIFEST);
ret.push_str(&format!("Manifest hashsum: {:x}\n", hasher.finalize_fixed())); ret.push_str(&format!("Manifest hashsum: {:x}\n", hasher.finalize_fixed()));
}
} reload_bins(&args).await;
unsafe { let (bins, _) = &*BINS.read().await;
if let Some((bins, time)) = &mut BINS {
reload_bins((bins, time), &args);
if bins.is_empty() { if bins.is_empty() {
return RawText(String::from("No binaries found")); return RawText(String::from("No binaries found"));
} }
@ -94,27 +141,21 @@ async fn index() -> RawText<String> {
.collect::<Vec<String>>() .collect::<Vec<String>>()
)) ))
} }
}
}
RawText(ret) RawText(ret)
} }
#[get("/runner/manifest")] #[get("/runner/manifest")]
async fn get_manifest() -> Vec<u8> { async fn get_manifest<'a>() -> Vec<u8> {
unsafe { let manifest = &*MANIFEST;
match &MANIFEST { manifest.clone()
Some(man) => man.clone(),
_ => unreachable!(),
}
}
} }
#[get("/<bin>")] #[get("/<bin>")]
async fn get_script(bin: &str) -> ScriptResponse { async fn get_script(bin: &str) -> ScriptResponse {
let args = Args::parse(); let args = Args::parse();
unsafe { reload_bins(&args).await;
if let Some((bins, time)) = &mut BINS { let (bins, _) = &*BINS.read().await;
reload_bins((bins, time), &args); match bins.get(bin) {
return match bins.get(bin) {
None => ScriptResponse::Status(Status::NotFound), None => ScriptResponse::Status(Status::NotFound),
Some(bin) => { Some(bin) => {
let mut script = String::from(WEB_SH); let mut script = String::from(WEB_SH);
@ -125,26 +166,19 @@ async fn get_script(bin: &str) -> ScriptResponse {
.replace("{{EXTERNAL_ADDRESS}}", &args.url); .replace("{{EXTERNAL_ADDRESS}}", &args.url);
ScriptResponse::Text(RawText(script)) ScriptResponse::Text(RawText(script))
} }
};
} }
} }
ScriptResponse::Status(Status::NotFound)
}
#[get("/<bin>/platforms")] #[get("/<bin>/platforms")]
async fn get_platforms(bin: &str) -> ScriptResponse { async fn get_platforms(bin: &str) -> ScriptResponse {
let args = Args::parse(); let args = Args::parse();
unsafe { reload_bins(&args).await;
if let Some((bins, time)) = &mut BINS { let (bins, _) = &*BINS.read().await;
reload_bins((bins, time), &args); match bins.get(bin) {
return match bins.get(bin) {
None => ScriptResponse::Status(Status::NotFound), None => ScriptResponse::Status(Status::NotFound),
Some(bin) => ScriptResponse::Text(RawText(format_platform_list(bin))), Some(bin) => ScriptResponse::Text(RawText(format_platform_list(bin))),
};
} }
} }
ScriptResponse::Status(Status::NotFound)
}
#[get("/bin/<bin>/<platform>/<arch>")] #[get("/bin/<bin>/<platform>/<arch>")]
async fn get_binary(bin: &str, platform: &str, arch: &str) -> BinaryResponse { async fn get_binary(bin: &str, platform: &str, arch: &str) -> BinaryResponse {
@ -178,12 +212,7 @@ async fn get_binary_sign(bin: &str, platform: &str, arch: &str) -> SignResponse
Ok(f) => f, Ok(f) => f,
Err(_) => return SignResponse::Status(Status::BadRequest), Err(_) => return SignResponse::Status(Status::BadRequest),
}; };
let keypair = unsafe { let keypair = &*KEYPAIR;
match &KEYPAIR {
Some(pair) => pair,
_ => unreachable!(),
}
};
SignResponse::Bin(keypair.sk.sign(file.as_slice(), Some(Noise::generate())).as_slice().to_vec()) SignResponse::Bin(keypair.sk.sign(file.as_slice(), Some(Noise::generate())).as_slice().to_vec())
} }
#[launch] #[launch]
@ -194,47 +223,7 @@ async fn rocket() -> _ {
std::process::exit(1); std::process::exit(1);
} }
unsafe { let _ = &*BINS.read().await;
BINS = Some((get_bins(&args), Instant::now()));
println!("Generating keypair");
let kp = KeyPair::generate();
KEYPAIR = Some(kp.clone());
println!(
"Keypair generated. Public key: {}",
&kp.pk.iter().map(|x| format!("{:x}", x)).collect::<Vec<String>>().join("")
);
println!("Generating manifest");
let mut manifest: Vec<u8> = Vec::new();
let mut bin_pub_key: Vec<u8> = kp.pk.to_vec();
manifest.append(&mut bin_pub_key);
let mut runners = 0;
for element in std::fs::read_dir(args.runners_dir).unwrap() {
let en = element.unwrap();
if en.path().is_file() {
let mut hasher = sha2::Sha256::new();
hasher.update(std::fs::read(en.path()).unwrap().as_slice());
let mut contents = Vec::from(
format!(
"{:x} {}\n",
hasher.finalize_fixed(),
en.path().file_name().unwrap().to_str().unwrap()
)
.as_bytes(),
);
runners += 1;
manifest.append(&mut contents);
}
}
let mut hasher = sha2::Sha256::new();
hasher.update(&manifest);
println!(
"Manifest generated with {} runners and SHA256: {:x}",
runners,
hasher.finalize_fixed()
);
MANIFEST = Some(manifest)
};
let figment = Figment::from(rocket::Config::default()) let figment = Figment::from(rocket::Config::default())
.merge(("ident", "Binhost")) .merge(("ident", "Binhost"))