use std::collections::HashSet;
use std::path::PathBuf;
use serde::{Deserialize, Serialize};
use crate::coalesce::Settings;
use crate::label_matcher::LabelMatcher;
#[derive(Clone, Default, Debug, Serialize, Deserialize, Eq, PartialEq)]
pub struct Logfile {
#[serde(default)]
pub file: PathBuf,
#[serde(rename = "read-users")]
pub users: Option<Vec<String>>,
pub size: Option<u64>,
pub generations: Option<u64>,
#[serde(rename = "line-prefix")]
pub line_prefix: Option<String>,
}
#[derive(Clone, Debug, Default, Serialize, Deserialize, Eq, PartialEq)]
pub struct Debug {
pub log: Option<Logfile>,
#[serde(rename = "parse-error-log")]
pub parse_error_log: Option<Logfile>,
#[serde(rename = "dump-state-period")]
pub dump_state_period: Option<u64>,
}
#[derive(PartialEq, Eq, Debug, Serialize, Deserialize, Hash)]
#[serde(rename_all = "lowercase")]
pub enum ArrayOrString {
Array,
String,
}
fn execve_argv_default() -> HashSet<ArrayOrString> {
let mut execve_argv = HashSet::new();
execve_argv.insert(ArrayOrString::Array);
execve_argv
}
#[derive(Debug, Serialize, Deserialize)]
pub struct Transform {
#[serde(default = "execve_argv_default", rename = "execve-argv")]
pub execve_argv: HashSet<ArrayOrString>,
#[serde(default, rename = "execve-argv-limit-bytes")]
pub execve_argv_limit_bytes: Option<usize>,
}
impl Default for Transform {
fn default() -> Self {
Transform {
execve_argv: execve_argv_default(),
execve_argv_limit_bytes: None,
}
}
}
#[derive(Debug, Default, Serialize, Deserialize)]
pub struct Translate {
#[serde(default)]
pub universal: bool,
#[serde(default, rename = "user-db")]
pub userdb: bool,
}
fn execve_env_default() -> HashSet<String> {
let mut execve_env = HashSet::new();
execve_env.insert("LD_PRELOAD".into());
execve_env.insert("LD_LIBRARY_PATH".into());
execve_env
}
fn true_value() -> bool {
true
}
fn false_value() -> bool {
false
}
#[derive(Debug, Serialize, Deserialize)]
pub struct Enrich {
#[serde(default, rename = "execve-env")]
pub execve_env: HashSet<String>,
#[serde(default = "true_value")]
pub container: bool,
#[serde(default = "true_value")]
pub pid: bool,
#[serde(default = "false_value", rename = "parent-info")]
pub parent_info: bool,
#[serde(default = "true_value")]
pub script: bool,
}
impl Default for Enrich {
fn default() -> Self {
Enrich {
execve_env: execve_env_default(),
container: true,
pid: true,
parent_info: false,
script: true,
}
}
}
#[derive(Default, Debug, Serialize, Deserialize)]
pub struct LabelProcess {
#[serde(default, rename = "label-keys")]
pub label_keys: HashSet<String>,
#[serde(default, rename = "label-exe")]
pub label_exe: Option<LabelMatcher>,
#[serde(default, rename = "unlabel-exe")]
pub unlabel_exe: Option<LabelMatcher>,
#[serde(default, rename = "label-script")]
pub label_script: Option<LabelMatcher>,
#[serde(default, rename = "unlabel-script")]
pub unlabel_script: Option<LabelMatcher>,
#[serde(default, rename = "propagate-labels")]
pub propagate_labels: HashSet<String>,
}
#[derive(Default, Debug, Serialize, Deserialize)]
pub struct Filter {
#[serde(default, rename = "filter-keys")]
pub filter_keys: HashSet<String>,
#[serde(default, rename = "filter-labels")]
pub filter_labels: HashSet<String>,
}
#[derive(Debug, Serialize, Deserialize)]
pub struct Config {
pub user: Option<String>,
pub directory: Option<PathBuf>,
#[serde(default, rename = "statusreport-period")]
pub statusreport_period: Option<u64>,
#[serde(default)]
pub auditlog: Logfile,
#[serde(default)]
pub debug: Debug,
#[serde(default)]
pub transform: Transform,
#[serde(default)]
pub translate: Translate,
#[serde(default)]
pub enrich: Enrich,
#[serde(default, rename = "label-process")]
pub label_process: LabelProcess,
#[serde(default)]
pub filter: Filter,
}
impl Default for Config {
fn default() -> Self {
Config {
user: None,
directory: Some(".".into()),
statusreport_period: None,
auditlog: Logfile {
file: "audit.log".into(),
users: None,
size: Some(10 * 1024 * 1024),
generations: Some(5),
line_prefix: None,
},
debug: Debug::default(),
transform: Transform::default(),
translate: Translate::default(),
enrich: Enrich::default(),
label_process: LabelProcess::default(),
filter: Filter::default(),
}
}
}
impl std::fmt::Display for Config {
fn fmt(&self, fmt: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
write!(
fmt,
"(user={} directory={} statusreport-period={} file={} users={} size={} generations={})",
self.user.clone().unwrap_or_else(|| "n/a".to_string()),
self.directory
.clone()
.unwrap_or_else(|| PathBuf::from("."))
.display(),
self.statusreport_period.unwrap_or(0),
self.auditlog.file.to_string_lossy(),
self.auditlog
.users
.clone()
.unwrap_or_else(|| vec!["n/a".to_string()])
.join(","),
self.auditlog.size.unwrap_or(0),
self.auditlog.generations.unwrap_or(0)
)
}
}
impl Config {
pub fn make_coalesce_settings(&self) -> Settings {
Settings {
execve_argv_list: self.transform.execve_argv.contains(&ArrayOrString::Array),
execve_argv_string: self.transform.execve_argv.contains(&ArrayOrString::String),
execve_argv_limit_bytes: self.transform.execve_argv_limit_bytes,
execve_env: self
.enrich
.execve_env
.iter()
.map(|s| s.as_bytes().to_vec())
.collect(),
enrich_container: self.enrich.container,
enrich_pid: self.enrich.pid,
enrich_parent_info: self.enrich.parent_info,
enrich_script: self.enrich.script,
proc_label_keys: self
.label_process
.label_keys
.iter()
.map(|s| s.as_bytes().to_vec())
.collect(),
proc_propagate_labels: self
.label_process
.propagate_labels
.iter()
.map(|s| s.as_bytes().to_vec())
.collect(),
translate_universal: self.translate.universal,
translate_userdb: self.translate.userdb,
label_exe: self.label_process.label_exe.as_ref(),
unlabel_exe: self.label_process.unlabel_exe.as_ref(),
label_script: self.label_process.label_script.as_ref(),
unlabel_script: self.label_process.unlabel_script.as_ref(),
filter_keys: self
.filter
.filter_keys
.iter()
.map(|s| s.as_bytes().to_vec())
.collect(),
filter_labels: self
.filter
.filter_labels
.iter()
.map(|s| s.as_bytes().to_vec())
.collect(),
}
}
}
#[cfg(test)]
mod test {
use super::*;
use std::path::Path;
#[test]
fn simple() {
let c: Config = toml::de::from_str(
r#"
user = "somebody"
directory = "/path/to/somewhere"
statusreport-period = 86400
[auditlog]
file = "somefile"
read-users = ["splunk"]
"#,
)
.unwrap();
println!("{:#?}", &c);
assert_eq!(c.user, Some("somebody".to_string()));
assert_eq!(
c.directory,
Some(Path::new("/path/to/somewhere").to_path_buf())
);
assert_eq!(c.statusreport_period, Some(86400));
assert_eq!(
c.auditlog,
Logfile {
file: Path::new("somefile").to_path_buf(),
users: Some(vec!["splunk".to_string()]),
size: None,
generations: None,
line_prefix: None,
}
);
}
#[test]
fn parse_defaults() {
toml::de::from_str::<Config>("").unwrap();
toml::de::from_str::<Config>(
r#"
[auditlog]
[debug]
[transform]
[translate]
[enrich]
[label-process]
[filter]
"#,
)
.unwrap();
}
}