cli to run nebula and nebula-cert, and remove cache dirs

This commit is contained in:
c0repwn3r 2023-03-20 13:36:15 -04:00
parent a4fda57da8
commit 2d7626317d
Signed by: core
GPG Key ID: FDBF740DADDCEECF
6 changed files with 300 additions and 4 deletions

62
Cargo.lock generated
View File

@ -295,6 +295,17 @@ version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b"
[[package]]
name = "colored"
version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b3616f750b84d8f0de8a58bda93e08e2a81ad3f523089b05f1dffecab48c6cbd"
dependencies = [
"atty",
"lazy_static",
"winapi",
]
[[package]]
name = "const-oid"
version = "0.9.1"
@ -554,7 +565,16 @@ version = "4.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ca3aa72a6f96ea37bbc5aa912f6788242832f75369bdfdadcb0e38423f100059"
dependencies = [
"dirs-sys",
"dirs-sys 0.3.7",
]
[[package]]
name = "dirs"
version = "5.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dece029acd3353e3a58ac2e3eb3c8d6c35827a892edc6cc4138ef9c33df46ecd"
dependencies = [
"dirs-sys 0.4.0",
]
[[package]]
@ -568,6 +588,17 @@ dependencies = [
"winapi",
]
[[package]]
name = "dirs-sys"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "04414300db88f70d74c5ff54e50f9e1d1737d9a5b90f53fcf2e95ca2a9ab554b"
dependencies = [
"libc",
"redox_users",
"windows-sys 0.45.0",
]
[[package]]
name = "dotenvy"
version = "0.15.6"
@ -1393,6 +1424,15 @@ dependencies = [
"libc",
]
[[package]]
name = "num_threads"
version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2819ce041d2ee131036f4fc9d6ae7ae125a3a40e97ba64d04fe799ad9dabbb44"
dependencies = [
"libc",
]
[[package]]
name = "once_cell"
version = "1.17.0"
@ -2094,6 +2134,19 @@ version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8fe458c98333f9c8152221191a77e2a44e8325d0193484af2e9421a53019e57d"
[[package]]
name = "simple_logger"
version = "4.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e78beb34673091ccf96a8816fce8bfd30d1292c7621ca2bcb5f2ba0fae4f558d"
dependencies = [
"atty",
"colored",
"log",
"time 0.3.17",
"windows-sys 0.42.0",
]
[[package]]
name = "slab"
version = "0.4.7"
@ -2170,7 +2223,7 @@ dependencies = [
"bytes",
"crc",
"crossbeam-queue",
"dirs",
"dirs 4.0.0",
"dotenvy",
"either",
"event-listener",
@ -2349,11 +2402,14 @@ name = "tfclient"
version = "0.1.0"
dependencies = [
"clap",
"dirs 5.0.0",
"flate2",
"hex",
"log",
"reqwest",
"serde",
"sha2",
"simple_logger",
"tar",
"tempfile",
"trifid-pki",
@ -2406,6 +2462,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a561bf4617eebd33bca6434b988f39ed798e527f51a1e797d0ee4f61c0a38376"
dependencies = [
"itoa",
"libc",
"num_threads",
"serde",
"time-core",
"time-macros",

View File

@ -9,6 +9,11 @@ description = "An open-source reimplementation of a Defined Networking-compatibl
[dependencies]
clap = { version = "4.1.10", features = ["derive"] }
trifid-pki = { version = "0.1.5", path = "../trifid-pki" }
dirs = "5.0.0"
log = "0.4.17"
simple_logger = "4.1.0"
sha2 = "0.10.6"
hex = "0.4.3"
[build-dependencies]
serde = { version = "1.0.157", features = ["derive"] }

5
tfclient/src/dirs.rs Normal file
View File

@ -0,0 +1,5 @@
use std::path::PathBuf;
pub fn get_data_dir() -> Option<PathBuf> {
dirs::data_dir().map(|f| f.join("tfclient/"))
}

View File

@ -0,0 +1,104 @@
use std::error::Error;
use std::fs;
use std::fs::File;
use std::io::Write;
use std::os::unix::fs::PermissionsExt;
use std::path::PathBuf;
use std::process::{Child, Command, Output};
use log::debug;
use crate::dirs::get_data_dir;
use crate::util::sha256;
pub fn extract_embedded_nebula() -> Result<PathBuf, Box<dyn Error>> {
let data_dir = get_data_dir().ok_or("Unable to get platform-specific data dir")?;
if !data_dir.exists() {
fs::create_dir_all(&data_dir)?;
debug!("Created data directory {}", data_dir.as_path().display());
}
let bin_dir = data_dir.join("cache/");
let hash_dir = bin_dir.join(format!("{}/", sha256(crate::nebula_bin::NEBULA_BIN)));
if !hash_dir.exists() {
fs::create_dir_all(&hash_dir)?;
debug!("Created directory {}", hash_dir.as_path().display());
}
let executable_postfix = if cfg!(windows) { ".exe" } else { "" };
let executable_name = format!("nebula-{}{}", crate::nebula_bin::NEBULA_VERSION, executable_postfix);
let file_path = hash_dir.join(executable_name);
if file_path.exists() {
// Already extracted
return Ok(file_path);
}
let mut file = File::create(&file_path)?;
file.write_all(crate::nebula_bin::NEBULA_BIN)?;
debug!("Extracted nebula to {}", file_path.as_path().display());
Ok(file_path)
}
pub fn extract_embedded_nebula_cert() -> Result<PathBuf, Box<dyn Error>> {
let data_dir = get_data_dir().ok_or("Unable to get platform-specific data dir")?;
if !data_dir.exists() {
fs::create_dir_all(&data_dir)?;
debug!("Created data directory {}", data_dir.as_path().display());
}
let bin_dir = data_dir.join("cache/");
let hash_dir = bin_dir.join(format!("{}/", sha256(crate::nebula_cert_bin::NEBULA_CERT_BIN)));
if !hash_dir.exists() {
fs::create_dir_all(&hash_dir)?;
debug!("Created directory {}", hash_dir.as_path().display());
}
let executable_postfix = if cfg!(windows) { ".exe" } else { "" };
let executable_name = format!("nebula-cert-{}{}", crate::nebula_cert_bin::NEBULA_CERT_VERSION, executable_postfix);
let file_path = hash_dir.join(executable_name);
if file_path.exists() {
// Already extracted
return Ok(file_path);
}
let mut file = File::create(&file_path)?;
file.write_all(crate::nebula_cert_bin::NEBULA_CERT_BIN)?;
debug!("Extracted nebula-cert to {}", file_path.as_path().display());
Ok(file_path)
}
#[cfg(unix)]
pub fn _setup_permissions(path: &PathBuf) -> Result<(), Box<dyn Error>> {
let meta = path.metadata()?;
let mut perms = meta.permissions();
perms.set_mode(0o0755);
debug!("Setting permissions on {} to 755", path.as_path().display());
fs::set_permissions(path, perms)?;
Ok(())
}
#[cfg(windows)]
pub fn _setup_permissions() -> Result<(), Box<dyn Error>> {
Ok(())
}
pub fn run_embedded_nebula(args: &[String]) -> Result<Child, Box<dyn Error>> {
let path = extract_embedded_nebula()?;
debug!("Running {} with args {:?}", path.as_path().display(), args);
_setup_permissions(&path)?;
Ok(Command::new(path).args(args).spawn()?)
}
pub fn run_embedded_nebula_cert(args: &[String]) -> Result<Child, Box<dyn Error>> {
let path = extract_embedded_nebula_cert()?;
debug!("Running {} with args {:?}", path.as_path().display(), args);
_setup_permissions(&path)?;
Ok(Command::new(path).args(args).spawn()?)
}

View File

@ -14,6 +14,10 @@
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
pub mod embedded_nebula;
pub mod dirs;
pub mod util;
pub mod nebula_bin {
include!(concat!(env!("OUT_DIR"), "/nebula.bin.rs"));
}
@ -21,7 +25,14 @@ pub mod nebula_cert_bin {
include!(concat!(env!("OUT_DIR"), "/nebula_cert.bin.rs"));
}
use clap::{Parser, ArgAction};
use std::error::Error;
use std::fs;
use std::process::Child;
use clap::{Parser, ArgAction, Subcommand};
use log::{error, info};
use simple_logger::SimpleLogger;
use crate::dirs::get_data_dir;
use crate::embedded_nebula::{run_embedded_nebula, run_embedded_nebula_cert};
#[derive(Parser)]
#[command(author = "c0repwn3r", version, about, long_about = None)]
@ -29,17 +40,121 @@ use clap::{Parser, ArgAction};
struct Cli {
#[arg(short = 'v', long = "version", action = ArgAction::SetTrue)]
#[clap(global = true)]
version: bool
version: bool,
#[command(subcommand)]
subcommand: Commands
}
#[derive(Subcommand)]
enum Commands {
/// Run the `nebula` binary. This is useful if you want to do debugging with tfclient's internal nebula.
RunNebula {
/// Arguments to pass to the `nebula` binary
#[clap(trailing_var_arg=true, allow_hyphen_values=true)]
args: Vec<String>
},
/// Run the `nebula-cert` binary. This is useful if you want to mess with certificates. Note: tfclient does not actually use nebula-cert for certificate operations, and instead uses trifid-pki internally
RunNebulaCert {
/// Arguments to pass to the `nebula-cert` binary
#[clap(trailing_var_arg=true, allow_hyphen_values=true)]
args: Vec<String>
},
/// Clear any cached data that tfclient may have added
ClearCache {}
}
fn main() {
SimpleLogger::new().init().unwrap();
let args = Cli::parse();
if args.version {
print_version();
}
match args.subcommand {
Commands::RunNebula { args } => {
match run_embedded_nebula(&args) {
Ok(mut c) => {
match c.wait() {
Ok(stat) => {
match stat.code() {
Some(code) => {
if code != 0 {
error!("Nebula process exited with nonzero status code {}", code);
}
std::process::exit(code);
},
None => {
info!("Nebula process terminated by signal");
std::process::exit(0);
}
}
},
Err(e) => {
error!("Unable to wait for child to exit: {}", e);
std::process::exit(1);
}
}
},
Err(e) => {
error!("Unable to start nebula binary: {}", e);
std::process::exit(1);
}
}
},
Commands::ClearCache { .. } => {
let data_dir = match get_data_dir() {
Some(dir) => dir,
None => {
error!("Unable to get platform-specific data dir");
std::process::exit(1);
}
};
match fs::remove_dir_all(&data_dir) {
Ok(_) => (),
Err(e) => {
error!("Unable to delete data dir: {}", e);
std::process::exit(0);
}
}
info!("Removed data dir {}", data_dir.as_path().display());
info!("Removed all cached data.");
std::process::exit(0);
},
Commands::RunNebulaCert { args } => {
match run_embedded_nebula_cert(&args) {
Ok(mut c) => {
match c.wait() {
Ok(stat) => {
match stat.code() {
Some(code) => {
if code != 0 {
error!("nebula-cert process exited with nonzero status code {}", code);
}
std::process::exit(code);
},
None => {
info!("nebula-cert process terminated by signal");
std::process::exit(0);
}
}
},
Err(e) => {
error!("Unable to wait for child to exit: {}", e);
std::process::exit(1);
}
}
},
Err(e) => {
error!("Unable to start nebula-cert binary: {}", e);
std::process::exit(1);
}
}
}
}
}
fn print_version() {

9
tfclient/src/util.rs Normal file
View File

@ -0,0 +1,9 @@
use sha2::Sha256;
use sha2::Digest;
pub fn sha256(bytes: &[u8]) -> String {
let mut hasher = Sha256::new();
hasher.update(bytes);
let digest = hasher.finalize();
hex::encode(digest)
}