Skip to content
Draft
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
923 changes: 867 additions & 56 deletions Cargo.lock

Large diffs are not rendered by default.

3 changes: 3 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@ resolver = "2"
members = [
"fact",
"fact-api",
"fact-core",
"fact-ebpf",
"fact-operator",
]
default-members = ["fact"]

Expand All @@ -30,6 +32,7 @@ openssl = "0.10.75"
prometheus-client = { version = "0.25.0", default-features = false }
prost = "0.14.0"
prost-types = "0.14.0"
schemars = { version = "1" }
serde = { version = "1.0.219", features = ["derive"] }
serde_json = "1.0.142"
shlex = "2.0.1"
Expand Down
27 changes: 24 additions & 3 deletions Containerfile
Original file line number Diff line number Diff line change
Expand Up @@ -42,10 +42,11 @@ FROM builder AS build
ARG FACT_VERSION
RUN --mount=type=cache,target=/root/.cargo/registry \
--mount=type=cache,target=/app/target \
cargo build --release && \
cp target/release/fact fact
cargo build --release --all && \
cp target/release/fact fact && \
cp target/release/fact-operator fact-operator

FROM ubi-micro-base
FROM ubi-micro-base AS fact

ARG FACT_VERSION
LABEL name="fact" \
Expand All @@ -64,3 +65,23 @@ COPY LICENSE-APACHE LICENSE-MIT LICENSE-GPL2 /licenses/
RUN update-crypto-policies --set DEFAULT:PQ

ENTRYPOINT ["fact"]

FROM ubi-micro-base AS fact-operator

ARG FACT_VERSION
LABEL name="fact-operator" \
vendor="StackRox" \
maintainer="support@stackrox.com" \
summary="File activity operator" \
description="This image supports an operator for deploying the file activity daemonset" \
io.stackrox.fact-operator.version="${FACT_VERSION}"

COPY --from=package_installer /out/ /

COPY --from=build /app/fact-operator /usr/local/bin

COPY LICENSE-APACHE LICENSE-MIT LICENSE-GPL2 /licenses/

RUN update-crypto-policies --set DEFAULT:PQ

ENTRYPOINT ["fact-operator"]
18 changes: 17 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@ version:
image-name:
@echo "$(FACT_IMAGE_NAME)"

operator-name:
@echo "$(FACT_OPERATOR_NAME)"

mock-server:
make -C mock-server

Expand All @@ -17,9 +20,21 @@ image:
-f Containerfile \
--build-arg FACT_VERSION=$(FACT_VERSION) \
--build-arg RUST_VERSION=$(RUST_VERSION) \
--target fact \
-t $(FACT_IMAGE_NAME) \
$(CURDIR)

operator:
$(DOCKER) build \
-f Containerfile \
--build-arg FACT_VERSION=$(FACT_VERSION) \
--build-arg RUST_VERSION=$(RUST_VERSION) \
--target fact-operator \
-t $(FACT_OPERATOR_NAME) \
$(CURDIR)

images: image operator

licenses:THIRD_PARTY_LICENSES.html

THIRD_PARTY_LICENSES.html:Cargo.lock
Expand Down Expand Up @@ -54,4 +69,5 @@ format:
make -C fact-ebpf format
ruff format tests/

.PHONY: tag mock-server integration-tests image image-name licenses coverage lint clean
.PHONY: tag mock-server integration-tests image images image-name
.PHONY: operator operator-name licenses coverage lint clean
2 changes: 2 additions & 0 deletions constants.mk
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ FACT_TAG ?= $(shell git describe --always --tags --abbrev=10 --dirty)
FACT_VERSION ?= $(FACT_TAG)
FACT_REGISTRY ?= quay.io/stackrox-io/fact
FACT_IMAGE_NAME ?= $(FACT_REGISTRY):$(FACT_TAG)
FACT_OPERATOR_REGISTRY ?= quay.io/stackrox-io/fact-operator
FACT_OPERATOR_NAME ?= $(FACT_OPERATOR_REGISTRY):$(FACT_TAG)

CLANG_FMT ?= $(shell which clang-format)

