Skip to content
Open
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
39 changes: 15 additions & 24 deletions fact/src/bpf/mod.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use std::{io, path::PathBuf};
use std::io;

use anyhow::{Context, bail};
use aya::{
Expand All @@ -7,7 +7,6 @@ use aya::{
programs::{Program, lsm::LsmLink},
};
use checks::Checks;
use globset::{Glob, GlobSet, GlobSetBuilder};
use libc::c_char;
use log::{error, info, warn};
use tokio::{
Expand All @@ -16,7 +15,12 @@ use tokio::{
task::JoinSet,
};

use crate::{config::BpfConfig, event::Event, host_info, metrics::EventCounter};
use crate::{
config::{BpfConfig, PathsConfig},
event::Event,
host_info,
metrics::EventCounter,
};

use fact_ebpf::{LPM_SIZE_MAX, event_t, inode_key_t, inode_value_t, metrics_t, path_prefix_t};

Expand All @@ -30,11 +34,9 @@ pub struct Bpf {

tx: mpsc::Sender<Event>,

paths_config: watch::Receiver<Vec<PathBuf>>,
paths_config: watch::Receiver<PathsConfig>,
paths_lpm_map: LpmTrie<MapData, [c_char; LPM_SIZE_MAX as usize], c_char>,

paths_globset: GlobSet,

links: Vec<LsmLink>,

running: watch::Receiver<bool>,
Expand All @@ -43,7 +45,7 @@ pub struct Bpf {

impl Bpf {
pub fn new(
paths_config: watch::Receiver<Vec<PathBuf>>,
paths_config: watch::Receiver<PathsConfig>,
bpf_config: &BpfConfig,
running: watch::Receiver<bool>,
metrics: EventCounter,
Expand All @@ -66,7 +68,6 @@ impl Bpf {
checks,
tx,
paths_config,
paths_globset: GlobSet::empty(),
paths_lpm_map,
links: Vec::new(),
running,
Expand Down Expand Up @@ -176,10 +177,9 @@ impl Bpf {
}

fn load_paths(&mut self) -> anyhow::Result<()> {
if self.paths_config.borrow().is_empty() {
if self.paths_config.borrow().patterns().is_empty() {
self.detach_progs();
self.cleanup_lpm_map(&[])?;
self.paths_globset = GlobSet::empty();
return Ok(());
}

Expand All @@ -190,22 +190,13 @@ impl Bpf {
// Add the new prefixes
let new_paths = {
let paths_config = self.paths_config.borrow();
let mut new_paths = Vec::with_capacity(paths_config.len());
let mut builder = GlobSetBuilder::new();
for p in paths_config.iter() {
let Some(glob_str) = p.to_str() else {
bail!("failed to convert path {} to string", p.display());
};

builder.add(
Glob::new(glob_str).with_context(|| format!("invalid glob {}", glob_str))?,
);

let patterns = paths_config.patterns();
let mut new_paths = Vec::with_capacity(patterns.len());
for p in patterns {
let prefix = path_prefix_t::try_from(p)?;
self.paths_lpm_map.insert(&prefix.into(), 0, 0)?;
new_paths.push(prefix);
}
self.paths_globset = builder.build()?;
new_paths
};

Expand Down Expand Up @@ -318,7 +309,7 @@ impl Bpf {
// so we let the event go into HostScanner and make the
// decision there.
if !event.is_monitored_by_parent() &&
event.is_ignored(&self.paths_globset) {
event.is_ignored(&self.paths_config.borrow().globset) {
self.metrics.dropped();
continue;
}
Expand Down Expand Up @@ -395,7 +386,7 @@ mod bpf_tests {
let mut config = FactConfig::default();
config.set_paths(paths);
let bpf_config = config.bpf.clone();
let reloader = Reloader::from(config);
let reloader = Reloader::try_from(config).unwrap();
let metrics = Metrics::new();
let (run_tx, run_rx) = watch::channel(true);
let (bpf, mut rx) = Bpf::new(
Expand Down
111 changes: 87 additions & 24 deletions fact/src/config/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ use std::{

use anyhow::{Context, bail};
use clap::Parser;
use globset::{Glob, GlobSet};
use log::info;
use yaml_rust2::{Yaml, YamlLoader, yaml};

Expand All @@ -33,7 +34,7 @@ fn yaml_to_duration_secs(v: &Yaml) -> Option<Duration> {

#[derive(Debug, Default, PartialEq, Clone)]
pub struct FactConfig {
paths: Option<Vec<PathBuf>>,
paths: PathsConfig,
pub grpc: GrpcConfig,
pub otel: OTelConfig,
pub endpoint: EndpointConfig,
Expand Down Expand Up @@ -82,10 +83,7 @@ impl FactConfig {
}

pub fn update(&mut self, from: &FactConfig) {
if let Some(paths) = from.paths.as_deref() {
self.paths = Some(paths.to_owned());
}

self.paths.update(&from.paths);
self.grpc.update(&from.grpc);
self.otel.update(&from.otel);
self.endpoint.update(&from.endpoint);
Expand Down Expand Up @@ -116,10 +114,6 @@ impl FactConfig {
}
}

pub fn paths(&self) -> &[PathBuf] {
self.paths.as_ref().map(|v| v.as_ref()).unwrap_or(&[])
}

pub fn skip_pre_flight(&self) -> bool {
self.skip_pre_flight.unwrap_or(false)
}
Expand All @@ -146,7 +140,7 @@ impl FactConfig {

#[cfg(test)]
pub fn set_paths(&mut self, paths: Vec<PathBuf>) {
self.paths = Some(paths);
self.paths = paths.try_into().expect("Invalid paths");
}
}

Expand Down Expand Up @@ -188,21 +182,10 @@ impl TryFrom<Vec<Yaml>> for FactConfig {

match k {
"paths" if v.is_array() => {
let paths = v
.as_vec()
.unwrap()
.iter()
.map(|p| {
let Some(p) = p.as_str() else {
bail!("Path has invalid type: {p:?}");
};
Ok(PathBuf::from(p))
})
.collect::<anyhow::Result<_>>()?;
config.paths = Some(paths);
config.paths = v.as_vec().unwrap().try_into()?;
}
"paths" if v.is_null() => {
config.paths = Some(Vec::new());
config.paths = PathsConfig::empty();
}
"grpc" if v.is_hash() => {
let grpc = v.as_hash().unwrap();
Expand Down Expand Up @@ -271,6 +254,83 @@ impl TryFrom<Vec<Yaml>> for FactConfig {
}
}

#[derive(Debug, Default, Clone)]
pub struct PathsConfig {
patterns: Option<Vec<PathBuf>>,
pub globset: GlobSet,
}

impl PathsConfig {
const fn empty() -> Self {
PathsConfig {
patterns: Some(Vec::new()),
globset: GlobSet::empty(),
}
}

fn globset_build<'a>(patterns: impl Iterator<Item = &'a PathBuf>) -> anyhow::Result<GlobSet> {
let mut builder = GlobSet::builder();
for p in patterns {
let Some(p) = p.to_str() else {
bail!("paths item has invalid UTF-8: {}", p.display());
};
let p = Glob::new(p).with_context(|| format!("invalid glob {}", p))?;
builder.add(p);
}

builder.build().map_err(anyhow::Error::from)
}

fn update(&mut self, other: &Self) {
if other.patterns.is_some() {
self.patterns = other.patterns.clone();
self.globset = other.globset.clone();
}
}

pub fn patterns(&self) -> &[PathBuf] {
self.patterns.as_deref().unwrap_or(&[])
}
}

impl PartialEq for PathsConfig {
fn eq(&self, other: &Self) -> bool {
self.patterns() == other.patterns()
}
}

impl TryFrom<&yaml::Array> for PathsConfig {
type Error = anyhow::Error;

fn try_from(value: &yaml::Array) -> Result<Self, Self::Error> {
let paths = value
.iter()
.map(|p| match p.as_str() {
Some(p) => Ok(p.into()),
None => bail!("paths field has invalid type: {p:?}"),
})
.collect::<Result<Vec<_>, _>>()?;
let globset = PathsConfig::globset_build(paths.iter())?;

Ok(PathsConfig {
patterns: Some(paths),
globset,
})
}
}

impl TryFrom<Vec<PathBuf>> for PathsConfig {
type Error = anyhow::Error;

fn try_from(paths: Vec<PathBuf>) -> Result<Self, Self::Error> {
let globset = PathsConfig::globset_build(paths.iter())?;
Ok(PathsConfig {
patterns: Some(paths),
globset,
})
}
}

#[derive(Debug, Default, PartialEq, Eq, Clone)]
pub struct EndpointConfig {
address: Option<SocketAddr>,
Expand Down Expand Up @@ -915,7 +975,10 @@ pub struct FactCli {
impl FactCli {
fn into_config(self) -> FactConfig {
FactConfig {
paths: self.paths,
paths: self
.paths
.map(|patterns| patterns.try_into().expect("Invalid paths configuration"))
.unwrap_or_default(),
Comment thread
coderabbitai[bot] marked this conversation as resolved.
grpc: GrpcConfig {
url: self.url,
certs: self.certs,
Expand Down
41 changes: 22 additions & 19 deletions fact/src/config/reloader/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use tokio::{
time::interval,
};

use crate::config::OTelConfig;
use crate::config::{OTelConfig, PathsConfig};

use super::{CONFIG_FILES, EndpointConfig, FactConfig, GrpcConfig};

Expand All @@ -18,7 +18,7 @@ pub struct Reloader {
endpoint: watch::Sender<EndpointConfig>,
grpc: watch::Sender<GrpcConfig>,
otel: watch::Sender<OTelConfig>,
paths: watch::Sender<Vec<PathBuf>>,
paths: watch::Sender<PathsConfig>,
files: HashMap<&'static str, (i64, i64)>,
scan_interval: watch::Sender<Duration>,
rate_limit: watch::Sender<u64>,
Expand Down Expand Up @@ -80,7 +80,7 @@ impl Reloader {

/// Subscribe to get notifications when paths configuration is
/// changed.
pub fn paths(&self) -> watch::Receiver<Vec<PathBuf>> {
pub fn paths(&self) -> watch::Receiver<PathsConfig> {
self.paths.subscribe()
}

Expand Down Expand Up @@ -147,17 +147,6 @@ impl Reloader {

/// Propagate configuration changes to all subscribers that need it
fn send_updates(&self, new: FactConfig) {
self.paths.send_if_modified(|old| {
let new = new.paths();
if *old != new {
debug!("Sending new paths configuration...");
*old = new.to_vec();
true
} else {
false
}
});

self.scan_interval.send_if_modified(|old| {
let new = new.scan_interval();
if *old != new {
Expand Down Expand Up @@ -188,9 +177,20 @@ impl Reloader {
endpoint,
grpc,
otel,
paths,
..
} = new;

self.paths.send_if_modified(|old| {
if *old != paths {
debug!("Sending new paths configuration...");
*old = paths;
true
} else {
false
}
});

self.endpoint.send_if_modified(|old| {
if *old != endpoint {
debug!("Sending new endpoint configuration...");
Expand Down Expand Up @@ -242,8 +242,10 @@ impl Reloader {
}
}

impl From<FactConfig> for Reloader {
fn from(config: FactConfig) -> Self {
impl TryFrom<FactConfig> for Reloader {
type Error = anyhow::Error;

fn try_from(config: FactConfig) -> Result<Self, Self::Error> {
let files = CONFIG_FILES
.iter()
.filter_map(|path| {
Expand All @@ -265,23 +267,24 @@ impl From<FactConfig> for Reloader {
.collect();

let enabled = config.hotreload();
let (paths, _) = watch::channel(config.paths().to_vec());
let (scan_interval, _) = watch::channel(config.scan_interval());
let (rate_limit, _) = watch::channel(config.rate_limit());

let FactConfig {
endpoint,
grpc,
otel,
paths,
..
} = config;

let (endpoint, _) = watch::channel(endpoint);
let (grpc, _) = watch::channel(grpc);
let (otel, _) = watch::channel(otel);
let (paths, _) = watch::channel(paths);
let trigger = Arc::new(Notify::new());

Reloader {
Ok(Reloader {
enabled,
endpoint,
grpc,
Expand All @@ -291,6 +294,6 @@ impl From<FactConfig> for Reloader {
rate_limit,
files,
trigger,
}
})
}
}
Loading
Loading