Host rebooted 2026-09-16 17:47 UTC, recreating every container at once and producing 70 alerts in a single burst - all traced to benign init-time patterns that will recur on every future reboot: systemd-executor's PAM reads during boot, firefly-iii/fidi's s6 healthcheck shell, and romm's own loopback redis/app connection at container start.
102 lines
6.2 KiB
YAML
102 lines
6.2 KiB
YAML
# Tune out stock-rule false positives observed on this specific host, so the
|
|
# broad netdata "any Falco match" alarm only pages for things worth paging
|
|
# for. Uses Falco's own user_* customization macros rather than disabling
|
|
# whole rules, so the underlying security check stays active for everything
|
|
# else. Add entries here as new noisy defaults turn up - don't let this list
|
|
# grow into blinding Falco to anything actually meaningful.
|
|
|
|
# "Sensitive file opened for reading by non-trusted program" false-positives:
|
|
# - pg_isready: fired every ~10s from inbox-zero-db's healthcheck touching
|
|
# /etc/shadow - NSS/libc user-lookup behavior during process init, not
|
|
# credential harvesting.
|
|
# - lscr.io/linuxserver/obsidian, lscr.io/linuxserver/calibre: fires once per
|
|
# container start from `sed -i s/CORRUPT_FILE/NOPASSWD/g /etc/sudoers` -
|
|
# linuxserver.io's own s6-init NOPASSWD setup, same class of init-time file
|
|
# touch. Same pattern confirmed on calibre 2026-08-19; scoped per-image
|
|
# rather than by proc.name alone since any other linuxserver.io image will
|
|
# hit this the first time it's added here too.
|
|
# - systemd-executor (container_name=host): fired 63x reading /etc/shadow and
|
|
# every /etc/pam.d/* file during the 2026-09-16 17:47 host reboot, as every
|
|
# service's login/session setup ran at once. Normal PAM stack behavior at
|
|
# boot, not credential harvesting - will recur on every future reboot.
|
|
# Scoped by proc.exepath since proc.name is a meaningless re-exec artifact
|
|
# ("9") for systemd's own executor process during --deserialize.
|
|
- macro: user_known_read_sensitive_files_activities
|
|
condition: >
|
|
(proc.name = pg_isready)
|
|
or (container.image.repository in ("lscr.io/linuxserver/obsidian", "lscr.io/linuxserver/calibre") and proc.name = sed)
|
|
or (proc.exepath = "/usr/lib/systemd/systemd-executor")
|
|
|
|
# "Fileless execution via memfd_create" fires from nvidia-ctk's
|
|
# ldconfig-refresh hook, which runs via memfd_create by design every time
|
|
# an NVIDIA-GPU container (e.g. ollama) starts - normal container-toolkit
|
|
# behavior, not defense evasion. Scoped to the hook's own parent process
|
|
# names rather than the ollama/ollama image, so drift detection stays live
|
|
# for anything else that image does.
|
|
- macro: known_memfd_execution_processes
|
|
condition: (proc.pname in (nvidia-containe, nvidia-ctk))
|
|
|
|
# "Drop and execute new binary in container" (output text: "Executing binary
|
|
# not part of base image") false-positives on three known-benign sources,
|
|
# all confirmed via docker logs falco (2026-08-08 to 2026-08-16):
|
|
# - gitea: re-executes itself for git-shell commands (serv/hook/pre-receive/
|
|
# post-receive) over SSH, which looks identical to a dropped binary.
|
|
# Scoped to proc.name=gitea, not the whole container, so this
|
|
# previously-compromised container still gets checked for any OTHER
|
|
# unexpected binary.
|
|
# - lscr.io/linuxserver/obsidian: Electron's normal /proc/self/exe
|
|
# self-re-exec pattern (zygote/renderer/utility processes), fires ~6x
|
|
# every time the container is recreated (~daily, likely watchtower).
|
|
# Scoped to the whole image since this is normal Electron startup
|
|
# behavior end to end, not one specific binary.
|
|
# - tsl0922/ttyd (Synchronet BBS web terminal): telnet/pv/busybox-extras
|
|
# installed via apk at container start and then used for the BBS's
|
|
# normal telnet workflow. Scoped to those three binaries, not the image,
|
|
# since this is a shell-access container where an unexpected binary is
|
|
# more worth knowing about.
|
|
- macro: known_drop_and_execute_activities
|
|
condition: >
|
|
(container.image.repository = "docker.gitea.com/gitea" and proc.name = gitea)
|
|
or (container.image.repository = "lscr.io/linuxserver/obsidian")
|
|
or (container.image.repository = "tsl0922/ttyd" and proc.name in (telnet, pv, busybox-extras))
|
|
|
|
# "Redirect STDOUT/STDIN to Network Connection in Container" false-positives,
|
|
# both confirmed recurring (not one-off) via netdata's alert history:
|
|
# - gitea: sshd/sshd-session redirect the just-accepted SSH connection's
|
|
# socket onto stdin/stdout/stderr via dup2 - this is simply how every SSH
|
|
# server handles every session, not a reverse shell. Fires 4x per SSH
|
|
# connection (sshd x2, sshd-session x2). Scoped to those two binaries in
|
|
# this one container, not the whole image, since gitea itself spawning
|
|
# this pattern from some other binary would still be worth flagging.
|
|
# - fireflyiii/core: its wait-for-it.sh startup script opens a raw TCP
|
|
# connection to the postgres container to poll for DB readiness, which
|
|
# redirects stdio the same way a reverse shell would mechanically. Scoped
|
|
# to the script's own cmdline, not the whole image.
|
|
# - rommapp/romm: its own init (bash /init) connects to its bundled loopback
|
|
# redis (127.0.0.1:6379) and app port (127.0.0.1:5000) at container start,
|
|
# same dup2-based mechanic. Confirmed at the 2026-09-16 host reboot; will
|
|
# recur on every container start. Scoped to the image, not proc.name,
|
|
# since this is the container's own /init script.
|
|
- macro: user_known_stand_streams_redirect_activities
|
|
condition: >
|
|
(container.image.repository = "docker.gitea.com/gitea" and proc.name in (sshd, sshd-session))
|
|
or (container.image.repository = "fireflyiii/core" and proc.cmdline contains "wait-for-it.sh")
|
|
or (container.image.repository = "rommapp/romm" and proc.cmdline = "bash /init")
|
|
|
|
# "Clear Log Activities" false-positive: lsyncd (host systemd service, not a
|
|
# container - running since 2026-07-29) truncates its own status file,
|
|
# /var/log/lsyncd-status.log, as normal self-logging behavior. Matched via
|
|
# the stock rule's fd.directory=/var/log check, not because it's actually
|
|
# clearing evidence. Scoped to that one file, not all of /var/log, so any
|
|
# other log-truncation in that directory still gets flagged.
|
|
- macro: allowed_clear_log_files
|
|
condition: (fd.name = "/var/log/lsyncd-status.log" and proc.name = lsyncd)
|
|
|
|
# "Run shell untrusted" false-positive: fireflyiii/core and
|
|
# fireflyiii/data-importer's own s6 healthcheck (`sh ./data/check`, spawned
|
|
# by s6-notifyoncheck) fires twice per container start. Confirmed at the
|
|
# 2026-09-16 host reboot; will recur on every start. This list is the
|
|
# rule's own designated customization point (default: empty).
|
|
- list: user_known_shell_spawn_binaries
|
|
items: [s6-notifyoncheck]
|