Falco on a host

Rules, drivers and tuning alerts.

Advanced14 min · lesson 10 of 15

Falco is a runtime security sensor from the CNCF (Cloud Native Computing Foundation). It watches system calls on a host as they happen, evaluates them against rules, and raises an alert the moment one matches: a sensitive file read, a write into a persistence location, an unexpected shell. It is often met inside Kubernetes, but it runs just as well on a plain server. In this lesson you install Falco 0.45.0 from the vendor's repository on Ubuntu 26.04, check which driver it uses and what it opened, read the rules it ships, trigger a default rule with an ordinary admin action, write a local rule, tune noise with an exception, and remove Falco cleanly afterwards.

How Falco sees the host

Falco needs a tap on the kernel's system-call stream, and current releases offer two. The default is the modern eBPF probe: eBPF programs compiled into Falco itself and adapted to the running kernel through BTF (linux-det/ebpf explains both), so nothing is built on the host and no kernel headers are needed. It requires a kernel with BTF and the BPF ring buffer, which in practice means 5.8 or newer; both lab kernels qualify. The alternative is a kernel module, for older kernels, which must match the exact kernel and is loaded into the most privileged part of the system. The older "legacy" eBPF probe was removed in Falco 0.44.0.

From a system call to an alert
1A process makes a syscall
openat, execve, connect
2Modern eBPF probe
records it in the kernel
3Ring buffer to user space
one per two CPUs on this host
4Rules engine
conditions over fields, first match wins
5Alert with fields
process, parent, user, loginuid, file
6Outputs
stdout to the journal, syslog, file, HTTP
Alerts are only as useful as where they go: ship them off the host.

Install from the vendor's repository

Falco's packages come from download.falco.org, signed with the project's key. Store the key in its own keyring, check the fingerprint against the one on falco.org's installation page, and name the keyring with signed-by so it vouches for this repository only (the pattern from "Packages and updates" in Linux essentials).

deploy@web01 · Ubuntu 26.04 LTS
$ curl -fsSL https://falco.org/repo/falcosecurity-packages.asc | sudo gpg --dearmor -o /usr/share/keyrings/falco-archive-keyring.gpg gpg --show-keys /usr/share/keyrings/falco-archive-keyring.gpg
… pub rsa4096 2025-12-11 [SC] [expires: 2028-12-10] 478B2FBBC75F4237B731DA4365106822B35B1B1F uid Falcosecurity Package Signing <cncf-falco-dev@lists.cncf.io>
$ echo "deb [signed-by=/usr/share/keyrings/falco-archive-keyring.gpg] https://download.falco.org/packages/deb stable main" | sudo tee /etc/apt/sources.list.d/falcosecurity.list
deb [signed-by=/usr/share/keyrings/falco-archive-keyring.gpg] https://download.falco.org/packages/deb stable main
$ sudo apt-get update >/dev/null; apt-cache policy falco | head -3
falco: Installed: (none) Candidate: 0.45.0

The fingerprint 478B2FBBC75F4237B731DA4365106822B35B1B1F matches the one Falco publishes, and the key expires in December 2028, so plan to refresh it. The candidate is 0.45.0, released on 2026-09-21. The package normally asks in a text menu which driver to use; three documented variables answer instead: FALCO_FRONTEND=noninteractive skips the menu, FALCO_DRIVER_CHOICE=modern_ebpf picks the driver, and FALCOCTL_ENABLED=no turns off automatic rule updates, which the lab does so the rules you read below cannot change mid-lesson.

