# Enodia Sentinel configuration (TOML). # Read at startup from /etc/enodia-sentinel.toml (or $ENODIA_CONFIG). # Restart after editing: sudo systemctl restart enodia-sentinel.service # # Every key is optional; omitted keys keep their built-in default. # --- timing -------------------------------------------------------------- sample_interval = 4.0 # seconds between detector sweeps cooldown = 60 # min seconds before re-alerting the same signature baseline_grace = 10 # seconds after start before new-listener/suid arm suid_refresh = 3600 # seconds between SUID baseline refreshes suid_scan_interval = 60 # seconds between (backgrounded) SUID filesystem scans # --- detectors (omit one to disable it) ---------------------------------- detectors = [ "reverse_shell", "ld_preload", "deleted_exe", "new_listener", "new_suid", "persistence", "egress", ] # --- tuning -------------------------------------------------------------- # Process names treated as "interpreters" for reverse-shell / egress checks. interpreters = [ "bash", "sh", "dash", "zsh", "ksh", "ash", "nc", "ncat", "netcat", "socat", "telnet", "python", "python2", "python3", "perl", "ruby", "php", "lua", "awk", ] # Trusted public CIDRs that should NOT trip the egress detector # (your VPS, VPN exit, monitoring endpoints, …). egress_allow_cidrs = [] # Listener ports that never alert even if they appear after baseline. listener_allow_ports = [] # Dirs where any SUID binary is treated as CRITICAL (attacker-writable). suid_hot_dirs = ["/tmp", "/dev/shm", "/var/tmp", "/home", "/run/user"] # Writable dirs always scanned for SUID even if on a separate filesystem # (a tmpfs /tmp would otherwise be skipped by the on-device walk). suid_scan_extra_dirs = ["/tmp", "/dev/shm", "/var/tmp", "/run/user"] # Persistence files/dirs watched for modification. watch_persistence = [ "/etc/cron.d", "/etc/crontab", "/etc/cron.daily", "/etc/cron.hourly", "/etc/cron.weekly", "/var/spool/cron", "/etc/systemd/system", "/etc/ld.so.preload", "/etc/passwd", "/etc/sudoers", "/etc/sudoers.d", "/root/.ssh/authorized_keys", "/root/.bashrc", "/root/.profile", ] # --- eBPF event layer ---------------------------------------------------- # Event-driven execve monitor: catches short-lived processes the poll loop # misses, matched against the Snort-style rule engine. Requires python-bpfcc # and root; degrades gracefully to polling otherwise. ebpf_exec_monitor = true # Optional path to a TOML file of extra [[exec_rules]] (sid/msg/severity/ # classtype + path_prefixes/exec_comm/parent_comm/argv_regex). exec_rules_file = "" # Add a 3s bpftrace execve trace to each snapshot (requires the bpftrace pkg). capture_execve_bpftrace = false # --- retention ----------------------------------------------------------- max_snapshots = 300 # auto-delete oldest beyond this count (0 = no cap) max_snapshot_age_days = 60 # auto-delete older than this (0 = no age cap) # --- notifications: desktop --------------------------------------------- notify_users = [] # e.g. ["luna"] — desktop notify-send on alert notify_urgency = "critical" # low / normal / critical # --- notifications: phone push ------------------------------------------ # A backend turns on when its required keys are set. Only alerts at or above # notify_min_severity are pushed. notify_min_severity = "HIGH" # MEDIUM / HIGH / CRITICAL # ntfy (open-source, self-hostable). Install the ntfy app and subscribe to the # topic; keep the topic name secret — it is the only access control on ntfy.sh. notify_ntfy_url = "" # e.g. "https://ntfy.sh" or your own server notify_ntfy_topic = "" # e.g. "enodia-" notify_ntfy_token = "" # optional Bearer token (protected topics) # Pushover notify_pushover_token = "" # application token notify_pushover_user = "" # user/group key # Generic webhook — receives the alert JSON via POST (Discord/Slack/your own). notify_webhook_url = "" # Optional dashboard base URL embedded in pushes (e.g. your Tailscale URL). dashboard_url = "" # --- web dashboard (read-only) ------------------------------------------ web_bind = "" # "" = auto-detect Tailscale IP; or an explicit address web_port = 8787 web_token = "" # bearer token; auto-generated + saved if empty on a # non-loopback bind. Set explicitly to keep the unit # fully read-only.