Skip to content
Closed
Show file tree
Hide file tree
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
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ The entire configuration is defined in a YAML file, typically named `suriconf.ya
|-----------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `suri-configuration` | Path to the default Suricata configuration file. |
| `log-dir` | Directory for Suricata logs (requires read/write permissions). |
| `socket` | Path to Suricata socket. |
| `preconf-time` | Duration of the Suricata preconfiguration run. |
| `analysis` | Analysis type: `dynamic` (multiple Suricata runs) or `static` (single Suricata run). |
| `mode` | Output mode: `suggestion` (recommendations only) or `modify` (writes changes to Suricata configuration file).<br>Modify mode with `yaml_change`: `ask` (user confirms each change) or `force` (all detected changes are applied automatically). |
Expand Down
4 changes: 4 additions & 0 deletions src/argument.rs
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,10 @@ pub enum Commands {
#[clap(short='l', long)]
path_to_logs: Option<PathBuf>,

/// Change path to Unix Socket
#[clap(short='s', long)]
path_to_socket: Option<PathBuf>,

/// Change the time of Suricata preconfiguration run (in seconds)
#[clap(short='t', long="time")]
preconf_time: Option<u64>,
Expand Down
102 changes: 85 additions & 17 deletions src/suricata.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ SPDX-License-Identifier: BSD-3-Clause
This file executes Suricata.
*/

const PIDFILE: &str = "/var/run/suriconf_suricata.pid";

use std::process::{Child, Command};
use crate::yaml::{emergency_check_memcap, Suriconf};
use crate::{FLOW_WINDOW, MIN_RUN};
Expand All @@ -16,10 +18,11 @@ use std::time::Duration;
use crossbeam_channel::{bounded, select, tick, Receiver};
use std::process::Stdio;
use std::io::{BufRead, BufReader};
use std::thread;
use procfs::process::{all_processes, Process};
use std::{fs, thread};
use procfs::process::{Process};
use signal_hook::consts::SIGINT;
use signal_hook::iterator::Signals;
use std::path::{Path, PathBuf};
use std::sync::{Arc, Mutex};
use std::sync::atomic::{AtomicBool, Ordering};

Expand All @@ -35,6 +38,14 @@ pub fn execute_suricata<'a>(suriconf: &Suriconf, logs: &mut CreatedLogs, options
let mut suricata_again = SuricataAgain::default();

let mut vec_of_sur_cmd: Vec<String> = vec![];
match delete_pid_file() {
Err(e) => {
panic!("{e}");
}
Ok(()) => {}
};

set_pid_file(&mut vec_of_sur_cmd);
get_capture_mode(suriconf, &mut vec_of_sur_cmd);

if cfg!(target_os = "windows") {
Expand Down Expand Up @@ -66,7 +77,7 @@ pub fn execute_suricata<'a>(suriconf: &Suriconf, logs: &mut CreatedLogs, options
let mut child = Command::new("sudo")
.arg("-n")
.args(args)
.stdout(Stdio::piped())
.stdout(Stdio::null())
.stderr(Stdio::piped())
.spawn()
.expect("Failed to execute process.");
Expand All @@ -81,6 +92,14 @@ pub fn execute_suricata<'a>(suriconf: &Suriconf, logs: &mut CreatedLogs, options
}
});

let suri_pid = match wait_on_suricata_start(&suriconf.socket, &mut child) {
Ok(pid) => pid,
Err(e) => {
kill_suricata(&mut child);
panic!("{e}")
}
};

let timeout = Duration::from_secs(suriconf.preconf_time);
let start = std::time::Instant::now();
let ticks_thread1 = tick(Duration::from_millis(100));
Expand Down Expand Up @@ -140,7 +159,6 @@ pub fn execute_suricata<'a>(suriconf: &Suriconf, logs: &mut CreatedLogs, options

let ticks = tick(Duration::from_millis(100));
let emergency_ticks = tick(Duration::from_secs(FLOW_WINDOW));
let suri_pid = check_process_name_for_suricata_main().expect("Unable to get Suricata-Main.");

loop {
select! {
Expand Down Expand Up @@ -198,6 +216,26 @@ pub fn get_capture_mode(suriconf: &Suriconf, vec_of_sur_cmd: &mut Vec<String>) {
});
}

pub fn delete_pid_file() -> std::io::Result<()> {
let status = Command::new("sudo")
.arg("-n")
.arg("rm")
.arg("-f")
.arg(PIDFILE)
.status()?;

if status.success() {
Ok(())
} else {
Err(std::io::Error::other("Failed to delete Suricata PID file."))
}
}

pub fn set_pid_file(vec_of_sur_cmd: &mut Vec<String>) {
vec_of_sur_cmd.push("--pidfile".to_string());
vec_of_sur_cmd.push(PIDFILE.to_string());
}

pub fn get_cpu_usage(sys: &mut SystemVar) {
sys.sys.refresh_cpu_usage();

Expand Down Expand Up @@ -272,24 +310,53 @@ pub fn get_workers(sys: &mut SystemVar) -> u64 {
}
workers
}

fn check_process_name_for_suricata_main() -> Option<i32> {
for _ in 0..10 {
for prc in all_processes().expect("Unable to get all processes.") {
let process: Process;
match prc {
Ok(prc) => {process = prc}
Err(_) => {continue}
fn get_suricata_pid() -> Result<i32, String> {
for _ in 0..20 {
if let Ok(content) = fs::read_to_string(PIDFILE) {
if let Ok(pid) = content.trim().parse::<i32>() {
if let Ok(comm) = fs::read_to_string(format!("/proc/{}/comm", pid)) {
if comm.trim() == "Suricata-Main" {
return Ok(pid);
}
}
}
}
thread::sleep(Duration::from_millis(1000));
}
Err("Suricata process not found.".into())
}
fn wait_on_suricata_start(socket: &PathBuf, child: &mut Child) -> Result<i32, String> {
match get_suricata_pid() {
Ok(pid) => {
let socket = socket.to_str().ok_or("Socket path is not valid UTF-8.")?;
for _ in 0..120 {
let output = Command::new("sudo")
.arg("-n")
.arg("suricatasc")
.arg("-c")
.arg("uptime")
.arg(socket)
.output()
.map_err(|e| format!("Unable to execute Suricata socket control tool: {e}"))?;

if output.status.success() {
return Ok(pid)
}

if process.stat().expect("Unable to find stats about process.").comm == "Suricata-Main" {
return Some(process.pid);
if let Some(status) = child.try_wait().expect("Unable to get Suricata status.") {
return Err(format!("Suricata exited during startup with status: {status}."));
}

thread::sleep(Duration::from_millis(1000));
}
Err("Suricata could not start.".into())
}
Err(e) => {
Err(e)
}
thread::sleep(Duration::from_millis(100));
}
None
}

fn get_cores_with_threads(suri_pid: i32, sys: &mut SystemVar) {
let proc = Process::new(suri_pid).expect("Unable to create process.");
let tasks = proc.tasks().expect("Unable to get process tasks.");
Expand Down Expand Up @@ -330,6 +397,7 @@ pub fn kill_suricata(child: &mut Child) {
.arg("Suricata-Main")
.status()
.expect("Unable to pkill Suricata-Main (SIGKILL).");

if !output.success() {
panic!("Process failed.");
}
Expand All @@ -356,4 +424,4 @@ pub fn check_min_suricata_runtime_for_modules(suriconf: &Suriconf) {
panic!("Unable to execute Suricata and have enough samples from preconfiguration, \
FlowThreads module needs at least 6 minutes.")
}
}
}
1 change: 1 addition & 0 deletions src/suriconf.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ ethtool-bin: /usr/bin/ethtool
ifconfig-bin: /usr/sbin/ifconfig
ip-bin: /usr/sbin/ip
log-dir: /var/log/suricata/
socket: /var/run/suricata/suricata-command.socket
preconf-time: 360 # in seconds
# 360 is minimum for flow_threads module
analysis: static # static/dynamic
Expand Down
12 changes: 12 additions & 0 deletions src/yaml.rs
Original file line number Diff line number Diff line change
Expand Up @@ -726,6 +726,7 @@ pub struct Suriconf {
pub ifconfig_bin: PathBuf,
pub ip_bin: PathBuf,
pub log_dir: PathBuf,
pub socket: PathBuf,
pub preconf_time: u64,
pub analysis: Analysis,
pub mode: Mode,
Expand Down Expand Up @@ -844,6 +845,13 @@ impl Suriconf {
self.find_log_dir(suriconf_string).expect("Unable to parse path to logs.")
};

self.socket = if let Some(Commands::Suricata { path_to_socket: Some(p), .. }) = &args.cmd {
p.clone()
}
else {
self.find_socket(suriconf_string).expect("Unable to parse path to unix socket.")
};

self.preconf_time = if let Some( Commands::Suricata { preconf_time: Some(p), .. }) = &args.cmd {
p.clone()
}
Expand Down Expand Up @@ -952,6 +960,10 @@ impl Suriconf {
text.get("log-dir").and_then(|c| c.as_str()).map(|c| PathBuf::from(c))
}

pub fn find_socket(&self, text: &Value) -> Option<PathBuf> {
text.get("socket").and_then(|c| c.as_str()).map(|c| PathBuf::from(c))
}

pub fn find_preconf_time(&self, text: &Value) -> Option<u64> {
text.get("preconf-time").and_then(|t| t.as_u64())
}
Expand Down
Loading