deploy@web01 · Ubuntu 26.04 LTS
$ sudo env FALCO_FRONTEND=noninteractive FALCO_DRIVER_CHOICE=modern_ebpf FALCOCTL_ENABLED=no apt-get install -y falco
… The following NEW packages will be installed: falco 0 upgraded, 1 newly installed, 0 to remove and 6 not upgraded. … Get:1 https://d20hasrqv82i0q.cloudfront.net/packages/deb stable/main arm64 falco arm64 0.45.0 [46.5 MB] … Unpacking falco (0.45.0) ... Setting up falco (0.45.0) ... [POST-INSTALL] Disable all possible 'falco' services: [POST-INSTALL] Configure falcoctl 'modern_ebpf' driver type: … Created symlink '/etc/systemd/system/falcoctl-artifact-follow.service' → '/dev/null'. [POST-INSTALL] Trigger deamon-reload: [POST-INSTALL] Enable 'falco-modern-bpf.service': Created symlink '/etc/systemd/system/falco.service' → '/usr/lib/systemd/system/falco-modern-bpf.service'. Created symlink '/etc/systemd/system/multi-user.target.wants/falco-modern-bpf.service' → '/usr/lib/systemd/system/falco-modern-bpf.service'. [POST-INSTALL] Start 'falco-modern-bpf.service': …
$ systemctl list-unit-files "falco*"
UNIT FILE STATE PRESET falco-custom.service disabled enabled falco-kmod-inject.service static - falco-kmod.service disabled enabled falco-modern-bpf.service enabled enabled falco.service alias - falcoctl-artifact-follow.service masked enabled 6 unit files listed.

The package is the only new one; the dkms, headers and dialog packages that Falco's install page also lists are needed for the kernel module and the menu, not for the modern probe. Its post-install script configured the modern_ebpf driver, masked falcoctl-artifact-follow.service (the rule updater, linked to /dev/null), enabled falco-modern-bpf.service with falco.service as an alias, and started it. The kmod and custom units stay disabled; exactly one Falco service should run.

Next, check the version, the driver and what the service opened.

deploy@web01 · Ubuntu 26.04 LTS
$ falco --version
… Sun Sep 27 08:58:11 2026: Falco initialized with configuration files: Sun Sep 27 08:58:11 2026: /etc/falco/config.d/engine-kind-falcoctl.yaml | schema validation: ok Sun Sep 27 08:58:11 2026: /etc/falco/config.d/falco.container_plugin.yaml | schema validation: ok Sun Sep 27 08:58:11 2026: /etc/falco/falco.yaml | schema validation: ok … Falco version: 0.45.0 Libs version: 0.26.0 Plugin API: 3.12.0 Engine: 0.65.0 Driver: API version: 11.0.0 Schema version: 4.3.0 Default driver: 11.0.0+driver

Falco 0.45.0 with libs 0.26.0 and driver API 11.0.0, and the configuration files it read: the installer's config.d/engine-kind-falcoctl.yaml (the driver choice), a container plugin file, and falco.yaml. A version line proves the package is installed, not that the sensor is working. The service's own log for its current run does.

deploy@web01 · Ubuntu 26.04 LTS
$ ID=$(systemctl show -p InvocationID --value falco-modern-bpf) sudo journalctl _SYSTEMD_INVOCATION_ID=$ID -o cat | grep -E "Opening|ring buffer|event sources|webserver"
Starting health webserver with threadiness 2, listening on 0.0.0.0:8765 Loaded event sources: syscall Enabled event sources: syscall Opening 'syscall' source with modern BPF probe. One ring buffer every '2' CPUs. …
$ ID=$(systemctl show -p InvocationID --value falco-modern-bpf) sudo journalctl _SYSTEMD_INVOCATION_ID=$ID -o cat | grep -m2 "TOCTOU"
[libs]: libpman: failure while attaching TOCTOU mitigation program for 'creat' system call. Detection will continue to work, but TOCTOU mitigation may not properly work (errno: 2 | message: No such file or directory) [libs]: libpman: failure while attaching TOCTOU mitigation program for 'open' system call. Detection will continue to work, but TOCTOU mitigation may not properly work (errno: 2 | message: No such file or directory)
$ sudo ss -tlnp | grep 8765
LISTEN 0 5 0.0.0.0:8765 0.0.0.0:* users:(("falco",pid=163650,fd=10))

