Skip to content

Commit

Permalink
Add syslog capabilities
Browse files Browse the repository at this point in the history
  • Loading branch information
azenna committed Oct 17, 2023
1 parent 45870e9 commit 63f18fd
Show file tree
Hide file tree
Showing 5 changed files with 124 additions and 32 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions crates/modules/logger/README.md
Expand Up @@ -7,13 +7,15 @@ This module will log Pulsar threat events to stdout.
|Config|Type|Description|
|------|----|-----------|
|console|bool|log to stdout|
|syslog|bool|log to syslog|

Default configuration:

```ini
[logger]
enabled=true
console=true
syslog=true
```

You disable this module with:
Expand Down
115 changes: 83 additions & 32 deletions crates/modules/logger/src/lib.rs
Expand Up @@ -2,7 +2,17 @@ use pulsar_core::pdk::{
CleanExit, ConfigError, Event, ModuleConfig, ModuleContext, ModuleError, PulsarModule,
ShutdownSignal, Version,
};
use std::{
env,
fs::File,
io,
os::{
fd::AsFd,
unix::{fs::MetadataExt, net::UnixDatagram},
},
};

const UNIX_SOCK_PATHS: [&str; 3] = ["/dev/log", "/var/run/syslog", "/var/run/log"];
const MODULE_NAME: &str = "logger";

pub fn module() -> PulsarModule {
Expand All @@ -19,17 +29,36 @@ async fn logger_task(
) -> Result<CleanExit, ModuleError> {
let mut receiver = ctx.get_receiver();
let mut rx_config = ctx.get_config();
let mut logger = Logger::from_config(rx_config.read()?);
let sender = ctx.get_sender();

let mut logger = match Logger::from_config(rx_config.read()?) {
Ok(logr) => logr,
Err(logr) => {
sender
.raise_warning("Failed to connect to syslog".into())
.await;
logr
}
};

loop {
tokio::select! {
r = shutdown.recv() => return r,
_ = rx_config.changed() => {
logger = Logger::from_config(rx_config.read()?);
logger = match Logger::from_config(rx_config.read()?) {
Ok(logr) => logr,
Err(logr) => {
sender.raise_warning("Failed to connect to syslog".into()).await;
logr
}
}
}
msg = receiver.recv() => {
let msg = msg?;
logger.process(&msg)
if let Err(e) = logger.process(&msg) {
sender.raise_warning(format!("Writing to syslog failed: {e}")).await;
logger = Logger { syslog: None, ..logger };
}
},
}
}
Expand All @@ -39,7 +68,7 @@ async fn logger_task(
struct Config {
console: bool,
// file: bool, //TODO:
// syslog: bool, //TODO:
syslog: bool,
}

impl TryFrom<&ModuleConfig> for Config {
Expand All @@ -49,51 +78,73 @@ impl TryFrom<&ModuleConfig> for Config {
Ok(Self {
console: config.with_default("console", true)?,
// file: config.required("file")?,
// syslog: config.required("syslog")?,
syslog: config.with_default("syslog", true)?,
})
}
}

#[derive(Debug)]
struct Logger {
console: bool,
syslog: Option<UnixDatagram>,
}

impl Logger {
fn from_config(rx_config: Config) -> Self {
let Config { console } = rx_config;
Self { console }
fn from_config(rx_config: Config) -> Result<Self, Self> {
let Config { console, syslog } = rx_config;

let connected_to_journal = io::stderr()
.as_fd()
.try_clone_to_owned()
.and_then(|fd| File::from(fd).metadata())
.map(|meta| format!("{}:{}", meta.dev(), meta.ino()))
.ok()
.and_then(|stderr| {
env::var_os("JOURNAL_STREAM").map(|s| s.to_string_lossy() == stderr.as_str())
})
.unwrap_or(false);

let opt_sock = (syslog && !connected_to_journal)
.then(|| {
let sock = UnixDatagram::unbound().ok()?;
UNIX_SOCK_PATHS
.iter()
.find_map(|path| sock.connect(path).ok())
.map(|_| sock)
})
.flatten();

if syslog && opt_sock.is_none() {
Err(Self {
console,
syslog: opt_sock,
})
} else {
Ok(Self {
console,
syslog: opt_sock,
})
}
}

fn process(&self, event: &Event) {
if event.header().threat.is_some() && self.console {
terminal::print_event(event);
fn process(&mut self, event: &Event) -> io::Result<()> {
if event.header().threat.is_some() {
if self.console {
terminal::print_event(event);
}

if let Some(ref mut syslog) = &mut self.syslog {
syslog.send(format!("{}", event).as_bytes())?;
}
}
Ok(())
}
}

pub mod terminal {
use chrono::{DateTime, Utc};
use pulsar_core::{event::Threat, pdk::Event};
use pulsar_core::pdk::Event;

pub fn print_event(event: &Event) {
let header = event.header();
let time = DateTime::<Utc>::from(header.timestamp).format("%Y-%m-%dT%TZ");
let image = &header.image;
let pid = &header.pid;
let payload = event.payload();

if let Some(Threat {
source,
description,
extra: _,
}) = &event.header().threat
{
println!(
"[{time} \x1b[1;30;43mTHREAT\x1b[0m {image} ({pid})] [{source} - {description}] {payload}"
)
} else {
let source = &header.source;
println!("[{time} \x1b[1;30;46mEVENT\x1b[0m {image} ({pid})] [{source}] {payload}")
}
println!("{:#}", event)
}
}
1 change: 1 addition & 0 deletions crates/pulsar-core/Cargo.toml
Expand Up @@ -18,3 +18,4 @@ log = "0.4"
thiserror = "1.0.30"
nix = "0.26.2"
strum = { version = "0.25", features = ["derive"] }
chrono = { version = "0.4.23", features = ["std"], default-features = false }
37 changes: 37 additions & 0 deletions crates/pulsar-core/src/event.rs
Expand Up @@ -4,6 +4,7 @@ use std::{
time::SystemTime,
};

use chrono::{DateTime, Utc};
use serde::{de::DeserializeOwned, ser, Deserialize, Serialize};
use strum::{EnumDiscriminants, EnumString};
use validatron::{Operator, Validatron, ValidatronError};
Expand All @@ -29,6 +30,42 @@ impl Event {
}
}

impl fmt::Display for Event {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let header = self.header();
let time = DateTime::<Utc>::from(header.timestamp).format("%Y-%m-%dT%TZ");
let image = &header.image;
let pid = &header.pid;
let payload = self.payload();

if let Some(Threat {
source,
description,
extra: _,
}) = &self.header().threat
{
if f.alternate() {
writeln!(f, "[{time} \x1b[1;30;43mTHREAT\x1b[0m {image} ({pid})] [{source} - {description}] {payload}")
} else {
writeln!(
f,
"[{time} THREAT {image} ({pid})] [{source} - {description}] {payload}"
)
}
} else {
let source = &header.source;
if f.alternate() {
writeln!(
f,
"[{time} \x1b[1;30;46mEVENT\x1b[0m {image} ({pid})] [{source}] {payload}"
)
} else {
writeln!(f, "[{time} EVENT {image} ({pid})] [{source}] {payload}")
}
}
}
}

#[derive(Debug, Clone, Serialize, Deserialize, Validatron)]
pub struct Header {
pub image: String,
Expand Down

0 comments on commit 63f18fd

Please sign in to comment.