diff --git a/Cargo.toml b/Cargo.toml index c635d64..49d6460 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -56,6 +56,7 @@ ptr_meta = "0.1" rkyv_typename = "0.7" rkyv_dyn = "0.7" inventory = "0.1" +linkme = "0.2.10" chrono = { version = "0.4", features = ["serde"] } # Password hashing for internal users @@ -77,7 +78,6 @@ capnp-rpc = "0.14.1" rsasl = { git = "https://github.com/dequbed/rsasl.git", branch = "main", default_features = false, features = ["unstable_custom_mechanism", "provider", "registry_static", "plain"] } desfire = "0.2.0-alpha1" hex = { version = "0.4.3", features = ["serde"] } -linkme = "0.2.10" futures-signals = "0.3.22" async-oneshot = "0.5" diff --git a/bffhd/capnp/config.rs b/bffhd/capnp/config.rs new file mode 100644 index 0000000..942621d --- /dev/null +++ b/bffhd/capnp/config.rs @@ -0,0 +1,59 @@ +use std::fmt::Formatter; +use std::net::ToSocketAddrs; +use std::path::PathBuf; + +use serde::{Serialize, Deserialize}; + +use crate::config::deser_option; + +#[derive(Debug, Clone, Serialize, Deserialize)] +/// API Socket Configuration block. +/// +/// One configuration block can result in several sockets if the given `address` resolves to more +/// than one SocketAddr. BFFH will attempt to bind to all of them. +pub struct Listen { + pub address: String, + + #[serde(default, skip_serializing_if = "Option::is_none", deserialize_with = "deser_option")] + pub port: Option, +} + +impl Listen { + pub fn to_tuple(&self) -> (&str, u16) { + (self.address.as_str(), self.port.unwrap_or(DEFAULT_PORT)) + } +} + +impl std::fmt::Display for Listen { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!(f, "{}:{}", &self.address, self.port.unwrap_or(DEFAULT_PORT)) + } +} + +impl ToSocketAddrs for Listen { + type Iter = <(String, u16) as ToSocketAddrs>::Iter; + + fn to_socket_addrs(&self) -> std::io::Result { + if let Some(port) = self.port { + (self.address.as_str(), port).to_socket_addrs() + } else { + (self.address.as_str(), DEFAULT_PORT).to_socket_addrs() + } + } +} + +#[derive(Debug, Clone, Serialize, Deserialize, Default)] +pub struct TlsListen { + pub certfile: PathBuf, + pub keyfile: PathBuf, + + #[serde(default, skip_serializing_if = "Option::is_none")] + pub ciphers: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub tls_min_version: Option, + #[serde(default = "Vec::new", skip_serializing_if = "Vec::is_empty")] + pub protocols: Vec, +} + +// The default port in the non-assignable i.e. free-use area +pub const DEFAULT_PORT: u16 = 59661; \ No newline at end of file diff --git a/bffhd/capnp/mod.rs b/bffhd/capnp/mod.rs index 92a04ca..fea11e7 100644 --- a/bffhd/capnp/mod.rs +++ b/bffhd/capnp/mod.rs @@ -1,6 +1,3 @@ -use crate::config::Listen; - - use async_net::TcpListener; @@ -18,13 +15,12 @@ use std::io; use std::net::SocketAddr; - use crate::authentication::AuthenticationHandle; - - - use crate::session::SessionManager; +mod config; +pub use config::{Listen, TlsListen}; + mod authenticationsystem; mod connection; mod machine; diff --git a/bffhd/config/dhall.rs b/bffhd/config/dhall.rs new file mode 100644 index 0000000..f506b52 --- /dev/null +++ b/bffhd/config/dhall.rs @@ -0,0 +1,8 @@ +use std::path::Path; +use crate::Config; + +pub fn read_config_file(path: impl AsRef) -> Result { + serde_dhall::from_file(path) + .parse() + .map_err(Into::into) +} \ No newline at end of file diff --git a/bffhd/config.rs b/bffhd/config/mod.rs similarity index 71% rename from bffhd/config.rs rename to bffhd/config/mod.rs index fea704c..c8cb36c 100644 --- a/bffhd/config.rs +++ b/bffhd/config/mod.rs @@ -6,17 +6,23 @@ use serde::{Serialize, Deserialize}; use std::fmt::Formatter; use std::net::{ToSocketAddrs}; +use serde_dhall::{SimpleType}; +mod dhall; +pub use dhall::read_config_file as read; + use crate::authorization::permissions::{PrivilegesBuf}; use crate::authorization::roles::Role; +use crate::capnp::{Listen, TlsListen}; +use crate::logging::LogConfig; -type Result = std::result::Result; +pub fn load(path: impl AsRef, args: &clap::ArgMatches) -> anyhow::Result { + unimplemented!() +} -pub fn read(path: &Path) -> Result { - serde_dhall::from_file(path) - .parse() - .map_err(Into::into) +pub struct ConfigBlock { + static_type: SimpleType, } #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] @@ -78,7 +84,7 @@ pub struct Config { pub verbosity: isize, #[serde(default, skip)] - pub log_format: String, + pub logging: LogConfig, } impl Config { @@ -105,49 +111,6 @@ pub(crate) fn deser_option<'de, D, T>(d: D) -> std::result::Result, D: Ok(T::deserialize(d).ok()) } -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct Listen { - address: String, - #[serde(default, skip_serializing_if = "Option::is_none", deserialize_with = "deser_option")] - port: Option, -} - -#[derive(Debug, Clone, Serialize, Deserialize, Default)] -pub struct TlsListen { - pub certfile: PathBuf, - pub keyfile: PathBuf, - - #[serde(default, skip_serializing_if = "Option::is_none")] - pub ciphers: Option, - #[serde(default, skip_serializing_if = "Option::is_none")] - pub tls_min_version: Option, - #[serde(default = "Vec::new", skip_serializing_if = "Vec::is_empty")] - pub protocols: Vec, -} - -impl Listen { - pub fn to_tuple(&self) -> (&str, u16) { - (self.address.as_str(), self.port.unwrap_or(DEFAULT_PORT)) - } -} - -impl std::fmt::Display for Listen { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - write!(f, "{}:{}", &self.address, self.port.unwrap_or(DEFAULT_PORT)) - } -} - -impl ToSocketAddrs for Listen { - type Iter = <(String, u16) as ToSocketAddrs>::Iter; - - fn to_socket_addrs(&self) -> std::io::Result { - if let Some(port) = self.port { - (self.address.as_str(), port).to_socket_addrs() - } else { - (self.address.as_str(), DEFAULT_PORT).to_socket_addrs() - } - } -} impl Default for Config { fn default() -> Self { @@ -194,10 +157,7 @@ impl Default for Config { tlskeylog: None, verbosity: 0, - log_format: "Full".to_string(), + logging: LogConfig::default(), } } } - -// The default port in the non-assignable i.e. free-use area -pub const DEFAULT_PORT: u16 = 59661; diff --git a/bffhd/lib.rs b/bffhd/lib.rs index ce2dd2e..84c615f 100644 --- a/bffhd/lib.rs +++ b/bffhd/lib.rs @@ -40,7 +40,6 @@ mod session; - use std::sync::{Arc}; use anyhow::Context; @@ -79,7 +78,7 @@ pub static RESOURCES: OnceCell = OnceCell::new(); impl Diflouroborane { pub fn new(config: Config) -> anyhow::Result { - logging::init(&config); + logging::init(&config.logging); tracing::info!(version=RELEASE_STRING, "Starting"); let span = tracing::info_span!("setup"); diff --git a/bffhd/logging.rs b/bffhd/logging.rs index eb4852c..9ef1ed3 100644 --- a/bffhd/logging.rs +++ b/bffhd/logging.rs @@ -1,16 +1,46 @@ use tracing_subscriber::{EnvFilter}; use crate::Config; -pub fn init(config: &Config) { +use serde::{Serialize, Deserialize}; + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct LogConfig { + #[serde(default, skip_serializing_if = "Option::is_none")] + /// Log filter string in the tracing format `target[span{field=value}]=level`. + /// lvalue is optional and multiple filters can be combined with comma. + /// e.g. `warn,diflouroborane::actors=debug` will only print `WARN` and `ERROR` unless the + /// message is logged in a span below `diflouroborane::actors` (i.e. by an actor task) in + /// which case `DEBUG` and `INFO` will also be printed. + pub filter: Option, + + pub format: String, +} + +impl Default for LogConfig { + fn default() -> Self { + Self { + filter: None, + format: "full".to_string(), + } + } +} + +pub fn init(config: &LogConfig) { + let filter = if let Some(ref filter) = config.filter { + EnvFilter::new(filter.as_str()) + } else { + EnvFilter::from_env("BFFH_LOG") + }; + let builder = tracing_subscriber::fmt() - .with_env_filter(EnvFilter::from_default_env()); - let format = config.log_format.to_lowercase(); + .with_env_filter(filter); + + let format = config.format.to_lowercase(); match format.as_str() { "compact" => builder.compact().init(), "pretty" => builder.pretty().init(), "full" => builder.init(), _ => builder.init(), } - tracing::info!(format = format.as_str(), "Logging initialized") } \ No newline at end of file diff --git a/bffhd/resources/state/value.rs b/bffhd/resources/state/value.rs index 53c913e..e2e3b4c 100644 --- a/bffhd/resources/state/value.rs +++ b/bffhd/resources/state/value.rs @@ -9,7 +9,10 @@ use rkyv_dyn::{DynDeserializer, DynError, DynSerializer}; use crate::utils::oid::ObjectIdentifier; + +// Not using linkme because dynamically loaded modules use inventory; + use rkyv::ser::{ScratchSpace, Serializer}; use serde::de::Error as SerdeError; use serde::ser::SerializeMap; diff --git a/bffhd/tls.rs b/bffhd/tls.rs index 9f46bab..69e5952 100644 --- a/bffhd/tls.rs +++ b/bffhd/tls.rs @@ -8,6 +8,7 @@ use futures_rustls::TlsAcceptor; use rustls::{Certificate, PrivateKey, ServerConfig, SupportedCipherSuite}; use rustls::version::{TLS12, TLS13}; use tracing::{Level}; +use crate::capnp::TlsListen; use crate::config; use crate::keylog::KeyLogFile; @@ -61,7 +62,7 @@ impl TlsConfig { } } - pub fn make_tls_acceptor(&self, config: &config::TlsListen) -> anyhow::Result { + pub fn make_tls_acceptor(&self, config: &TlsListen) -> anyhow::Result { let span = tracing::debug_span!("tls"); let _guard = span.enter(); diff --git a/bin/bffhd/main.rs b/bin/bffhd/main.rs index 9da1ab0..4f06659 100644 --- a/bin/bffhd/main.rs +++ b/bin/bffhd/main.rs @@ -1,4 +1,4 @@ -use clap::{Arg, Command}; +use clap::{Arg, Command, Parser}; use diflouroborane::{config, Diflouroborane}; @@ -91,16 +91,23 @@ fn main() -> anyhow::Result<()> { // invalidate the generated TOML let stdout = io::stdout(); let mut handle = stdout.lock(); - handle.write_all(&encoded.as_bytes()).unwrap(); + handle.write_all(encoded.as_bytes()).unwrap(); // Early return to exit. return Ok(()); } else if matches.is_present("check config") { match config::read(&PathBuf::from_str(configpath).unwrap()) { Ok(c) => { - println!("{:#?}", c); - println!("config is valid"); - std::process::exit(0); + let formatted = format!("{:#?}", c); + + // Direct writing to fd 1 is faster but also prevents any print-formatting that could + // invalidate the generated TOML + let stdout = io::stdout(); + let mut handle = stdout.lock(); + handle.write_all(formatted.as_bytes()).unwrap(); + + // Early return to exit. + return Ok(()); } Err(e) => { eprintln!("{}", e); @@ -140,7 +147,7 @@ fn main() -> anyhow::Result<()> { if config.verbosity == 0 && matches.is_present("quiet") { config.verbosity = -1; } - config.log_format = matches.value_of("log format").unwrap_or("Full").to_string(); + config.logging.format = matches.value_of("log format").unwrap_or("full").to_string(); let mut bffh = Diflouroborane::new(config)?; bffh.run()?;