feat(agent): add windows and freebsd read-only collectors
This commit is contained in:
@@ -1,9 +1,16 @@
|
|||||||
use anyhow::{Result, bail};
|
use std::fs;
|
||||||
|
|
||||||
|
use anyhow::Result;
|
||||||
|
use chrono::Utc;
|
||||||
|
|
||||||
|
use crate::collectors::common::{
|
||||||
|
agent_id, command_output, current_session, domain, hostname, role_security_events, username,
|
||||||
|
};
|
||||||
use crate::config::AgentRole;
|
use crate::config::AgentRole;
|
||||||
use crate::telemetry::{
|
use crate::telemetry::{
|
||||||
IdentityInfo, NetworkSnapshot, ProcessInfo, ResourceInfo, SecurityEventInfo, SessionSnapshot,
|
IdentityInfo, NetworkConnectionInfo, NetworkInterfaceInfo, NetworkSnapshot, ProcessInfo,
|
||||||
TelemetryCollector, WorkforceActivityInfo,
|
ResourceInfo, SecurityEventInfo, SessionSnapshot, TelemetryCollector, WorkforceActivityInfo,
|
||||||
|
empty_workforce_activity,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
@@ -19,31 +26,184 @@ impl FreeBsdCollector {
|
|||||||
|
|
||||||
impl TelemetryCollector for FreeBsdCollector {
|
impl TelemetryCollector for FreeBsdCollector {
|
||||||
fn collect_identity(&self) -> Result<IdentityInfo> {
|
fn collect_identity(&self) -> Result<IdentityInfo> {
|
||||||
let _ = self.role;
|
let host = hostname();
|
||||||
unsupported()
|
Ok(IdentityInfo {
|
||||||
|
agent_id: agent_id(&host),
|
||||||
|
hostname: host,
|
||||||
|
os_name: command_output("uname", &["-s"]).unwrap_or_else(|| "FreeBSD".to_string()),
|
||||||
|
os_version: command_output("uname", &["-r"]).unwrap_or_default(),
|
||||||
|
platform: "freebsd".to_string(),
|
||||||
|
username: username(),
|
||||||
|
domain: domain(),
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn collect_sessions(&self) -> Result<SessionSnapshot> {
|
fn collect_sessions(&self) -> Result<SessionSnapshot> {
|
||||||
unsupported()
|
let mut active = vec![current_session("local")];
|
||||||
|
let mut ssh = Vec::new();
|
||||||
|
if std::env::var("SSH_CLIENT").is_ok() || std::env::var("SSH_TTY").is_ok() {
|
||||||
|
let session = current_session("ssh");
|
||||||
|
ssh.push(session.clone());
|
||||||
|
active.push(session);
|
||||||
|
}
|
||||||
|
Ok(SessionSnapshot {
|
||||||
|
active_sessions: active,
|
||||||
|
rdp_sessions: Vec::new(),
|
||||||
|
ssh_sessions: ssh,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn collect_processes(&self) -> Result<Vec<ProcessInfo>> {
|
fn collect_processes(&self) -> Result<Vec<ProcessInfo>> {
|
||||||
unsupported()
|
Ok(freebsd_processes(128))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn collect_resources(&self) -> Result<ResourceInfo> {
|
fn collect_resources(&self) -> Result<ResourceInfo> {
|
||||||
unsupported()
|
let memory_total = command_output("sysctl", &["-n", "hw.physmem"])
|
||||||
|
.and_then(|value| value.parse::<u64>().ok())
|
||||||
|
.unwrap_or(0);
|
||||||
|
Ok(ResourceInfo {
|
||||||
|
uptime_seconds: 0,
|
||||||
|
cpu_usage_percent: 0.0,
|
||||||
|
memory_total,
|
||||||
|
memory_used: 0,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn collect_network(&self) -> Result<NetworkSnapshot> {
|
fn collect_network(&self) -> Result<NetworkSnapshot> {
|
||||||
unsupported()
|
Ok(NetworkSnapshot {
|
||||||
|
interfaces: freebsd_interfaces(),
|
||||||
|
connections: freebsd_connections(256),
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn collect_security_events(&self) -> Result<Vec<SecurityEventInfo>> {
|
fn collect_security_events(&self) -> Result<Vec<SecurityEventInfo>> {
|
||||||
unsupported()
|
let mut events = role_security_events(self.role);
|
||||||
|
if let Some(summary) = freebsd_syslog_summary() {
|
||||||
|
events.push(SecurityEventInfo {
|
||||||
|
event_id: "freebsd-syslog-summary".to_string(),
|
||||||
|
source: "syslog".to_string(),
|
||||||
|
severity: "INFO".to_string(),
|
||||||
|
summary,
|
||||||
|
timestamp: Utc::now(),
|
||||||
|
evidence: vec!["/var/log/messages".to_string()],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
Ok(events)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn collect_workforce_activity(&self) -> Result<WorkforceActivityInfo> {
|
fn collect_workforce_activity(&self) -> Result<WorkforceActivityInfo> {
|
||||||
unsupported()
|
let mut activity = empty_workforce_activity();
|
||||||
|
activity.active_today = true;
|
||||||
|
activity.explanation = vec![
|
||||||
|
"FreeBSD collector reports host/session/process/network context; pfSense mode is read-only".to_string(),
|
||||||
|
];
|
||||||
|
Ok(activity)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn unsupported<T>() -> Result<T> {
|
fn freebsd_processes(limit: usize) -> Vec<ProcessInfo> {
|
||||||
bail!(
|
let Some(raw) = command_output("ps", &["-axo", "pid,ppid,comm,rss"]) else {
|
||||||
"FreeBSD/pfSense collector skeleton is present; sysctl/procstat/kvm probes are planned behind the same TelemetryRecord API"
|
return Vec::new();
|
||||||
)
|
};
|
||||||
|
let mut items = raw
|
||||||
|
.lines()
|
||||||
|
.skip(1)
|
||||||
|
.filter_map(|line| {
|
||||||
|
let cols = line.split_whitespace().collect::<Vec<_>>();
|
||||||
|
let pid = cols.first()?.parse::<u32>().ok()?;
|
||||||
|
let ppid = cols.get(1).and_then(|value| value.parse::<u32>().ok());
|
||||||
|
let name = cols.get(2).unwrap_or(&"process").to_string();
|
||||||
|
let memory_bytes = cols
|
||||||
|
.get(3)
|
||||||
|
.and_then(|value| value.parse::<u64>().ok())
|
||||||
|
.map(|value| value.saturating_mul(1024));
|
||||||
|
Some(ProcessInfo {
|
||||||
|
pid,
|
||||||
|
ppid,
|
||||||
|
name,
|
||||||
|
exe: None,
|
||||||
|
username: None,
|
||||||
|
cpu_percent: None,
|
||||||
|
memory_bytes,
|
||||||
|
started_at: None,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
items.truncate(limit);
|
||||||
|
items
|
||||||
|
}
|
||||||
|
|
||||||
|
fn freebsd_interfaces() -> Vec<NetworkInterfaceInfo> {
|
||||||
|
let Some(raw) = command_output("ifconfig", &["-l"]) else {
|
||||||
|
return Vec::new();
|
||||||
|
};
|
||||||
|
raw.split_whitespace()
|
||||||
|
.map(|name| NetworkInterfaceInfo {
|
||||||
|
name: name.to_string(),
|
||||||
|
mac: None,
|
||||||
|
addresses: Vec::new(),
|
||||||
|
up: true,
|
||||||
|
rx_bytes: None,
|
||||||
|
tx_bytes: None,
|
||||||
|
})
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn freebsd_connections(limit: usize) -> Vec<NetworkConnectionInfo> {
|
||||||
|
let Some(raw) = command_output("sockstat", &["-4", "-6"]) else {
|
||||||
|
return Vec::new();
|
||||||
|
};
|
||||||
|
let mut items = raw
|
||||||
|
.lines()
|
||||||
|
.skip(1)
|
||||||
|
.filter_map(parse_sockstat_line)
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
items.truncate(limit);
|
||||||
|
items
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_sockstat_line(line: &str) -> Option<NetworkConnectionInfo> {
|
||||||
|
let cols = line.split_whitespace().collect::<Vec<_>>();
|
||||||
|
let protocol = cols.get(4)?.to_ascii_lowercase();
|
||||||
|
if protocol != "tcp" && protocol != "udp" {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
let (local_addr, local_port) = split_host_port(cols.get(5)?)?;
|
||||||
|
let (remote_addr, remote_port) = cols
|
||||||
|
.get(6)
|
||||||
|
.and_then(|value| split_host_port(value))
|
||||||
|
.unwrap_or_default();
|
||||||
|
Some(NetworkConnectionInfo {
|
||||||
|
protocol: protocol.clone(),
|
||||||
|
local_addr,
|
||||||
|
local_port,
|
||||||
|
remote_addr: Some(remote_addr),
|
||||||
|
remote_port: Some(remote_port),
|
||||||
|
state: if protocol == "tcp" { "OPEN" } else { "UDP" }.to_string(),
|
||||||
|
pid: cols.get(2).and_then(|value| value.parse::<u32>().ok()),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn split_host_port(value: &str) -> Option<(String, u16)> {
|
||||||
|
let (host, port) = value.rsplit_once(':')?;
|
||||||
|
Some((
|
||||||
|
host.trim_matches(['[', ']']).to_string(),
|
||||||
|
port.parse().ok()?,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn freebsd_syslog_summary() -> Option<String> {
|
||||||
|
let text = fs::read_to_string("/var/log/messages").ok()?;
|
||||||
|
let count = text
|
||||||
|
.lines()
|
||||||
|
.rev()
|
||||||
|
.take(200)
|
||||||
|
.filter(|line| {
|
||||||
|
let lower = line.to_lowercase();
|
||||||
|
lower.contains("error") || lower.contains("fail") || lower.contains("denied")
|
||||||
|
})
|
||||||
|
.count();
|
||||||
|
Some(format!(
|
||||||
|
"recent FreeBSD syslog warning/error lines: {count}"
|
||||||
|
))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,14 @@
|
|||||||
use anyhow::{Result, bail};
|
use anyhow::Result;
|
||||||
|
use chrono::Utc;
|
||||||
|
|
||||||
|
use crate::collectors::common::{
|
||||||
|
agent_id, command_output, current_session, domain, hostname, role_security_events, username,
|
||||||
|
};
|
||||||
use crate::config::AgentRole;
|
use crate::config::AgentRole;
|
||||||
use crate::telemetry::{
|
use crate::telemetry::{
|
||||||
IdentityInfo, NetworkSnapshot, ProcessInfo, ResourceInfo, SecurityEventInfo, SessionSnapshot,
|
IdentityInfo, NetworkConnectionInfo, NetworkSnapshot, ProcessInfo, ResourceInfo,
|
||||||
TelemetryCollector, WorkforceActivityInfo,
|
SecurityEventInfo, SessionSnapshot, TelemetryCollector, WorkforceActivityInfo,
|
||||||
|
empty_workforce_activity,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
@@ -19,31 +24,201 @@ impl WindowsCollector {
|
|||||||
|
|
||||||
impl TelemetryCollector for WindowsCollector {
|
impl TelemetryCollector for WindowsCollector {
|
||||||
fn collect_identity(&self) -> Result<IdentityInfo> {
|
fn collect_identity(&self) -> Result<IdentityInfo> {
|
||||||
let _ = self.role;
|
let host = hostname();
|
||||||
unsupported()
|
Ok(IdentityInfo {
|
||||||
|
agent_id: agent_id(&host),
|
||||||
|
hostname: host,
|
||||||
|
os_name: "Windows".to_string(),
|
||||||
|
os_version: windows_version(),
|
||||||
|
platform: "windows".to_string(),
|
||||||
|
username: username(),
|
||||||
|
domain: domain(),
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn collect_sessions(&self) -> Result<SessionSnapshot> {
|
fn collect_sessions(&self) -> Result<SessionSnapshot> {
|
||||||
unsupported()
|
let mut active = vec![current_session("local")];
|
||||||
|
let mut rdp = Vec::new();
|
||||||
|
if std::env::var("SESSIONNAME")
|
||||||
|
.unwrap_or_default()
|
||||||
|
.to_ascii_lowercase()
|
||||||
|
.contains("rdp")
|
||||||
|
{
|
||||||
|
let session = current_session("rdp");
|
||||||
|
rdp.push(session.clone());
|
||||||
|
active.push(session);
|
||||||
|
}
|
||||||
|
Ok(SessionSnapshot {
|
||||||
|
active_sessions: active,
|
||||||
|
rdp_sessions: rdp,
|
||||||
|
ssh_sessions: Vec::new(),
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn collect_processes(&self) -> Result<Vec<ProcessInfo>> {
|
fn collect_processes(&self) -> Result<Vec<ProcessInfo>> {
|
||||||
unsupported()
|
Ok(windows_processes(128))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn collect_resources(&self) -> Result<ResourceInfo> {
|
fn collect_resources(&self) -> Result<ResourceInfo> {
|
||||||
unsupported()
|
let (memory_total, memory_used) = windows_memory();
|
||||||
|
Ok(ResourceInfo {
|
||||||
|
uptime_seconds: 0,
|
||||||
|
cpu_usage_percent: 0.0,
|
||||||
|
memory_total,
|
||||||
|
memory_used,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn collect_network(&self) -> Result<NetworkSnapshot> {
|
fn collect_network(&self) -> Result<NetworkSnapshot> {
|
||||||
unsupported()
|
Ok(NetworkSnapshot {
|
||||||
|
interfaces: Vec::new(),
|
||||||
|
connections: windows_connections(256),
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn collect_security_events(&self) -> Result<Vec<SecurityEventInfo>> {
|
fn collect_security_events(&self) -> Result<Vec<SecurityEventInfo>> {
|
||||||
unsupported()
|
let mut events = role_security_events(self.role);
|
||||||
|
events.push(SecurityEventInfo {
|
||||||
|
event_id: "windows-collector-v03".to_string(),
|
||||||
|
source: "awatch-agent-rs".to_string(),
|
||||||
|
severity: "INFO".to_string(),
|
||||||
|
summary: "Windows read-only collector is active without PowerShell primary collection; WinAPI/ETW/WMI depth is planned behind the same TelemetryRecord contract".to_string(),
|
||||||
|
timestamp: Utc::now(),
|
||||||
|
evidence: vec!["no PowerShell primary collector".to_string()],
|
||||||
|
});
|
||||||
|
Ok(events)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn collect_workforce_activity(&self) -> Result<WorkforceActivityInfo> {
|
fn collect_workforce_activity(&self) -> Result<WorkforceActivityInfo> {
|
||||||
unsupported()
|
let mut activity = empty_workforce_activity();
|
||||||
|
activity.active_today = true;
|
||||||
|
activity.explanation = vec![
|
||||||
|
"Windows collector reports session/process/network context; ActivityWatch/workforce scoring is calculated server-side".to_string(),
|
||||||
|
];
|
||||||
|
Ok(activity)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn unsupported<T>() -> Result<T> {
|
fn windows_version() -> String {
|
||||||
bail!(
|
command_output("cmd", &["/C", "ver"])
|
||||||
"Windows collector requires target_os=windows WinAPI/WMI implementation; PowerShell is not a primary collector"
|
.or_else(|| std::env::var("OS").ok())
|
||||||
)
|
.unwrap_or_else(|| "Windows".to_string())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn windows_memory() -> (u64, u64) {
|
||||||
|
let Some(raw) = command_output(
|
||||||
|
"wmic",
|
||||||
|
&[
|
||||||
|
"OS",
|
||||||
|
"get",
|
||||||
|
"FreePhysicalMemory,TotalVisibleMemorySize",
|
||||||
|
"/Value",
|
||||||
|
],
|
||||||
|
) else {
|
||||||
|
return (0, 0);
|
||||||
|
};
|
||||||
|
let mut free_kib = 0;
|
||||||
|
let mut total_kib = 0;
|
||||||
|
for line in raw.lines() {
|
||||||
|
if let Some(value) = line.strip_prefix("FreePhysicalMemory=") {
|
||||||
|
free_kib = value.trim().parse::<u64>().unwrap_or(0);
|
||||||
|
}
|
||||||
|
if let Some(value) = line.strip_prefix("TotalVisibleMemorySize=") {
|
||||||
|
total_kib = value.trim().parse::<u64>().unwrap_or(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let total = total_kib.saturating_mul(1024);
|
||||||
|
let used = total_kib.saturating_sub(free_kib).saturating_mul(1024);
|
||||||
|
(total, used)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn windows_processes(limit: usize) -> Vec<ProcessInfo> {
|
||||||
|
let Some(raw) = command_output("tasklist", &["/FO", "CSV", "/NH"]) else {
|
||||||
|
return Vec::new();
|
||||||
|
};
|
||||||
|
let mut items = raw
|
||||||
|
.lines()
|
||||||
|
.filter_map(parse_tasklist_line)
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
items.truncate(limit);
|
||||||
|
items
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_tasklist_line(line: &str) -> Option<ProcessInfo> {
|
||||||
|
let cols = parse_csv_line(line);
|
||||||
|
let name = cols.first()?.to_string();
|
||||||
|
let pid = cols.get(1)?.parse::<u32>().ok()?;
|
||||||
|
let memory_bytes = cols.get(4).map(|value| parse_tasklist_memory(value));
|
||||||
|
Some(ProcessInfo {
|
||||||
|
pid,
|
||||||
|
ppid: None,
|
||||||
|
name,
|
||||||
|
exe: None,
|
||||||
|
username: None,
|
||||||
|
cpu_percent: None,
|
||||||
|
memory_bytes,
|
||||||
|
started_at: None,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_csv_line(line: &str) -> Vec<String> {
|
||||||
|
line.trim_matches('"')
|
||||||
|
.split("\",\"")
|
||||||
|
.map(|value| value.trim().to_string())
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_tasklist_memory(value: &str) -> u64 {
|
||||||
|
value
|
||||||
|
.chars()
|
||||||
|
.filter(|ch| ch.is_ascii_digit())
|
||||||
|
.collect::<String>()
|
||||||
|
.parse::<u64>()
|
||||||
|
.unwrap_or(0)
|
||||||
|
.saturating_mul(1024)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn windows_connections(limit: usize) -> Vec<NetworkConnectionInfo> {
|
||||||
|
let Some(raw) = command_output("netstat", &["-ano"]) else {
|
||||||
|
return Vec::new();
|
||||||
|
};
|
||||||
|
let mut items = raw
|
||||||
|
.lines()
|
||||||
|
.filter_map(parse_netstat_line)
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
items.truncate(limit);
|
||||||
|
items
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_netstat_line(line: &str) -> Option<NetworkConnectionInfo> {
|
||||||
|
let cols = line.split_whitespace().collect::<Vec<_>>();
|
||||||
|
let protocol = cols.first()?.to_ascii_lowercase();
|
||||||
|
if protocol != "tcp" && protocol != "udp" {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
let (local_addr, local_port) = split_host_port(cols.get(1)?)?;
|
||||||
|
let (remote_addr, remote_port) = split_host_port(cols.get(2)?).unwrap_or_default();
|
||||||
|
let state = if protocol == "tcp" {
|
||||||
|
cols.get(3).unwrap_or(&"UNKNOWN").to_string()
|
||||||
|
} else {
|
||||||
|
"UDP".to_string()
|
||||||
|
};
|
||||||
|
let pid = cols.last().and_then(|value| value.parse::<u32>().ok());
|
||||||
|
Some(NetworkConnectionInfo {
|
||||||
|
protocol,
|
||||||
|
local_addr,
|
||||||
|
local_port,
|
||||||
|
remote_addr: Some(remote_addr),
|
||||||
|
remote_port: Some(remote_port),
|
||||||
|
state,
|
||||||
|
pid,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn split_host_port(value: &str) -> Option<(String, u16)> {
|
||||||
|
let (host, port) = value.rsplit_once(':')?;
|
||||||
|
Some((
|
||||||
|
host.trim_matches(['[', ']']).to_string(),
|
||||||
|
port.parse().ok()?,
|
||||||
|
))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,8 +15,8 @@ Agent -> Telemetry -> Analytics -> Risk -> Investigation -> Report
|
|||||||
## Границы реализации v0.3
|
## Границы реализации v0.3
|
||||||
|
|
||||||
- Linux collector собирает реальные данные через `/proc`, `/sys`, окружение сессии и системные журналы.
|
- Linux collector собирает реальные данные через `/proc`, `/sys`, окружение сессии и системные журналы.
|
||||||
- Windows collector имеет стабильный публичный интерфейс и подготовлен под WinAPI, ETW, Event Log API и WMI-библиотеки Rust.
|
- Windows collector формирует `TelemetryRecord` без PowerShell primary collection: identity/session/process/network/resource context через read-only системные источники и командные probe-фолбэки. Глубина WinAPI, ETW, Event Log API и WMI-библиотеки Rust является следующим расширением.
|
||||||
- FreeBSD collector имеет стабильный публичный интерфейс и подготовлен под `sysctl`, `procstat`, `kvm` и стандартные интерфейсы FreeBSD.
|
- FreeBSD collector формирует `TelemetryRecord` через read-only системные probe: `uname`, `sysctl`, `ps`, `ifconfig`, `sockstat`, `/var/log/messages`. Глубина `procstat`/`kvm` является следующим расширением.
|
||||||
- PowerShell не является основным механизмом сбора. Он допускается только как будущий `legacy` fallback под feature flag.
|
- PowerShell не является основным механизмом сбора. Он допускается только как будущий `legacy` fallback под feature flag.
|
||||||
- Агент не содержит скрытых функций, драйверов ядра, кейлоггера, записи экрана, перехвата документов и контентного DLP-анализа.
|
- Агент не содержит скрытых функций, драйверов ядра, кейлоггера, записи экрана, перехвата документов и контентного DLP-анализа.
|
||||||
|
|
||||||
|
|||||||
@@ -6,16 +6,22 @@ BSD-слой нужен для будущей поддержки FreeBSD и pfSe
|
|||||||
|
|
||||||
## FreeBSD collector
|
## FreeBSD collector
|
||||||
|
|
||||||
В v0.3 FreeBSD collector имеет стабильный интерфейс и честно возвращает ограничение реализации, если бинарник собран под FreeBSD до включения нативных источников.
|
В v0.3 FreeBSD collector уже формирует единый `TelemetryRecord` через read-only системные probe.
|
||||||
|
|
||||||
Планируемые источники:
|
Текущие источники:
|
||||||
|
|
||||||
- `sysctl`;
|
- `sysctl`;
|
||||||
|
- `ps`;
|
||||||
|
- `ifconfig`;
|
||||||
|
- `sockstat`;
|
||||||
|
- `/var/log/messages`;
|
||||||
|
- окружение SSH-сессии.
|
||||||
|
|
||||||
|
Следующий слой глубины:
|
||||||
|
|
||||||
- `procstat`;
|
- `procstat`;
|
||||||
- `kvm`;
|
- `kvm`;
|
||||||
- сетевые интерфейсы ОС;
|
- расширенная статистика pf/pfSense.
|
||||||
- системный syslog;
|
|
||||||
- сведения об SSH-сессиях.
|
|
||||||
|
|
||||||
## pfSense mode
|
## pfSense mode
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user