Move datadir and make it generic.

This commit is contained in:
Adam Ierymenko 2022-09-28 12:04:55 -04:00
parent 433c6058d2
commit a6f09dbec4
No known key found for this signature in database
GPG key ID: C8877CF2D7A5D7F3
9 changed files with 108 additions and 49 deletions

View file

@ -17,3 +17,7 @@ parking_lot = { version = "^0", features = [], default-features = false }
serde = { version = "^1", features = ["derive"], default-features = false }
serde_json = { version = "^1", features = ["std"], default-features = false }
clap = { version = "^3", features = ["std", "suggestions"], default-features = false }
[target."cfg(not(windows))".dependencies]
signal-hook = "^0"
libc = "^0"

View file

@ -1,3 +1,45 @@
// (c) 2020-2022 ZeroTier, Inc. -- currently propritery pending actual release and licensing. See LICENSE.md.
fn main() {}
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc;
use std::time::Duration;
use zerotier_utils::exitcode;
use zerotier_vl1_service::VL1Service;
fn main() {
std::process::exit(
if let Ok(_tokio_runtime) = zerotier_utils::tokio::runtime::Builder::new_multi_thread().enable_all().build() {
let test_inner = Arc::new(zerotier_network_hypervisor::vl1::DummyInnerProtocol::default());
let test_path_filter = Arc::new(zerotier_network_hypervisor::vl1::DummyPathFilter::default());
let datadir = open_datadir(&flags);
let svc = VL1Service::new(datadir, test_inner, test_path_filter, zerotier_vl1_service::VL1Settings::default());
if svc.is_ok() {
let svc = svc.unwrap();
svc.node().init_default_roots();
// Wait for kill signal on Unix-like platforms.
#[cfg(unix)]
{
let term = Arc::new(AtomicBool::new(false));
let _ = signal_hook::flag::register(libc::SIGINT, term.clone());
let _ = signal_hook::flag::register(libc::SIGTERM, term.clone());
let _ = signal_hook::flag::register(libc::SIGQUIT, term.clone());
while !term.load(Ordering::Relaxed) {
std::thread::sleep(Duration::from_secs(1));
}
}
println!("Terminate signal received, shutting down...");
exitcode::OK
} else {
eprintln!("FATAL: error launching service: {}", svc.err().unwrap().to_string());
exitcode::ERR_IOERR
}
} else {
eprintln!("FATAL: error launching service: can't start async runtime");
exitcode::ERR_IOERR
},
);
}

View file

