-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
working on better configuration management
- Loading branch information
Showing
9 changed files
with
374 additions
and
10 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,7 +1,31 @@ | ||
use std::{ | ||
collections::BTreeMap, | ||
net::SocketAddr, | ||
}; | ||
|
||
use axum::Router; | ||
use serde::{ | ||
Deserialize, | ||
Serialize, | ||
}; | ||
|
||
use super::Context; | ||
|
||
pub(crate) fn router() -> Router<Context> { | ||
todo!(); | ||
} | ||
|
||
#[derive(Debug)] | ||
pub struct Captures { | ||
inner: BTreeMap<Id, ()>, | ||
} | ||
|
||
impl Captures {} | ||
|
||
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)] | ||
#[serde(rename_all = "kebab-case")] | ||
pub enum Id { | ||
SocksProxy { bind_address: SocketAddr }, | ||
HttpProxy { bind_address: SocketAddr }, | ||
PacketCapture { interface: String }, | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,201 @@ | ||
use std::{ | ||
borrow::Cow, | ||
path::{ | ||
Path, | ||
PathBuf, | ||
}, | ||
sync::Arc, | ||
time::Duration, | ||
}; | ||
|
||
use serde::Deserialize; | ||
use tokio::sync::RwLock; | ||
use toml_edit::DocumentMut; | ||
use tracing::Instrument; | ||
|
||
use super::{ | ||
Error, | ||
FileHash, | ||
DEFAULT_CONFIG, | ||
}; | ||
use crate::util::watch::{ | ||
FileWatcher, | ||
WatchModified, | ||
}; | ||
|
||
#[derive(Clone, Debug)] | ||
pub struct ConfigFile { | ||
inner: Arc<RwLock<Inner>>, | ||
} | ||
|
||
pub struct Loader { | ||
inner: Inner, | ||
watch: WatchModified, | ||
hash: FileHash, | ||
} | ||
|
||
#[derive(Debug, Deserialize)] | ||
pub struct Data { | ||
data_dir: Option<PathBuf>, | ||
} | ||
|
||
#[derive(Debug)] | ||
struct Inner { | ||
path: PathBuf, | ||
hash: FileHash, | ||
document: DocumentMut, | ||
data: Data, | ||
} | ||
|
||
impl ConfigFile { | ||
const DEBOUNCE: Duration = Duration::from_secs(1); | ||
|
||
pub fn open(path: impl AsRef<Path>) -> Result<Loader, Error> { | ||
open(path.as_ref()) | ||
} | ||
} | ||
|
||
impl Loader { | ||
pub fn load(self) -> ConfigFile { | ||
let path = self.inner.path.clone(); | ||
let inner = Arc::new(RwLock::new(self.inner)); | ||
|
||
let span = tracing::info_span!("watch-config"); | ||
tokio::spawn( | ||
WatchConfig { | ||
inner: inner.clone(), | ||
watch: self.watch, | ||
path, | ||
hash: self.hash, | ||
} | ||
.run() | ||
.instrument(span), | ||
); | ||
|
||
ConfigFile { inner } | ||
} | ||
|
||
pub fn data_dir(&self) -> Option<&Path> { | ||
self.inner.data.data_dir.as_deref() | ||
} | ||
} | ||
|
||
fn open(path: &Path) -> Result<Loader, Error> { | ||
let mut toml: Option<Cow<'static, str>> = None; | ||
|
||
if !path.exists() { | ||
std::fs::write(path, DEFAULT_CONFIG).map_err(|error| { | ||
Error::WriteFile { | ||
error, | ||
path: path.to_owned(), | ||
} | ||
})?; | ||
|
||
toml = Some(DEFAULT_CONFIG.into()); | ||
} | ||
|
||
let watcher = FileWatcher::new().map_err(|error| { | ||
Error::WatchFile { | ||
error, | ||
path: path.to_owned(), | ||
} | ||
})?; | ||
|
||
let watch = WatchModified::new(watcher, ConfigFile::DEBOUNCE).map_err(|error| { | ||
Error::WatchFile { | ||
error, | ||
path: path.to_owned(), | ||
} | ||
})?; | ||
|
||
let toml = if let Some(toml) = toml { | ||
toml | ||
} | ||
else { | ||
std::fs::read_to_string(path) | ||
.map_err(|error| { | ||
Error::ReadFile { | ||
error, | ||
path: path.to_owned(), | ||
} | ||
})? | ||
.into() | ||
}; | ||
|
||
let hash = FileHash::hash(toml.as_bytes()); | ||
|
||
let document: DocumentMut = toml.parse().map_err(|error| { | ||
Error::ParseToml { | ||
error, | ||
path: path.to_owned(), | ||
toml: toml.clone().into_owned(), | ||
} | ||
})?; | ||
|
||
let data: Data = toml_edit::de::from_document(document.clone()).map_err(|error| { | ||
Error::ParseToml { | ||
error: error.into(), | ||
path: path.to_owned(), | ||
toml: toml.into_owned(), | ||
} | ||
})?; | ||
|
||
Ok(Loader { | ||
inner: Inner { | ||
path: path.to_owned(), | ||
hash, | ||
document, | ||
data, | ||
}, | ||
watch, | ||
hash, | ||
}) | ||
} | ||
|
||
#[derive(Debug)] | ||
struct WatchConfig { | ||
inner: Arc<RwLock<Inner>>, | ||
watch: WatchModified, | ||
path: PathBuf, | ||
hash: FileHash, | ||
} | ||
|
||
impl WatchConfig { | ||
async fn run(mut self) { | ||
while let Ok(()) = self.watch.wait().await { | ||
match self.reload() { | ||
Ok(None) => {} | ||
Ok(Some(_document)) => todo!(), | ||
Err(_e) => todo!(), | ||
} | ||
} | ||
} | ||
|
||
fn reload(&mut self) -> Result<Option<DocumentMut>, Error> { | ||
let toml = std::fs::read_to_string(&self.path).map_err(|error| { | ||
Error::ReadFile { | ||
error, | ||
path: self.path.to_owned(), | ||
} | ||
})?; | ||
|
||
let hash = FileHash::hash(toml.as_bytes()); | ||
|
||
if hash != self.hash { | ||
let document: DocumentMut = toml.parse().map_err(|error| { | ||
Error::ParseToml { | ||
error, | ||
path: self.path.to_owned(), | ||
toml, | ||
} | ||
})?; | ||
|
||
self.hash = hash; | ||
|
||
Ok(Some(document)) | ||
} | ||
else { | ||
Ok(None) | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
use std::path::PathBuf; | ||
|
||
#[derive(Debug, thiserror::Error)] | ||
pub enum Error { | ||
#[error("Could not determine config directory.")] | ||
ConfigDirectory, | ||
|
||
#[error("Could not determine data directory.")] | ||
DataDirectory, | ||
|
||
#[error("Could not create directory: {path}")] | ||
CreateDirectory { | ||
#[source] | ||
error: std::io::Error, | ||
path: PathBuf, | ||
}, | ||
|
||
#[error("Could not read file: {path}")] | ||
ReadFile { | ||
#[source] | ||
error: std::io::Error, | ||
path: PathBuf, | ||
}, | ||
|
||
#[error("Could not write file: {path}")] | ||
WriteFile { | ||
#[source] | ||
error: std::io::Error, | ||
path: PathBuf, | ||
}, | ||
|
||
#[error("Could not watch file: {path}")] | ||
WatchFile { | ||
#[source] | ||
error: notify::Error, | ||
path: PathBuf, | ||
}, | ||
|
||
#[error("Could not parse TOML file: {path}")] | ||
ParseToml { | ||
#[source] | ||
error: toml_edit::TomlError, | ||
path: PathBuf, | ||
toml: String, | ||
}, | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,85 @@ | ||
mod config_file; | ||
mod error; | ||
|
||
use std::{ | ||
io::Cursor, | ||
path::{ | ||
Path, | ||
PathBuf, | ||
}, | ||
}; | ||
|
||
use config_file::ConfigFile; | ||
use murmur3::murmur3_x64_128; | ||
|
||
pub use self::error::Error; | ||
|
||
/// Default configuration directory relative to the OS's local configuration | ||
/// directory (e.g. `~/.config`` on Linux). | ||
pub const CONFIG_DIR_NAME: &'static str = "feralsec/skunk"; | ||
|
||
pub const DATA_DIR_NAME: &'static str = "feralsec/skunk"; | ||
|
||
/// Main configuration file name. | ||
pub const CONFIG_FILE: &'static str = "skunk.toml"; | ||
|
||
pub const DEFAULT_CONFIG: &'static str = include_str!("skunk.default.toml"); | ||
|
||
pub struct Config { | ||
config_dir: PathBuf, | ||
data_dir: PathBuf, | ||
config_file: ConfigFile, | ||
} | ||
|
||
impl Config { | ||
/// Open the configuration, either with the given `path` as path to the | ||
/// configuration directory, or using the default [`CONFIG_DIR_NAME`]. | ||
pub fn open(config_dir: Option<impl AsRef<Path>>) -> Result<Self, Error> { | ||
// determine path to configuration directory. | ||
let config_dir = config_dir | ||
.map(|path| path.as_ref().to_owned()) | ||
.or_else(|| dirs::config_local_dir().map(|path| path.join(CONFIG_DIR_NAME))) | ||
.ok_or(Error::ConfigDirectory)?; | ||
|
||
// if the directory doesn't exist, create it. | ||
create_dir_all(&config_dir)?; | ||
|
||
// parse the configuration TOML file. | ||
let config_file_path = config_dir.join(CONFIG_FILE); | ||
let config_file = ConfigFile::open(config_file_path)?; | ||
|
||
// determine path to data directory | ||
let data_dir = config_file | ||
.data_dir() | ||
.map(ToOwned::to_owned) | ||
.or_else(|| dirs::data_local_dir().map(|path| path.join(DATA_DIR_NAME))) | ||
.ok_or(Error::DataDirectory)?; | ||
|
||
create_dir_all(&data_dir)?; | ||
|
||
Ok(Self { | ||
config_dir, | ||
data_dir, | ||
config_file: config_file.load(), | ||
}) | ||
} | ||
} | ||
|
||
fn create_dir_all(path: impl AsRef<Path>) -> Result<(), Error> { | ||
let path = path.as_ref(); | ||
std::fs::create_dir_all(path).map_err(|error| { | ||
Error::CreateDirectory { | ||
error, | ||
path: path.to_owned(), | ||
} | ||
}) | ||
} | ||
|
||
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] | ||
struct FileHash(u128); | ||
|
||
impl FileHash { | ||
pub fn hash(data: &[u8]) -> Self { | ||
Self(murmur3_x64_128(&mut Cursor::new(data), 0).unwrap()) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
|
||
[ca] | ||
# The private key for the CA | ||
# key_file = "ca.key.pem" | ||
|
||
# The public key for the CA | ||
# cert_file = "ca.cert.pem" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -4,6 +4,7 @@ mod api; | |
mod app; | ||
mod args; | ||
mod config; | ||
mod config_new; | ||
mod proxy; | ||
mod util; | ||
|
||
|
Oops, something went wrong.