"Opening 'syscall' source with modern BPF probe" is the line that confirms capture started with the intended driver. Matching _SYSTEMD_INVOCATION_ID to the unit's current InvocationID limits the journal to this run of the service, so messages from an earlier start cannot mislead you. The two TOCTOU (time-of-check, time-of-use) notices are specific to aarch64: that architecture has no open or creat system calls, only openat, so the extra programs Falco attaches to cross-check those calls have nothing to attach to, and detection continues. Last, installing a sensor added a network listener: Falco's health web server on 0.0.0.0:8765, reachable from any network the host is on. If nothing remote needs it, set webserver.listen_address to 127.0.0.1 or disable the web server in a file under /etc/falco/config.d.

The rules and the configuration that loads them

deploy@web01 · Ubuntu 26.04 LTS
$ ls /etc/falco /etc/falco/rules.d /etc/falco/config.d
/etc/falco: config.d falco.yaml falco_rules.local.yaml falco_rules.yaml rules.d /etc/falco/config.d: engine-kind-falcoctl.yaml falco.container_plugin.yaml /etc/falco/rules.d:
$ grep -A4 "^rules_files:" /etc/falco/falco.yaml
rules_files: - /etc/falco/falco_rules.yaml - /etc/falco/falco_rules.local.yaml - /etc/falco/rules.d
$ grep -E "^(priority|json_output|watch_config_files):" /etc/falco/falco.yaml grep -E -A3 "^(stdout|syslog|file|http)_output:" /etc/falco/falco.yaml | grep -E "_output:|enabled:"
watch_config_files: true priority: debug json_output: false stdout_output: enabled: true syslog_output: enabled: true file_output: enabled: false http_output: enabled: false
$ grep -c "^- rule:" /etc/falco/falco_rules.yaml
25