@ -2,7 +2,6 @@
pub mod cli;
pub mod cmdline_help;
pub mod datadir;
pub mod localconfig;
pub mod utils;
pub mod vnic;
@ -18,9 +17,10 @@ use clap::{Arg, ArgMatches, Command};
use zerotier_network_hypervisor::{VERSION_MAJOR, VERSION_MINOR, VERSION_REVISION};
use zerotier_utils::exitcode;
use zerotier_vl1_service::datadir::DataDir;
use zerotier_vl1_service::VL1Service;
use crate::datadir::DataDir;
use crate::localconfig::Config;
pub fn print_help() {
let h = crate::cmdline_help::make_cmdline_help();
@ -44,7 +44,7 @@ pub struct Flags {
pub auth_token_override: Option<String>,
}
fn open_datadir(flags: &Flags) -> Arc<DataDir> {
fn open_datadir(flags: &Flags) -> Arc<DataDir<Config>> {
let datadir = DataDir::open(flags.base_path.as_str());
if datadir.is_ok() {
return Arc::new(datadir.unwrap());

View file

@ -1,42 +1,10 @@
// (c) 2020-2022 ZeroTier, Inc. -- currently propritery pending actual release and licensing. See LICENSE.md.
use std::fs::File;
use std::io::Read;
use std::path::Path;
use std::str::FromStr;
use zerotier_network_hypervisor::vl1::Identity;
/// Default sanity limit parameter for read_limit() used throughout the service.
pub const DEFAULT_FILE_IO_READ_LIMIT: usize = 1048576;
/// Convenience function to read up to limit bytes from a file.
///
/// If the file is larger than limit, the excess is not read.
pub fn read_limit<P: AsRef<Path>>(path: P, limit: usize) -> std::io::Result<Vec<u8>> {
let mut f = File::open(path)?;
let bytes = f.metadata()?.len().min(limit as u64) as usize;
let mut v: Vec<u8> = Vec::with_capacity(bytes);
v.resize(bytes, 0);
f.read_exact(v.as_mut_slice())?;
Ok(v)
}
/// Set permissions on a file or directory to be most restrictive (visible only to the service's user).
#[cfg(unix)]
pub fn fs_restrict_permissions<P: AsRef<Path>>(path: P) -> bool {
unsafe {
let c_path = std::ffi::CString::new(path.as_ref().to_str().unwrap()).unwrap();
libc::chmod(
c_path.as_ptr(),
if path.as_ref().is_dir() {
0o700
} else {
0o600
},
) == 0
}
}
use zerotier_utils::io::read_limit;
/// Returns true if the string starts with [yY1tT] or false for [nN0fF].
pub fn parse_bool(v: &str) -> Result<bool, String> {

View file

@ -14,3 +14,9 @@ parking_lot = { version = "^0", features = [], default-features = false }
serde = { version = "^1", features = ["derive"], default-features = false }
serde_json = { version = "^1", features = ["std"], default-features = false }
tokio = { version = "^1", default-features = false, features = ["fs", "io-util", "io-std", "net", "parking_lot", "process", "rt", "rt-multi-thread", "signal", "sync", "time"], optional = true }
[target."cfg(windows)".dependencies]
winapi = { version = "^0", features = ["handleapi", "ws2ipdef", "ws2tcpip"] }
[target."cfg(not(windows))".dependencies]
libc = "^0"

36
utils/src/io.rs Normal file
View file

@ -0,0 +1,36 @@
// (c) 2020-2022 ZeroTier, Inc. -- currently propritery pending actual release and licensing. See LICENSE.md.
use std::fs::File;
use std::io::Read;
use std::path::Path;
/// Default sanity limit parameter for read_limit() used throughout the service.
pub const DEFAULT_FILE_IO_READ_LIMIT: usize = 1048576;
/// Convenience function to read up to limit bytes from a file.
///
/// If the file is larger than limit, the excess is not read.
pub fn read_limit<P: AsRef<Path>>(path: P, limit: usize) -> std::io::Result<Vec<u8>> {
let mut f = File::open(path)?;
let bytes = f.metadata()?.len().min(limit as u64) as usize;
let mut v: Vec<u8> = Vec::with_capacity(bytes);
v.resize(bytes, 0);
f.read_exact(v.as_mut_slice())?;
Ok(v)
}
/// Set permissions on a file or directory to be most restrictive (visible only to the service's user).
#[cfg(unix)]
pub fn fs_restrict_permissions<P: AsRef<Path>>(path: P) -> bool {
unsafe {
let c_path = std::ffi::CString::new(path.as_ref().to_str().unwrap()).unwrap();
libc::chmod(
c_path.as_ptr(),
if path.as_ref().is_dir() {
0o700
} else {
0o600
},
) == 0
}
}

View file

@ -10,6 +10,7 @@ pub mod exitcode;
pub mod gate;
pub mod gatherarray;
pub mod hex;
pub mod io;
pub mod json;
pub mod marshalable;
pub mod memory;

View file

@ -1,32 +1,33 @@
// (c) 2020-2022 ZeroTier, Inc. -- currently propritery pending actual release and licensing. See LICENSE.md.
use crate::localconfig::Config;
use crate::utils::{read_limit, DEFAULT_FILE_IO_READ_LIMIT};
use std::path::{Path, PathBuf};
use std::str::FromStr;
use std::sync::Arc;
use parking_lot::{Mutex, RwLock};
use serde::de::DeserializeOwned;
use serde::Serialize;
use zerotier_crypto::random::next_u32_secure;
use zerotier_network_hypervisor::vl1::{Identity, NodeStorage};
use zerotier_utils::io::{fs_restrict_permissions, read_limit, DEFAULT_FILE_IO_READ_LIMIT};
use zerotier_utils::json::to_json_pretty;
pub const AUTH_TOKEN_FILENAME: &'static str = "authtoken.secret";
pub const IDENTITY_PUBLIC_FILENAME: &'static str = "identity.public";
pub const IDENTITY_SECRET_FILENAME: &'static str = "identity.secret";
pub const CONFIG_FILENAME: &'static str = "local.conf";
const AUTH_TOKEN_DEFAULT_LENGTH: usize = 48;
const AUTH_TOKEN_POSSIBLE_CHARS: &'static str = "0123456789abcdefghijklmnopqrstuvwxyz";
const AUTH_TOKEN_FILENAME: &'static str = "authtoken.secret";
const IDENTITY_PUBLIC_FILENAME: &'static str = "identity.public";
const IDENTITY_SECRET_FILENAME: &'static str = "identity.secret";
const CONFIG_FILENAME: &'static str = "local.conf";
/// Abstraction around ZeroTier's home data directory.
pub struct DataDir {
pub struct DataDir<Config: PartialEq + Eq + Clone + Send + Sync + Default + Serialize + DeserializeOwned + 'static> {
pub base_path: PathBuf,
config: RwLock<Arc<Config>>,
authtoken: Mutex<String>,
}
impl NodeStorage for DataDir {
impl<Config: PartialEq + Eq + Clone + Send + Sync + Default + Serialize + DeserializeOwned + 'static> NodeStorage for DataDir<Config> {
fn load_node_identity(&self) -> Option<Identity> {
let id_data = read_limit(self.base_path.join(IDENTITY_SECRET_FILENAME), 4096);
if id_data.is_err() {
@ -46,12 +47,12 @@ impl NodeStorage for DataDir {
let secret_path = self.base_path.join(IDENTITY_SECRET_FILENAME);
// TODO: handle errors
let _ = std::fs::write(&secret_path, id_secret_str.as_bytes());
assert!(crate::utils::fs_restrict_permissions(&secret_path));
assert!(fs_restrict_permissions(&secret_path));
let _ = std::fs::write(self.base_path.join(IDENTITY_PUBLIC_FILENAME), id_public_str.as_bytes());
}
}
impl DataDir {
impl<Config: PartialEq + Eq + Clone + Send + Sync + Default + Serialize + DeserializeOwned + 'static> DataDir<Config> {
pub fn open<P: AsRef<Path>>(path: P) -> std::io::Result<Self> {
let base_path = path.as_ref().to_path_buf();
if !base_path.is_dir() {
@ -95,7 +96,7 @@ impl DataDir {
tmp.push(AUTH_TOKEN_POSSIBLE_CHARS.as_bytes()[(next_u32_secure() as usize) % AUTH_TOKEN_POSSIBLE_CHARS.len()] as char);
}
std::fs::write(&authtoken_path, tmp.as_bytes())?;
assert!(crate::utils::fs_restrict_permissions(&authtoken_path));
assert!(fs_restrict_permissions(&authtoken_path));
*self.authtoken.lock() = tmp;
} else {
*self.authtoken.lock() = String::from_utf8_lossy(authtoken_bytes.unwrap().as_slice()).into();

View file

@ -6,6 +6,7 @@ mod settings;
mod vl1service;
pub mod constants;
pub mod datadir;
pub mod sys;
pub use localinterface::LocalInterface;