Expand Down
17 changes: 17 additions & 0 deletions fact-core/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
[package]
name = "fact-core"
version = "0.1.0"
edition = "2024"
license.workspace = true

[dependencies]
anyhow = { workspace = true }
clap = { workspace = true }
log = { workspace = true }
schemars = { workspace = true }
serde = { workspace = true }
tokio = { workspace = true }
yaml-rust2 = { workspace = true }

[build-dependencies]
anyhow = { workspace = true }
File renamed without changes.
154 changes: 145 additions & 9 deletions fact/src/config/mod.rs → fact-core/src/config/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ use std::{
use anyhow::{Context, bail};
use clap::Parser;
use log::info;
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use yaml_rust2::{Yaml, YamlLoader, yaml};

pub mod reloader;
Expand All @@ -23,11 +25,14 @@ const CONFIG_FILES: [&str; 4] = [
"fact.yaml",
];

#[derive(Debug, Default, PartialEq, Eq, Clone)]
#[derive(Debug, Default, PartialEq, Eq, Clone, JsonSchema, Serialize, Deserialize)]
pub struct FactConfig {
paths: Option<Vec<PathBuf>>,
#[serde(default)]
pub grpc: GrpcConfig,
#[serde(default)]
pub endpoint: EndpointConfig,
#[serde(default)]
pub bpf: BpfConfig,
skip_pre_flight: Option<bool>,
json: Option<bool>,
Expand Down Expand Up @@ -125,10 +130,59 @@ impl FactConfig {
self.rate_limit.unwrap_or(0)
}

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

pub fn to_yaml(&self) -> Yaml {
let mut config = yaml::Hash::new();

if let Some(paths) = &self.paths {
let paths = paths
.iter()
.filter_map(|p| p.to_str().map(|p| Yaml::String(p.to_string())))
.collect::<Vec<_>>();
config.insert(Yaml::String("paths".into()), Yaml::Array(paths));
}

config.insert(Yaml::String("grpc".into()), self.grpc.to_yaml());
config.insert(Yaml::String("endpoint".into()), self.endpoint.to_yaml());
config.insert(Yaml::String("bpf".into()), self.bpf.to_yaml());

if let Some(skip_pre_flight) = self.skip_pre_flight {
config.insert(
Yaml::String("skip_pre_flight".into()),
Yaml::Boolean(skip_pre_flight),
);
}

if let Some(json) = self.json {
config.insert(Yaml::String("json".into()), Yaml::Boolean(json));
}

if let Some(hotreload) = self.hotreload {
config.insert(Yaml::String("hotreload".into()), Yaml::Boolean(hotreload));
}

if let Some(scan_interval) = self.scan_interval {
let scan_interval = scan_interval.as_secs_f64();
let scan_interval = if scan_interval.fract() != 0.0 {
Yaml::Real(scan_interval.to_string())
} else {
Yaml::Integer(scan_interval as i64)
};
config.insert(Yaml::String("scan_interval".into()), scan_interval);
}

if let Some(rate_limit) = self.rate_limit {
config.insert(
Yaml::String("rate_limit".into()),
Yaml::Integer(rate_limit as i64),
);
}

Yaml::Hash(config)
}
}

impl TryFrom<&str> for FactConfig {
Expand Down Expand Up @@ -189,10 +243,16 @@ impl TryFrom<Vec<Yaml>> for FactConfig {
let grpc = v.as_hash().unwrap();
config.grpc = GrpcConfig::try_from(grpc)?;
}
"grpc" if v.is_null() => {
// Nothing to do
}
"endpoint" if v.is_hash() => {
let endpoint = v.as_hash().unwrap();
config.endpoint = EndpointConfig::try_from(endpoint)?;
}
"endpoint" if v.is_null() => {
// Nothing to do
}
"skip_pre_flight" => {
let Some(spf) = v.as_bool() else {
bail!("skip_pre_flight field has incorrect type: {v:?}");
Expand All @@ -205,12 +265,13 @@ impl TryFrom<Vec<Yaml>> for FactConfig {
};
config.json = Some(json);
}
"bpf" => {
let Some(bpf) = v.as_hash() else {
bail!("bpf section has incorrect type: {v:#?}");
};
"bpf" if v.is_hash() => {
let bpf = v.as_hash().unwrap();
config.bpf = BpfConfig::try_from(bpf)?;
}
"bpf" if v.is_null() => {
// Nothing to do
}
"hotreload" => {
let Some(hotreload) = v.as_bool() else {
bail!("hotreload field has incorrect type: {v:?}");
Expand Down Expand Up @@ -251,7 +312,7 @@ impl TryFrom<Vec<Yaml>> for FactConfig {
}
}

#[derive(Debug, Default, PartialEq, Eq, Clone)]
#[derive(Debug, Default, PartialEq, Eq, Clone, JsonSchema, Serialize, Deserialize)]
pub struct EndpointConfig {
address: Option<SocketAddr>,
expose_metrics: Option<bool>,
Expand Down Expand Up @@ -285,6 +346,37 @@ impl EndpointConfig {
pub fn health_check(&self) -> bool {
self.health_check.unwrap_or(false)
}

fn to_yaml(&self) -> Yaml {
let mut endpoint = yaml::Hash::new();

if let Some(address) = self.address {
endpoint.insert(
Yaml::String("address".into()),
Yaml::String(address.to_string()),
);
}

if let Some(expose_metrics) = self.expose_metrics {
endpoint.insert(
Yaml::String("expose_metrics".into()),
Yaml::Boolean(expose_metrics),
);
}

if let Some(health_check) = self.health_check {
endpoint.insert(
Yaml::String("health_check".into()),
Yaml::Boolean(health_check),
);
}

if !endpoint.is_empty() {
Yaml::Hash(endpoint)
} else {
Yaml::Null
}
}
}

impl TryFrom<&yaml::Hash> for EndpointConfig {
Expand Down Expand Up @@ -328,7 +420,7 @@ impl TryFrom<&yaml::Hash> for EndpointConfig {
}
}

#[derive(Debug, Default, PartialEq, Eq, Clone)]
#[derive(Debug, Default, PartialEq, Eq, Clone, JsonSchema, Serialize, Deserialize)]
pub struct GrpcConfig {
url: Option<String>,
certs: Option<PathBuf>,
Expand All @@ -352,6 +444,26 @@ impl GrpcConfig {
pub fn certs(&self) -> Option<&Path> {
self.certs.as_deref()
}

fn to_yaml(&self) -> Yaml {
let mut grpc = yaml::Hash::new();

if let Some(url) = &self.url {
grpc.insert(Yaml::String("url".into()), Yaml::String(url.clone()));
}

if let Some(certs) = &self.certs
&& let Some(certs) = certs.to_str()
{
grpc.insert(Yaml::String("certs".into()), Yaml::String(certs.into()));
}

if !grpc.is_empty() {
Yaml::Hash(grpc)
} else {
Yaml::Null
}
}
}

impl TryFrom<&yaml::Hash> for GrpcConfig {
Expand Down Expand Up @@ -385,7 +497,7 @@ impl TryFrom<&yaml::Hash> for GrpcConfig {
}
}

#[derive(Debug, Default, PartialEq, Eq, Clone)]
#[derive(Debug, Default, PartialEq, Eq, Clone, JsonSchema, Serialize, Deserialize)]
pub struct BpfConfig {
ringbuf_size: Option<u32>,
inodes_max: Option<u32>,
Expand All @@ -409,6 +521,30 @@ impl BpfConfig {
pub fn inodes_max(&self) -> u32 {
self.inodes_max.unwrap_or(65536)
}

fn to_yaml(&self) -> Yaml {
let mut bpf = yaml::Hash::new();

if let Some(ringbuf_size) = self.ringbuf_size {
bpf.insert(
Yaml::String("ringbuf_size".into()),
Yaml::Integer(ringbuf_size as i64),
);
}

if let Some(inodes_max) = self.inodes_max {
bpf.insert(
Yaml::String("inodes_max".into()),
Yaml::Integer(inodes_max as i64),
);
}

if !bpf.is_empty() {
Yaml::Hash(bpf)
} else {
Yaml::Null
}
}
}

impl TryFrom<&yaml::Hash> for BpfConfig {
Expand Down
File renamed without changes.
Loading
Loading