#Three ways to cut the noise
RoamSwitch gives you three ways to reduce alerts and automatic responses. Each one asks for a reason and keeps a record. Each one reduces something different.
| Tool | What it reduces | Where it is recorded | Takes effect |
|---|---|---|---|
Allowlist (roamswitch server allowlist) | Containment of one rule and process pair | /var/lib/roamswitch/ebpf_guard_allowlist.json | At once (no restart) |
Protected processes (protected_processes) | Freezing and killing that process | /etc/roamswitch/server.conf | After a service restart |
CVE acceptance (roamswitch vuln-accept) | The "needs action" count of package CVEs | ~/.config/roamswitch/vuln_acceptances.json | From the next scan |
The allowlist belongs to Kernel-level intrusion detection and autonomous isolation. A detection of an allowlisted pair is still recorded and notified (once per cooldown window). Only containment stops, and the pair does not count toward escalation to a host isolation. This part was checked in the code.
In this module you use all three on a disposable Ubuntu 24.04 VM. You add an entry, check that it applies, let it expire, and remove it.
#Add an allowlist entry with a reason and an end date
An allowlist entry is a pair of a rule and a process. Both names must match exactly. The same rule firing on a different program is still contained as before.
This writes to the allowlist (it changes the machine). To undo, run sudo roamswitch server allowlist remove <rule> <process>.
The rule names are examples for practice. In real use, write the rule name and process name from the detection. The detection history is shown by sudo roamswitch server ebpf.
sudo roamswitch server allowlist add "Reverse shell" backup-agent
sudo roamswitch server allowlist add "Reverse shell" backup-agent --days 14 Nightly backup connects to a remote host
sudo roamswitch server allowlist add "Outbound connection to C2" rsync --exe /usr/bin/rsync --days 7 Nightly rsync mirror
sudo roamswitch server allowlist add "X" rsync --days 400 long
sudo roamswitch server allowlist list Error: an allowlist entry needs a reason. Write it at the end of the command.
Ends: 2026-11-26 (14 days; use renew to extend)
✓ Added to allowlist: rule="Reverse shell" process="backup-agent"
⚠ Warning: this is a name-only allowlist entry. A process name can be spoofed just by renaming, so a malicious process renamed to this name would bypass detection. Bind it to the real executable with --exe <absolute-path> instead.
Ends: 2026-11-19 (7 days; use renew to extend)
✓ Added to allowlist: rule="Outbound connection to C2" process="rsync" exe="/usr/bin/rsync"
Error: --days needs a number from 1 to 365 (the default is 30).
2026-11-12T01:31:31+00:00
rule: Reverse shell
process: backup-agent
exe: (none: name-only, spoofable)
reason: Nightly backup connects to a remote host
added by: admin
ends: 2026-11-26 (14 days left)
2026-11-12T01:31:31+00:00
rule: Outbound connection to C2
process: rsync
exe: /usr/bin/rsync
reason: Nightly rsync mirror
added by: admin
ends: 2026-11-19 (7 days left)Four things show here.
- A reason is required. The last argument is the reason. Without one, nothing is added and the exit code is 1
- The end date is 30 days by default, 365 at most. 400 is refused
- A name-only entry prints a warning. Malware renamed to the same name would inherit it
- The list keeps the reason, who added it and the end date. The timestamp at the top is UTC
allowlist test answers how the guards would treat a process. It changes nothing.
sudo roamswitch server allowlist test "Outbound connection to C2" rsync --exe /usr/bin/rsync
sudo roamswitch server allowlist test "Outbound connection to C2" rsync --exe /tmp/rsync
sudo roamswitch server allowlist test "Reverse shell" sshd
sudo roamswitch server allowlist renew "Reverse shell" backup-agent --days 30
sudo roamswitch server allowlist remove "Reverse shell" backup-agent rule: Outbound connection to C2
process: rsync
exe: /usr/bin/rsync
✗ Freeze/kill protection: Not protected.
✓ Allowlist: This rule and process pair is allowlisted. The guards take no action.
Result: it is allowlisted, so the guards take no action on it.
rule: Outbound connection to C2
process: rsync
exe: /tmp/rsync
✗ Freeze/kill protection: Not protected.
✗ Allowlist: Not allowlisted.
Result: neither allowed nor protected. It can be contained according to the guard policy (guard.yaml).
rule: Reverse shell
process: sshd
exe: (unknown)
✓ Freeze/kill protection: Protected (built-in list). The guards never freeze or kill it.
Judged as /usr/sbin/sshd, found on PATH. If the running process is somewhere else, check it with --pid.
✗ Allowlist: Not allowlisted.
Result: a detection is still reported, but this process is never frozen or killed, and the eBPF guard only records and notifies instead of isolating it. Host-wide responses that do not name a process are unchanged.
✓ Renewed: the entry now ends on 2026-12-12.
✓ Removed from allowlist.The entry bound with --exe does not apply to /tmp/rsync. The name is the same, but the executable is somewhere else.
sshd is protected without adding anything, because it is on the built-in list. Protection stops freezing and killing. It does not stop host-wide responses.
renew sets a new end date counted from today. remove deletes one entry. When there is no such entry, it prints "No matching entry found." and exits with code 1.
#Name processes that containment never stops
Processes that containment never stops is set with protected_processes. These are processes that automatic containment (ransomware, runtime guard, canary) never freezes or kills. They are added to the built-in list (init, container runtimes, sshd, package managers, RoamSwitch itself). The default is empty.
There are two ways to write an entry (from the notes in server.conf.example).
- A process name counts only while its executable runs from a system location such as
/usr,/binor/opt - An absolute path matches that executable only. Use it for software installed outside the system locations
This edits the settings file (it changes the machine). The last step empties it again.
You run the same practice program from /opt and from /tmp. It is a copy of sleep.
sudo mkdir -p /opt/demo/bin; sudo cp /usr/bin/sleep /opt/demo/bin/worker; cp /usr/bin/sleep /tmp/worker
nohup /opt/demo/bin/worker 3600 >/dev/null 2>&1 &
P1=$!
nohup /tmp/worker 3600 >/dev/null 2>&1 &
P2=$!
sudo roamswitch server config set protected_processes "worker,../x"
sudo roamswitch server config set protected_processes worker
sudo roamswitch server allowlist test "Reverse shell" --pid $P1
sudo roamswitch server allowlist test "Reverse shell" --pid $P2
sudo roamswitch server config set protected_processes /tmp/worker
sudo roamswitch server allowlist test "Reverse shell" --pid $P2
sudo roamswitch server config set protected_processes ""
kill $P1 $P2Error: protected_processes: '../x' is not a process name or an absolute path
✓ Server configuration updated and saved. (protected_processes = worker)
To apply: sudo systemctl restart roamswitch-server or sudo roamswitch server restart
exe: /opt/demo/bin/worker
✓ Freeze/kill protection: Protected (protected_processes in server.conf, by name). The guards never freeze or kill it.
Note: server.conf changed after roamswitch-server started. The verdict above uses protected_processes as server.conf has it now; the running daemon uses it only after a restart (sudo roamswitch server restart).
exe: /tmp/worker
✗ Freeze/kill protection: The name is on a protected list, but the executable is at /tmp/worker, outside the system locations, so it is not protected (this stops a renamed binary from inheriting it).
✓ Server configuration updated and saved. (protected_processes = /tmp/worker)
exe: /tmp/worker
✓ Freeze/kill protection: Protected (protected_processes in server.conf, by executable path). The guards never freeze or kill it.
✓ Server configuration updated and saved. (protected_processes = )The name worker protected only the copy in /opt. The same name in /tmp was not protected. A value such as ../x is refused and not saved.
With --pid, the check uses where the running process's executable really is. That is more reliable than asking by name.
allowlist test reads protected_processes from the saved server.conf each time it runs. The daemon reads it only when it starts (checked in the code).
Right after saving, test answers "Protected", but the running daemon does not apply it yet. That is why test ends with "Note: server.conf changed after roamswitch-server started". After saving, run sudo roamswitch server restart. After the restart, test no longer printed the note. The allowlist, by contrast, applies without a restart.
- Put a path under
/tmpor similar inprotected_processes. The lab above does it only to compare. Anyone can replace a file in a world-writable place - Keep using a name-only allowlist entry. When the warning appears, add it again with
--exeandremovethe name-only one - Pick the longest end date without thinking. The end date is your review date
#Accept a CVE with a reason and an end date
Matching packages against known vulnerabilities (roamswitch scan-packages) checks the installed packages against a local map of known CVEs. It makes no network connection. roamswitch-updater delivers the map. Until it arrives, nothing is found.
Vulnerability acceptance takes one finding out of the "needs action" count, with a reason and an end date. The finding stays in the list.
This writes an acceptance record (it changes the machine). To undo, use --revoke.
roamswitch-update.service fetches the map once at install. An upgrade from an earlier version fetches it too when it is missing. On this VM it had arrived during the upgrade. If it has not, run sudo systemctl start roamswitch-update.service once.
roamswitch scan-packages
roamswitch vuln-accept Ubuntu pyjwt CVE-2026-102274 --version 2.7.0-1ubuntu0.3
roamswitch vuln-accept Ubuntu pyjwt CVE-2026-102274 --version 2.7.0 --reason "x"
roamswitch vuln-accept Ubuntu pyjwt CVE-2026-102274 --version 2.7.0-1ubuntu0.3 --days 7 --reason "No app uses PyJWKSet; upgrade in the next maintenance window"
roamswitch vuln-accept --list
roamswitch scan-packages | grep -A3 "CVE-2026-102274"
roamswitch vuln-accept --revoke Ubuntu pyjwt CVE-2026-102274 --version 2.7.0 Needs action: 15 / accepted: 0 (of which ending within 14 days: 0) / acceptance ran out and counted open again: 0
CVE-2026-111497 [Ubuntu 24.04] brotli 1.1.0-2build2 (CVSS 9.8)
KEV CISA has confirmed that this vulnerability is being exploited in the wild.
EPSS 83% (2026-11-09) (an estimate of how likely exploitation is, not a confirmation that it is exploited)
📦 Installed as: libbrotli1
Fixed in: 1.1.0-2ubuntu0.1
CVE-2026-102274 [Ubuntu 24.04] pyjwt 2.7.0-1ubuntu0.3 (CVSS 7.5)
EPSS 0.5% (2026-11-09) (an estimate of how likely exploitation is, not a confirmation that it is exploited)
📦 Installed as: python3-jwt
Fixed in: 2.7.0-1ubuntu0.4
A reason is required. Give it with --reason.
Note: CVE-2026-102274 for Ubuntu pyjwt 2.7.0 is not in today's scan result. The acceptance is recorded, but the version or the CVE ID may be wrong, or the package may not be installed. Compare it with the output of roamswitch scan-packages (--revoke removes a wrong one).
Accepted: Ubuntu pyjwt 2.7.0 CVE-2026-102274. It is left out of the count of findings that need action until 2027-02-10 (it stays in the list). It does not cover any other version. When the date passes it counts as open again and a record is written.
Accepted: Ubuntu pyjwt 2.7.0-1ubuntu0.3 CVE-2026-102274. It is left out of the count of findings that need action until 2026-11-19 (it stays in the list). It does not cover any other version. When the date passes it counts as open again and a record is written.
Ubuntu pyjwt CVE-2026-102274 (2.7.0-1ubuntu0.3)
until: 2026-11-19 (7 days left)
reason: No app uses PyJWKSet; upgrade in the next maintenance window
recorded by: admin (2026-11-12)
Ubuntu pyjwt CVE-2026-102274 (2.7.0)
until: 2027-02-10 (90 days left)
reason: x
recorded by: admin (2026-11-12)
CVE-2026-102274 [Ubuntu 24.04] pyjwt 2.7.0-1ubuntu0.3 (CVSS 7.5)
EPSS 0.5% (2026-11-09) (an estimate of how likely exploitation is, not a confirmation that it is exploited)
Accepted (until 2026-11-19): No app uses PyJWKSet; upgrade in the next maintenance window
📦 Installed as: python3-jwt
Acceptance removed: Ubuntu pyjwt 2.7.0 CVE-2026-102274. The finding is counted as open again.Write the package name and version exactly as the scan shows them. On Ubuntu that is the source package (pyjwt) and its version, not the installed package name (python3-jwt).
Without a reason nothing is recorded and the exit code is 2. A wrong version is still recorded, but a note appears and it covers no finding. Remove it with --revoke. Without --days, the end date is 90 days. The longest is 365.
An accepted finding does not leave the list. "Accepted" and the reason appear under it.
If the scan shows a "Fixed in" line, think about upgrading first. An acceptance records that you cannot upgrade yet, or that you checked and it does not affect you.
- A fixed version exists: upgrade. If you accept, use a short end date that reaches the next upgrade
- No fixed version: follow An actively exploited vulnerability has no fix yet and check how the package is used before you decide
- A KEV finding (exploitation confirmed) with a fixed version: upgrade instead of accepting
An acceptance can still be recorded for a KEV finding that has a fix, but a warning appears. On the VM, accepting the CVSS 9.8 KEV finding for brotli printed this line.
Warning: CISA has confirmed that CVE-2026-111497 is being exploited in the wild (KEV), and the fixed version 1.1.0-2ubuntu0.1 is out. Consider updating before accepting it.Stopping there is up to the person who records it. After the test, revoke it with roamswitch vuln-accept --revoke Ubuntu brotli CVE-2026-111497 --version 1.1.0-2build2.
#What happens at the end date
Both an allowlist entry and an acceptance stop applying at their end date. The records stay in the lists. Here you move the VM's clock forward to make them expire.
Allowlist and acceptance expiry is judged against the latest time the host has seen. Setting the clock back alone does not bring an expired entry back. Once NTP has synced the clock, entries apply until their real end date again (checked in the lab below). Even so, moving the clock forward on a real server ends entries all at once until the clock is synced. The notification history also drops records older than 7 days by the moved clock, and they do not come back when the clock is set back.
This changes the system clock (it changes the machine). The last two lines put it back.
You use the allowlist entry (rsync) and the acceptance (pyjwt) from the earlier labs, both ending on 2026-11-19.
sudo timedatectl set-ntp false
sudo date -s "+8 days"
roamswitch vuln-accept --list
roamswitch scan-packages | sed -n 7,8p
sudo roamswitch server allowlist list
sudo roamswitch server allowlist test "Outbound connection to C2" rsync --exe /usr/bin/rsync
roamswitch notifications Ubuntu pyjwt CVE-2026-102274 (2.7.0-1ubuntu0.3)
until: 2026-11-19 (expired (counted as open again))
Needs action: 15 / accepted: 0 (of which ending within 14 days: 0) / acceptance ran out and counted open again: 1
Acceptance ran out: Ubuntu pyjwt 2.7.0-1ubuntu0.3 CVE-2026-102274 (recorded)
rule: Outbound connection to C2
process: rsync
exe: /usr/bin/rsync
ends: 2026-11-19 (ended: the allowance no longer applies)
✗ Allowlist: The allowance has ended, so the guards act as usual (renew extends it).
Result: neither allowed nor protected. It can be contained according to the guard policy (guard.yaml).
2026-11-20T01:31:41.302984052+00:00
Risk acceptance expired
Ubuntu pyjwt 2.7.0-1ubuntu0.3, CVE-2026-102274: accepted until 2026-11-19. It is counted as open again.
It is not in the incident timeline yet (only root can write the timeline). The next sudo roamswitch scan-packages records it there. It is recorded here, in the notification history.An expired acceptance is counted apart, as "acceptance ran out and counted open again". It is also written once to the notification history. A scan run as an ordinary user does not write it to the timeline. Running sudo roamswitch scan-packages afterwards added a [Risk Acceptance] line to sudo roamswitch timeline.
An expired allowlist entry is listed as "ended". The guards act as if it were not there. To keep it, extend it with renew; if you no longer need it, remove it.
timedatectl set-ntp false removes the systemd-timesyncd unit link (/etc/systemd/system/dbus-org.freedesktop.timesync1.service). A few seconds later, a critical-file tampering notification arrived for it. You made the change yourself, so it is fine here.
Set the clock back and turn NTP sync on again. Wait until it has synced, then check once more.
sudo date -s "-8 days"
sudo roamswitch server allowlist test "Outbound connection to C2" rsync --exe /usr/bin/rsync | grep 'Allowlist'
sudo timedatectl set-ntp true
until timedatectl show -p NTPSynchronized --value | grep -q yes; do sleep 2; done
sudo roamswitch server allowlist test "Outbound connection to C2" rsync --exe /usr/bin/rsync | grep 'Allowlist'
roamswitch vuln-accept --list | grep until ✗ Allowlist: The allowance has ended, so the guards act as usual (renew extends it).
✓ Allowlist: This rule and process pair is allowlisted. The guards take no action.
until: 2026-11-19 (7 days left)Setting the clock back alone left the entry expired. Once NTP had synced the clock, both the entry and the acceptance applied again until their real end date (2026-11-19).
#Alerts you should not silence
Use these tools when you can explain why an alert is a false positive. When you cannot, investigate instead.
During the labs, running /tmp/worker made Process execution recording and attack-technique detection send one notification.
🔎 Suspicious execution pattern detected
Execution from a temp directory, deleted file or memfd (EXR-002)
Process: /tmp/worker (PID: 1246) / triage: Medium
Details: execution from a world-writable directory, category: tmp, path: /tmp/worker
Next step: sudo roamswitch events tree 1246
(notification only — nothing was blocked)
Process: admin / /tmp/worker / command: /tmp/worker 3600 (pid 1246)
Parent processes: bash ← script ← dash ← tart-guest-agent ← systemdIt also names the user (admin), the command and the chain of parent processes. Here you placed the file yourself, so you can explain it. On a real server, if you cannot, first see what started it with sudo roamswitch events tree <PID>.
- Can you explain what ran and why? Software you installed, running at a set time and doing a set thing, can be explained
- Is the executable in a system location, or a place you manage? If it is in a world-writable place such as
/tmpor/dev/shm, do not silence it - Can you write it narrowly? For the allowlist, add
--exeand keep the end date short. By design, the allowlist is written as rule and process pairs. There is no way to silence a whole rule
If any answer is "no", investigate instead.
- Execution from a temporary directory that you cannot explain. It is a common way in for attackers
- A KEV finding that has a fixed version. Upgrade instead of accepting
- Containment of a process you do not remember installing. Before you allowlist it, check what started it with
sudo roamswitch events tree <PID> - A detection that triggered host isolation. It is the most serious stage. Do not allowlist it to stop it next time before you know the cause
#What you cannot see
On a disposable Ubuntu 24.04 VM, adding, checking, renewing and removing allowlist entries, the protected_processes verdicts, and CVE acceptance and its expiry were all run for real.
- No real detection or containment was triggered. The eBPF guard's sensor (Falco or Tetragon) does not run on this VM. The effect of allowing and protecting was read from the
allowlist testverdict - A detection of an allowlisted pair is still notified and recorded (checked in the code). The allowlist is not a way to make alerts quiet
allowlist testanswers from the saved settings.protected_processesapplies only after the daemon restarts- Accepting a CVE does not lower the risk. Upgrading, removing or stopping the software does
You see "this is a name-only allowlist entry". What is the right response?
Show the explanation
A process name can be spoofed just by renaming. With
--exe, a same-named program elsewhere is not covered. In the lab, test showed that the entry did not apply to/tmp/rsync.
Right after saving protected_processes, test answers "Protected". Which is true?
Show the explanation
Saving the setting also prints "To apply: sudo systemctl restart roamswitch-server". Unlike the allowlist,
protected_processesis read when the daemon starts.
#What to learn next
- How to handle a vulnerability with no fix is in An actively exploited vulnerability has no fix yet
- What to do after containment fires is in A containment fired: the first hour
- Choosing the automatic isolation policy itself is covered in "Setting your automatic isolation policy" (coming soon)
- Every setting is listed in the Linux Server operations manual
Verified on 2026-11-12. Target: Linux 1.17.39. On a disposable Ubuntu 24.04 VM with the published Linux 1.17.39 (starting with no server.conf), ran allowlist add/test/renew/remove, protected_processes name and path verdicts and test after a restart, scan-packages with vuln-accept accept/revoke (including the KEV warning), expiry after moving the clock 8 days with the timeline record, and the entries applying again until their real end date once the clock was set back and synced by NTP, in Japanese and English, and re-read notices recorded with language=ja under LANG=en; that allowlisted pairs are still notified and when the daemon reads each setting was checked in the source.
Command output varies with the product version and the environment. If yours differs, check the version first.