Configuration¶
A config is a JSON document passed with --cfg <file>. It has two
top-level objects: norm (the policy to validate against) and an optional
collect (which domains to gather at runtime). A baseline file (see
Baseline capture) is just a generated norm, so it can be fed straight back
via --cfg.
{
"collect": { "processes": true, "ports": true, "packages": true,
"netif": true, "trace": true, "trace_window_ms": 1000,
"trace_args": false },
"norm": { "...": "..." }
}
The collect section¶
Selects domains without command-line flags (OR’d with the CLI flags), so a
deployed config can drive a device. Keys: processes, ports,
packages, netif, trace (plus trace_window_ms and
trace_args). Mounts are always collected; fingerprints need
--fingerprint <dir>.
The norm section¶
Each block is optional; only configured blocks are enforced. An other_*
flag, where present, controls whether items not listed in the norm are allowed.
Note
With no policy configured for a domain (e.g. no processes or
ports block, or no --cfg at all) that domain’s check is
inventory-only: the items are collected and reported but nothing is
flagged. The allow-list semantics (other_*_present: false ⇒ unlisted is a
violation) apply only once you actually provide a list — typically a
generated baseline. The policy-free threat heuristics
(integrity, IOC, persistence, accounts, resolvers) always run regardless, so
an unconfigured run still surfaces real compromise, just without inventory
noise.
Processes¶
"other_processes_present": false,
"processes": [ { "name": "sshd", "cpu_min": 0, "cpu_max": 100 } ]
A process is matched by name; cpu_min/cpu_max bound its CPU load.
With other_processes_present: false any unlisted process is a violation —
but only when the processes list is non-empty; an absent/empty list leaves
the process check inventory-only (see the note above).
Ports¶
"other_ports_present": false,
"ports": [
{ "port": 22, "protocol": "tcp", "state": "LISTEN", "process": "sshd" },
{ "port_min": 49152, "port_max": 65535 }
]
protocol (tcp/udp/tcp6/udp6) and state (LISTEN/ESTABLISHED/…) are
optional filters; process pins the owner; a range uses port_min /
port_max.
Files¶
Expected fingerprints (typically generated via --baseline --fingerprint):
"other_files_present": false,
"files": [
{ "path": "/bin/su", "csum": "...", "mode": "4755", "uid": 0, "gid": 0 },
{ "path": "/bin/ping", "csum": "...", "caps": "0100000200000000" }
]
mode (octal, incl. setuid/setgid/sticky), uid/gid and caps
(Linux file capabilities) are optional and enforced only when present. Paths
are image-relative (any --root prefix is stripped). See Baseline capture.
Packages¶
"other_packages_present": false,
"packages": [ { "name": "openssl", "version": "1.1.1n" } ]
SBOM drift; version is optional (any version allowed if omitted). CVE
matching is separate — see Packages & vulnerabilities.
Network¶
"other_netifs_present": true,
"netifs": [ { "name": "eth0", "promisc": false, "mac": "...", "mtu": 1500 } ],
"other_routes_present": false,
"routes": [ { "dest": "0.0.0.0", "mask": "0.0.0.0", "gateway": "192.168.1.1" } ],
"other_neighbors_present": true,
"neighbors": [ { "ip": "192.168.1.1", "mac": "aa:bb:cc:dd:ee:ff" } ]
See Network for the detection semantics (PROMISC, gateway redirect, ARP spoofing) and recommended flag settings.
Filesystems / mounts¶
"file_systems": {
"restrictions": { "file_mounts": true, "bind_mounts": { "files": true, "dirs": false } },
"paths": { "allow": ["/", "/proc", "/tmp"], "deny": ["/etc/shadow"] },
"verify_filesystems": [
{ "fstype": "ext4", "root": "/" },
{ "fstype": "tmpfs", "root": "/tmp", "options": { "require": ["nosuid","nodev"], "forbid": ["exec"] } }
]
}
Restrict file/bind mounts, allow/deny mount targets, and verify filesystem
types + mount-option flags against verify_filesystems rules. Offline, mount
data comes from the image’s /etc/fstab (see Offline image analysis).
Kernel memory (SLAB/SLUB)¶
"memory": {
"min_available_pct": 10,
"max_slab_pct": 40,
"max_unreclaim_pct": 25,
"min_swap_free_pct": 20,
"max_cache_mb": 512,
"require_slabinfo_restricted": true,
"require_freelist_hardened": true,
"require_freelist_random": true
}
Thresholds are checked against /proc/meminfo and /proc/slabinfo; a
percentage knob left out (or set to 0) is not enforced, so a device can pin
only what matters for it. max_cache_mb bounds any single slab cache,
which is the shape a kernel object leak takes long before total memory
exhaustion. The three require_* flags check hardening rather than volume:
that /proc/slabinfo is root-only, and that the running kernel was built
with CONFIG_SLAB_FREELIST_HARDENED / CONFIG_SLAB_FREELIST_RANDOM
(read from /proc/config, /boot/config-<release> or
/lib/modules/<release>/…; when no config is readable the option is reported
as unknown and never flagged).
Without this block the domain is inventory-only, with one exception: the meminfo-vs-slabinfo cross-check always runs. See Threat detection.
Syscalls (behavior)¶
"syscalls": {
"forbidden": ["init_module", "finit_module", "kexec_load", "bpf"],
"forbidden_args": ["path=/etc/shadow", "prot=rwx"],
"deny_classes": ["module", "kexec", "ptrace"],
"max_rate": 200000,
"max_process_rate": 50000
}
Checked against the --trace sample. forbidden names syscalls no process
may issue; deny_classes names whole behavior classes (exec, ptrace,
module, bpf, kexec, mount, namespace, privilege,
network, memexec, keyring, fsperm), which keeps a policy
meaningful as the kernel grows new syscalls. An unknown class name is ignored,
so a newer policy still loads on an older agent. forbidden_args matches
against the decoded argument text (--trace-args) as a substring, which
lets one rule pin a path, an address range or a flag combination. The two rate ceilings are
syscalls per second, host-wide and per process; a knob left out is not
enforced, and neither is evaluated over an observation window shorter than
200 ms. Without this block the domain is inventory-only. See Syscall behavior tracing.