Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add option to prefix enriched fields instead of capitalizing them #245

Merged
merged 1 commit into from
Jan 14, 2025
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Add option to prefix enriched fields instead of capitalizing them
This seems useful for mis-designed log analysis software that treats
JSNO object keys as case-insensitive.

Close #244
hillu committed Jan 13, 2025
commit 045951cd56cec494dcb996396cdb2962435ae9d0
7 changes: 7 additions & 0 deletions etc/laurel/config.toml
Original file line number Diff line number Diff line change
@@ -109,6 +109,13 @@ script = true
# Add groups that the user (uid) is a member of. Default: true
user-groups = true

# Add a prefix to enriched fields; this may be useful if logs are
# consumed by analysis software that doesn't properly understand
# uppercase and lowercase JSON object fields as identical. This
# setting has no affect enriched fields passed in from auditd.
# Default: unset
# prefix = "enriched_"

[label-process]

# Audit records that contain certain keys can be reused as a label
5 changes: 5 additions & 0 deletions man/laurel.8.md
Original file line number Diff line number Diff line change
@@ -140,6 +140,11 @@ Options that can be configured here actually add information to events
mentioned in `SYSCALL.exe`. Default: true
- `user-groups`: Add groups that the user ("uid") is a member of.
Default: true
- `prefix`: Add a prefix to enriched fields; this may be useful if
logs are onsumed by analysis software that doesn't properly
understand uppercase and lowercase JSON object fields as identical.
This setting does not affect enriched fields passed in from
`auditd(8)`. Default: unset

## `[label-process]` section

49 changes: 36 additions & 13 deletions src/coalesce.rs
Original file line number Diff line number Diff line change
@@ -35,6 +35,7 @@ pub struct Settings {
pub enrich_pid: bool,
pub enrich_script: bool,
pub enrich_uid_groups: bool,
pub enrich_prefix: Option<String>,

pub proc_label_keys: HashSet<Vec<u8>>,
pub proc_propagate_labels: HashSet<Vec<u8>>,
@@ -71,6 +72,7 @@ impl Default for Settings {
enrich_pid: true,
enrich_script: true,
enrich_uid_groups: true,
enrich_prefix: None,
proc_label_keys: HashSet::new(),
proc_propagate_labels: HashSet::new(),
translate_universal: false,
@@ -330,7 +332,7 @@ impl<'a, 'ev> Coalesce<'a, 'ev> {
}

/// Create an enriched pid entry in rv.
fn add_record_procinfo(&self, rec: &mut Body, key: &[u8], proc: &Process) {
fn add_record_procinfo(&self, rec: &mut Body, name: &[u8], proc: &Process) {
let mut m: Vec<(Key, Value)> = Vec::with_capacity(4);
match &proc.key {
ProcessKey::Event(id) => {
@@ -341,7 +343,7 @@ impl<'a, 'ev> Coalesce<'a, 'ev> {
m.push(("START_TIME".into(), format!("{sec}.{msec:03}").into()));
}
}
if key != b"pid" {
if name != b"pid" {
if let Some(comm) = &proc.comm {
m.push(("comm".into(), Value::from(comm.as_slice())));
}
@@ -368,7 +370,11 @@ impl<'a, 'ev> Coalesce<'a, 'ev> {
}
}

rec.push((Key::NameTranslated(key.into()), Value::Map(m)));
let key = match &self.settings.enrich_prefix {
Some(s) => Key::Name(NVec::from_iter(s.bytes().chain(name.iter().cloned()))),
None => Key::NameTranslated(name.into()),
};
rec.push((key, Value::Map(m)));
}

/// Translates UID, GID and variants, e.g.:
@@ -383,26 +389,34 @@ impl<'a, 'ev> Coalesce<'a, 'ev> {
return false;
}
match (key, value) {
(Key::NameUID(r), Value::Number(Number::Dec(d))) => {
(Key::NameUID(name), Value::Number(Number::Dec(d))) => {
let translated = if *d == 0xffffffff {
"unset".to_string()
} else if let Some(user) = self.userdb.get_user(*d as u32) {
user
} else {
format!("unknown({d})")
};
rec.push((Key::NameTranslated(r.clone()), Value::from(translated)));
let key = match &self.settings.enrich_prefix {
Some(s) => Key::Name(NVec::from_iter(s.bytes().chain(name.iter().cloned()))),
None => Key::NameTranslated(name.clone()),
};
rec.push((key, Value::from(translated)));
true
}
(Key::NameGID(r), Value::Number(Number::Dec(d))) => {
(Key::NameGID(name), Value::Number(Number::Dec(d))) => {
let translated = if *d == 0xffffffff {
"unset".to_string()
} else if let Some(group) = self.userdb.get_group(*d as u32) {
group
} else {
format!("unknown({d})")
};
rec.push((Key::NameTranslated(r.clone()), Value::from(translated)));
let key = match &self.settings.enrich_prefix {
Some(s) => Key::Name(NVec::from_iter(s.bytes().chain(name.iter().cloned()))),
None => Key::NameTranslated(name.clone()),
};
rec.push((key, Value::from(translated)));
true
}
_ => false,
@@ -1019,10 +1033,18 @@ impl<'a, 'ev> Coalesce<'a, 'ev> {
}

if let (Some(arch_name), true) = (arch_name, self.settings.translate_universal) {
body.push((Key::Literal("ARCH"), Value::Literal(arch_name)));
let key = match &self.settings.enrich_prefix {
Some(s) => Key::Name(NVec::from_iter(s.bytes().chain(b"arch".iter().cloned()))),
None => Key::Literal("ARCH"),
};
body.push((key, Value::Literal(arch_name)));
}
if let (Some(syscall_name), true) = (syscall_name, self.settings.translate_universal) {
body.push((Key::Literal("SYSCALL"), Value::Literal(syscall_name)));
let key = match &self.settings.enrich_prefix {
Some(s) => Key::Name(NVec::from_iter(s.bytes().chain(b"syscall".iter().cloned()))),
None => Key::Literal("SYSCALL"),
};
body.push((key, Value::Literal(syscall_name)));
}

self.add_record_procinfo(body, b"pid", proc);
@@ -1058,10 +1080,11 @@ impl<'a, 'ev> Coalesce<'a, 'ev> {
.unwrap_or(format!("unknown({id})"))
};

body.push((
Key::NameTranslated(NVec::from(*name)),
Value::from(translated),
));
let key = match &self.settings.enrich_prefix {
Some(s) => Key::Name(NVec::from_iter(s.bytes().chain((*name).iter().cloned()))),
None => Key::NameTranslated((*name).into()),
};
body.push((key, Value::from(translated)));
}
}

4 changes: 4 additions & 0 deletions src/config.rs
Original file line number Diff line number Diff line change
@@ -97,6 +97,8 @@ pub struct Enrich {
pub script: bool,
#[serde(default = "true_value", rename = "uid-groups")]
pub uid_groups: bool,
#[serde(default)]
pub prefix: Option<String>,
}

impl Default for Enrich {
@@ -108,6 +110,7 @@ impl Default for Enrich {
pid: true,
script: true,
uid_groups: true,
prefix: None,
}
}
}
@@ -335,6 +338,7 @@ impl Config {
enrich_pid: self.enrich.pid,
enrich_script: self.enrich.script,
enrich_uid_groups: self.enrich.uid_groups,
enrich_prefix: self.enrich.prefix.clone(),
proc_label_keys: self
.label_process
.label_keys