falco.yaml loads falco_rules.yaml (the vendor's rules), then falco_rules.local.yaml, then every file in rules.d, in that order. priority: debug means rules of every priority are active. watch_config_files: true makes Falco reload itself when a configuration or rules file changes. With stdout_output and syslog_output enabled, alerts land in the journal of falco-modern-bpf.service. The package ships 25 rules, the project's stable set; more experimental rule sets are published separately. Here is the condition of one of them.

deploy@web01 · Ubuntu 26.04 LTS
$ grep -A30 "^- rule: Read sensitive file untrusted" /etc/falco/falco_rules.yaml | sed -n "/condition:/,/^- /p"
condition: > open_read and sensitive_files and proc_name_exists and not proc.name in (user_mgmt_binaries, userexec_binaries, package_mgmt_binaries, cron_binaries, read_sensitive_file_binaries, shell_binaries, hids_binaries, vpn_binaries, mail_config_binaries, nomachine_binaries, sshkit_script_binaries, in.proftpd, mandb, salt-call, salt-minion, postgres_mgmt_binaries, google_oslogin_ ) and not cmp_cp_by_passwd … and not runuser_reading_pam and not linux_bench_reading_etc_shadow

A condition is a boolean expression over event fields. open_read, sensitive_files and proc_name_exists are macros, named conditions defined elsewhere in the file (sensitive_files includes /etc/shadow), and names like user_mgmt_binaries are lists of programs trusted to read such files. Every and not ... line is an exception someone needed. Never edit falco_rules.yaml to add your own: the package upgrade, and the rule updater if you enable it, replace that file. Your rules and exceptions go in rules.d or falco_rules.local.yaml. By default Falco stops at the first rule that matches an event (rule_matching: first), so load order also decides which alert you see.

An alert from ordinary admin work, and the noise around it

Before triggering anything, look at what the default rules already report on this host.

deploy@web01 · Ubuntu 26.04 LTS
$ ID=$(systemctl show -p InvocationID --value falco-modern-bpf) sudo journalctl _SYSTEMD_INVOCATION_ID=$ID -o cat | grep -m1 "Sensitive file"
08:58:09.410403453: Warning Sensitive file opened for reading by non-trusted program | file=/etc/shadow gparent=bash ggparent=sudo gggparent=sshd-session evt_type=openat user=<NA> user_uid=4294967295 user_loginuid=1001 process=runuser proc_exepath=/usr/sbin/runuser parent=bash command=runuser -l deploy -c systemctl list-unit-files \"falco*\" terminal=0 container_id=host container_name=host container_image_repository= container_image_tag= k8s_pod_name=<NA> k8s_ns_name=<NA>

Every command in this lab reaches the host through runuser -l deploy -c ..., a tool that runs a command as another account. Its PAM stack reads /etc/shadow, and runuser is not on the rule's trusted lists (the runuser_reading_pam macro above only excuses its reads of /etc/pam.d), so every lab step raised this alert. user=<NA> and user_uid=4294967295 (the unsigned form of -1) mean Falco had no user information for that process; the login identity it did have. Working over an ordinary SSH session you will probably not see this alert at all; automation that uses runuser produces it on real servers, and the Try this below gives you noise of your own to tune. The fix is an exception for that exact behaviour, not a quieter rule. The same local file can add a rule of your own, here for files appearing in the cron directories, a persistence location.

/etc/falco/rules.d/secopslog-local.yaml
# Local additions. Files in rules.d load after falco_rules.yaml and survive package upgrades.
# runuser (used by automation to run commands as another account) reads /etc/shadow through PAM.
- rule: Read sensitive file untrusted
exceptions:
- name: runuser_pam_shadow
fields: [proc.exepath, fd.name]
comps: [=, =]
values:
- [/usr/sbin/runuser, /etc/shadow]
override:
exceptions: append
# A file written, moved or linked into a cron location is a persistence location changing.
- list: secopslog_cron_paths
items: [/etc/crontab, /etc/cron.d, /etc/cron.hourly, /etc/cron.daily, /etc/cron.weekly, /etc/cron.monthly, /var/spool/cron]
- rule: Cron file written
desc: A process wrote, renamed or linked a file into a cron location
condition: >
(open_write and fd.name pmatch (secopslog_cron_paths))
or (evt.type in (rename, renameat, renameat2, link, linkat)
and fs.path.target pmatch (secopslog_cron_paths))
output: Cron file written (evt=%evt.type file=%fd.name target=%fs.path.target process=%proc.name parent=%proc.pname user=%user.name loginuid=%user.loginuid command=%proc.cmdline)
priority: WARNING
tags: [host, persistence]

The first entry names an existing rule and uses override: exceptions: append to add one exception to it; the older append: true form is deprecated. The exception matches only when both fields match: the program's full path (proc.exepath, which an attacker cannot fake without writing to /usr/sbin, unlike a process name) and the file. The rest is a new rule. The list names every cron location, and pmatch matches a path equal to or below any of them. The first clause, built on the shipped open_write macro, catches a file opened for writing there. That alone is easy to walk around: write the file anywhere else on the same filesystem and mv it in, and the kernel performs a rename, which opens nothing, so an open-based rule never fires (a hard link with ln is the same). The second clause covers those system calls, using fs.path.target, the resolved destination path. Validate against the shipped rules, since the local file uses their macros, then restart and check what loaded.

deploy@web01 · Ubuntu 26.04 LTS
$ sudo falco -V /etc/falco/falco_rules.yaml -V /etc/falco/rules.d/secopslog-local.yaml
… Sun Sep 27 08:58:14 2026: Validating rules file(s): Sun Sep 27 08:58:14 2026: /etc/falco/falco_rules.yaml Sun Sep 27 08:58:14 2026: /etc/falco/rules.d/secopslog-local.yaml /etc/falco/falco_rules.yaml: Ok /etc/falco/rules.d/secopslog-local.yaml: Ok
$ sudo systemctl restart falco-modern-bpf
$ sudo journalctl -u falco-modern-bpf -o cat --since "-20s" | grep -A3 "Loading rules from" | tail -4
Loading rules from: /etc/falco/falco_rules.yaml | schema validation: ok /etc/falco/falco_rules.local.yaml | schema validation: none /etc/falco/rules.d/secopslog-local.yaml | schema validation: ok

Both files validate and the running Falco now reads the local file. (Falco had in fact already reloaded when the file appeared, because of watch_config_files; the restart makes the moment explicit.) Now do ordinary admin things: read /etc/shadow as root, write a placeholder file into /etc/cron.d, and move a second one in from /var/tmp.

deploy@web01 · Ubuntu 26.04 LTS
$ sudo cat /etc/shadow >/dev/null echo "# fa-demo placeholder, no schedule" | sudo tee /etc/cron.d/fa-demo
# fa-demo placeholder, no schedule
$ echo "# fa-demo placeholder, no schedule" > /var/tmp/fa-staged sudo mv /var/tmp/fa-staged /etc/cron.d/fa-moved
$ ID=$(systemctl show -p InvocationID --value falco-modern-bpf) sudo journalctl _SYSTEMD_INVOCATION_ID=$ID -o cat | grep -E "Sensitive file|Cron file written"
08:58:18.725418497: Warning Sensitive file opened for reading by non-trusted program | file=/etc/shadow gparent=bash ggparent=runuser gggparent=bash evt_type=openat user=root user_uid=0 user_loginuid=1001 process=cat proc_exepath=/usr/lib/cargo/bin/coreutils/cat parent=sudo command=cat /etc/shadow terminal=0 container_id=host container_name=host container_image_repository= container_image_tag= k8s_pod_name=<NA> k8s_ns_name=<NA> 08:58:18.730138903: Warning Cron file written (evt=openat file=/etc/cron.d/fa-demo target=<NA> process=tee parent=sudo user=root loginuid=1001 command=tee /etc/cron.d/fa-demo) container_id=host container_name=host container_image_repository= container_image_tag= k8s_pod_name=<NA> k8s_ns_name=<NA> 08:58:18.748151072: Warning Cron file written (evt=renameat2 file=<NA> target=/etc/cron.d/fa-moved process=mv parent=sudo user=root loginuid=1001 command=mv /var/tmp/fa-staged /etc/cron.d/fa-moved) container_id=host container_name=host container_image_repository= container_image_tag= k8s_pod_name=<NA> k8s_ns_name=<NA>

Three alerts from this run of the service, and the runuser noise is gone. The first is the default rule doing its job: process=cat with parent=sudo, user=root, and user_loginuid=1001, the login identity (auid, linux-det/auditpipe) of deploy behind the sudo. proc_exepath is uutils' cat, and the gparent and ggparent shells belong to the lab's harness. The other two are the local rule. evt=openat is tee, run through sudo by login 1001, writing /etc/cron.d/fa-demo. evt=renameat2 is the mv: no file was opened, so file=<NA>, and the destination is in target=. Without the second clause that move would have passed in silence. Each alert carries the fields you need to decide whether it was expected. A root read of /etc/shadow by an administrator is often legitimate; the rule cannot know that, and your triage can.

Tune by exception, never by volume
When a rule fires on something you have verified as benign, add the narrowest exception that describes it (full executable path plus file, or plus parent) in rules.d. Lowering the rule's priority, raising priority: in falco.yaml, or disabling the rule removes the detection for every other process too, and an edit to falco_rules.yaml disappears at the next upgrade.

When Falco loses events or goes quiet

A sensor that is running is not necessarily a sensor that sees everything. The probe hands events to Falco through ring buffers in memory; when Falco's user-space side cannot keep up (a burst of system calls, a starved CPU), the buffer fills and new events are dropped, and no rule can match an event it never received. Falco's CPU cost grows with the host's system-call rate, so the busiest hosts are the likeliest to drop. Two settings govern this.

deploy@web01 · Ubuntu 26.04 LTS
$ sed -n "/^syscall_event_drops:/,/^[a-z]/p" /etc/falco/falco.yaml | grep -vE "^ *#|^$" | head -8 grep -A4 "^ modern_ebpf:" /etc/falco/falco.yaml
syscall_event_drops: threshold: .1 actions: - log - alert rate: .03333 max_burst: 1 simulate_drops: false modern_ebpf: cpus_for_each_buffer: 2 buf_size_preset: 4 drop_failed_exit: false disable_iterators: false

Falco checks its drop counters once a second. When dropped events exceed threshold (a fraction: .1 is 10% of that second's events), it takes the listed actions: log writes a debug-level log line, alert raises an alert named "Falco internal: syscall event drop" through the normal outputs, and rate with max_burst allow one such message every 30 seconds. So by default a host losing 5% of its events every second never says so; set threshold: 0 if you want to hear about any loss. The modern_ebpf block sizes the ring buffers: one buffer shared by every two CPUs, at buf_size_preset: 4; a larger preset trades memory for fewer drops. Whether Falco is still running at all is a separate question.

deploy@web01 · Ubuntu 26.04 LTS
$ systemctl show falco-modern-bpf -p ActiveState,Restart,NRestarts curl -s http://127.0.0.1:8765/healthz; echo
ActiveState=active Restart=on-failure NRestarts=0 {"status": "ok"}

systemd restarts the service if it exits with an error (Restart=on-failure), and NRestarts counts how often that has happened, which is worth collecting: a probe that fails to load after a kernel update, for example, makes Falco exit. /healthz on the web server answers ok while Falco runs. To see what a drop alert looks like without overloading a host, Falco can pretend: simulate_drops adds one dropped event every second. Put it in a file under config.d with threshold: 0, since one simulated drop is far below 10%, and with the action list spelled out: in the lab, a file that set only those two keys produced no alert.

deploy@web01 · Ubuntu 26.04 LTS
$ printf "syscall_event_drops:\n threshold: 0\n actions:\n - alert\n simulate_drops: true\n" | sudo tee /etc/falco/config.d/zz-simulate-drops.yaml sudo systemctl restart falco-modern-bpf
syscall_event_drops: threshold: 0 actions: - alert simulate_drops: true
$ ID=$(systemctl show -p InvocationID --value falco-modern-bpf) sudo journalctl _SYSTEMD_INVOCATION_ID=$ID -o cat | grep -m1 "Falco internal: syscall event drop"
08:58:31.788555524: Debug Falco internal: syscall event drop. 1 system calls dropped in last second. (ebpf_enabled="1" n_drops="1" n_drops_buffer_clone_fork_exit="0" n_drops_buffer_close_exit="0" n_drops_buffer_connect_exit="0" n_drops_buffer_dir_file_exit="0" n_drops_buffer_execve_exit="0" n_drops_buffer_open_exit="0" n_drops_buffer_other_interest_exit="0" n_drops_buffer_proc_exit="0" n_drops_buffer_total="0" n_drops_bug="0" n_drops_page_faults="0" n_drops_scratch_map="0" n_evts="1449")
$ sudo rm /etc/falco/config.d/zz-simulate-drops.yaml sudo systemctl restart falco-modern-bpf

The alert arrives at Debug priority, through the same outputs as every other alert, with the counters that explain it: n_drops="1" out of n_evts="1449" in that second, and the n_drops_buffer_* fields that say which kind of event was lost (all zero here, because the drop was simulated). A real one on a busy host is the cue to raise buf_size_preset, give Falco more CPU, or narrow the rules. Remove the file and restart once you have seen it, or the alert repeats every 30 seconds.

Watch the sensor as well as its alerts
Alerts that stay in the local journal can be deleted by the root intruder they describe, and a Falco that root stops, or whose probe fails to load after a kernel update, raises nothing at all. Send alerts off the host (http_output to a collector, or syslog forwarded as in linux-hard/logging), alert on drop alerts, and alert centrally when a host's Falco goes quiet. Enabling metrics with output_rule: true emits a "Falco internal: metrics snapshot" at a fixed interval, with kernel-side event and drop counters, which doubles as a heartbeat.

Stop and remove Falco

A sensor you are only evaluating should not keep running, or keep its listener open. Stop it, purge the package, and remove what the package does not own.

deploy@web01 · Ubuntu 26.04 LTS
$ sudo systemctl stop falco-modern-bpf; systemctl is-active falco-modern-bpf
inactive
$ sudo apt-get purge -y falco
… The following packages will be REMOVED: falco* 0 upgraded, 0 newly installed, 1 to remove and 6 not upgraded. … Removing falco (0.45.0) ... [PRE-REMOVE] Stop all Falco services: [PRE-REMOVE] Call 'falcoctl driver cleanup:' … Removed '/etc/systemd/system/multi-user.target.wants/falco-modern-bpf.service'. Removed '/etc/systemd/system/falco.service'. Removed '/etc/systemd/system/falcoctl-artifact-follow.service'. … Purging configuration files for falco (0.45.0) ... dpkg: warning: while removing falco, directory '/etc/falco/rules.d' not empty so not removed dpkg: warning: while removing falco, directory '/etc/falco/config.d' not empty so not removed
$ sudo rm -r /etc/falco /etc/apt/sources.list.d/falcosecurity.list /usr/share/keyrings/falco-archive-keyring.gpg systemctl list-unit-files "falco*"
UNIT FILE STATE PRESET 0 unit files listed.

The purge stopped the services, removed the unit links, and warned that /etc/falco/rules.d and /etc/falco/config.d were not empty: dpkg never deletes files it did not install, such as your local rule and the installer's driver choice. Removing /etc/falco, the repository line and the keyring finishes the job, and no Falco unit is left (systemctl exits 1 because nothing matched).

Try this

Install Falco on a lab host as above, add the local rules file, and wait for "Opening 'syscall' source" in its journal. Write a two-line script /usr/local/sbin/shadow-hash (#!/bin/sh and sha256sum /etc/shadow >/dev/null), make it executable, run it with sudo, and find the alert with sudo journalctl -u falco-modern-bpf -o cat --since -1min | grep process=sha256sum: parent=shadow-hash, and on Ubuntu 26.04 proc_exepath=/usr/lib/cargo/bin/coreutils/sha256sum, because uutils provides the command. Add a second exception to rules.d with fields [proc.exepath, proc.pname, fd.name] and the values from that alert (/usr/lib/cargo/bin/coreutils/sha256sum, shadow-hash, /etc/shadow), validate it with falco -V, restart, and confirm the script no longer alerts while sudo cat /etc/shadow still does. Copy proc_exepath from the alert rather than guessing it: /usr/bin/sha256sum would not match. Stop and purge Falco when you are done.

Takeaway

Confirm from the service's own log that Falco opened the syscall source, keep local rules and the narrowest exceptions in rules.d, and test each rule against the obvious way around it, such as mv instead of a write. Then watch the sensor itself: drop alerts, restarts, and a host whose alerts or metrics stop arriving.

Quick check
01The "Read sensitive file untrusted" rule fires every night when your backup agent, /opt/backup/bin/agent, reads /etc/shadow. What is the right change?
Correct — The exception removes only the verified behaviour; every other program reading /etc/shadow still alerts at the rule's priority.
Incorrect — That silences every warning-level rule on the host, which throws away far more coverage than the one known-good read.
Incorrect — falco_rules.yaml belongs to the package; an upgrade or the rule updater replaces it and your change disappears.
Incorrect — Disabling it also stops the rule catching anything else that reads /etc/shadow on the hosts that hold backups.
02A colleague's exception for a noisy rule matches proc.name = backup. Why is proc.exepath a better field to match on?
Incorrect — The process name is limited to 15 characters, not eight, and the problem here is trust, not length.
Incorrect — Exceptions accept any field; that is why the colleague's version loads, and why it is dangerous.
Correct — A process name is chosen by whoever starts the program; the executable's full path ties the exception to a specific installed file.
Incorrect — Fields are available when the condition is evaluated; the weakness of proc.name is that an attacker controls it.
03After installing Falco, ss -tlnp shows falco listening on 0.0.0.0:8765. What is it, and what should you do about it?
Incorrect — Port 8765 is a listening socket on the host, not an outbound connection; outputs are configured separately.
Correct — A sensor you add is new attack surface too; webserver.listen_address can be 127.0.0.1, or the web server can be disabled.
Incorrect — The ring buffer is shared memory between the kernel and Falco; it has nothing to do with a TCP port.
Incorrect — The gRPC output was removed in Falco 0.44.0; the listener on 8765 is the health web server.

Related