ironsh/iron-sensor

An eBPF-based behavioral monitor for AI coding agents.

C

134

3 commits

updated Mar 28, 2026

See the code

README

iron-sensor

An eBPF-based behavioral monitor for AI coding agents. iron-sensor detects when AI agents like Claude Code, Codex, and OpenClaw are running on a Linux system, then monitors every process they spawn, every file they touch, and every persistence mechanism they attempt.

Why this exists

AI coding agents run with your credentials, in your shell, on your infrastructure. They can do anything you can: read SSH keys, write cron jobs, modify systemd units, and escalate privileges. Most of the time, this helps you code. Some of the time, this installs a backdoor that exfiltrates your credentials. There is no way of telling which it is by default. iron-sensor solves that problem.

iron-sensor sits in the kernel via eBPF and watches what these agents actually do. Since it works in kernel-space, it is nearly zero overhead. It doesn't block anything. Rather, it emits structured events as NDJSON so you can see, alert on, and audit agent behavior.

Getting started

Download

Grab the latest pre-built binary from the GitHub Releases page:

curl -Lo iron-sensor.tar.gz https://github.com/ironsh/iron-sensor/releases/latest/download/iron-sensor_linux_amd64.tar.gz
tar xzf iron-sensor.tar.gz
sudo mv iron-sensor /usr/local/bin/

Build from source

Prerequisites:

  • Linux with kernel BTF support (5.8+)
  • Root privileges (BPF requires CAP_BPF/CAP_SYS_ADMIN)
  • Go 1.25+
  • clang, llvm, libbpf-dev (for BPF compilation)
  • just command runner

On Ubuntu/Debian:

sudo apt-get install clang llvm libbpf-dev
just build

This compiles the eBPF programs, generates Go bindings, and builds the bin/iron-sensor binary.

Run

# With default config (file sink to /var/log/iron/sensor/events.json)
sudo ./bin/iron-sensor

# With a config file
sudo ./bin/iron-sensor --config examples/config.example.yaml

# Development mode (stdout)
sudo ./bin/iron-sensor --config config.dev.yaml

Install as a service

sudo cp bin/iron-sensor /usr/local/bin/
sudo mkdir -p /etc/iron-sensor
sudo cp examples/config.example.yaml /etc/iron-sensor/config.yaml
sudo cp examples/iron-sensor.service /etc/systemd/system/
sudo systemctl enable --now iron-sensor

Configuration

iron-sensor is configured via a YAML file passed with --config. See config.example.yaml for all options.

sink_type: file

file_sink:
  output_path: /var/log/iron/sensor/events.json
  max_size: 100       # MB before rotation
  max_backups: 5
  compress: true

rules:
  min_severity: 1     # only emit alert and warn (drop info)
  overrides:
    package_manager:
      enabled: false   # suppress package manager events
    shell_spawn:
      severity: 2      # downgrade to info

Severity levels: 0 = alert, 1 = warn, 2 = info. The min_severity filter drops events above the threshold. Agent lifecycle events (start/stop) are always emitted regardless of filter.

When no --config is provided, iron-sensor uses sensible defaults (file sink, all rules enabled, no severity filter).

Event format

Events are emitted as newline-delimited JSON. Example:

{"event_id":"01J...","ts":"2025-01-15T10:30:00.000Z","category":"process","severity":1,"pid":12345,"ppid":12340,"comm":"curl","exe":"/usr/bin/curl","argv":["curl","https://example.com"],"cwd":"/home/user/project","agent_root_pid":12300,"in_agent_subtree":true,"rule_matched":"network_tool"}

Categories: agent_lifecycle, process, file, persistence

How it works

iron-sensor attaches to six kernel tracepoints:

TracepointWhat it captures
sched/sched_process_execEvery process execution — detects new agents and child processes
sched/sched_process_forkParent-child relationships — tracks the full agent process tree
sched/sched_process_exitProcess exits — emits lifecycle events with duration and exit codes
syscalls/sys_enter_openat + sys_exit_openatFile opens — captures path, flags, and file descriptor
syscalls/sys_enter_fchmodatPermission changes — detects setuid/setgid bit manipulation

When an agent is detected (via executable name and argv matching), iron-sensor registers it in a BPF hash map. The kernel-side program then propagates tracking to every child process the agent forks, building a complete subtree. All activity within that subtree — process spawns, file access, persistence writes — is emitted as classified, severity-tagged NDJSON events.

Agent detection

AgentHow it's identified
Claude Codeargv[0] basename is claude
OpenClawargv[0] basename is openclaw-gateway
CodexExecutable is python3 and argv contains codex

Detection works both at startup (scanning /proc) and live (via the exec tracepoint).

Custom binary detections

You can configure additional binary detections via the detections section in your config file. Each entry matches on the basename of argv[0], the same way the built-in Claude Code and OpenClaw detections work. Optionally, use args_regex to also require a regex match against the process arguments.

detections:
  binaries:
    - name: exfil_agent
      binary: exfil-tool
    - name: exfil_script
      binary: bash
      args_regex: "exfil\\.sh"
FieldDescription
nameSignature name that appears in emitted events (signature_matched)
binaryBasename of argv[0] to match (e.g. my-agent matches /usr/local/bin/my-agent)
args_regexOptional regex matched against argv[1:]. If set, at least one argument must match

Custom detections are appended to the built-in set — built-in agents are always detected regardless of config.

Detection rules

Events are classified by a rule engine with three categories. First match wins within each category.

Process rules — applied to any process spawned within an agent subtree:

RuleSeverityTriggers on
privilege_escalationAlertsudo, su, doas
ptrace_toolAlertstrace, ltrace, gdb
network_toolWarncurl, wget, nc, ncat, socat
interpreter_execWarnpython/ruby/perl/node spawned by a shell
shell_spawnWarnsh, bash, zsh, dash, fish
package_managerInfoapt, pip, npm, yarn, gem, cargo

Persistence rules — checked first on file events; a match re-categorizes the event:

RuleSeverityPaths
ssh_persistenceAlertauthorized_keys, ssh config, sshd_config
cron_writeAlert/etc/cron*, /var/spool/cron/*
systemd_unit_writeAlert/etc/systemd/*, ~/.config/systemd/*
system_profile_writeAlert/etc/profile, /etc/environment
ld_preload_writeAlert/etc/ld.so.preload, /etc/ld.so.conf.d/*
sudoers_writeAlert/etc/sudoers, /etc/sudoers.d/*
shell_rc_writeWarn.bashrc, .zshrc, .profile, etc.
xdg_autostartWarn~/.config/autostart/*, /etc/xdg/autostart/*
apt_hookWarn/etc/apt/apt.conf.d/*
npmrc_writeWarn.npmrc
git_hook_writeWarn.git/hooks/*

File rules — applied to file opens not caught by persistence rules:

RuleSeverityTriggers on
ssh_key_accessAlertAny access to ~/.ssh/
proc_mem_accessAlert/proc/*/mem, /proc/*/environ
docker_socketAlertdocker.sock, containerd paths
cgroup_writeAlert/sys/fs/cgroup/* writes
sensitive_file_writeAlert/etc/shadow, /etc/passwd, etc. writes
sensitive_file_readWarnSame paths, read-only
generic_writeInfoAny file write

Testing

# Unit tests
just test

# End-to-end tests (requires root + BPF)
sudo go test -tags e2e -v -count=1 ./test/e2e/

The e2e tests boot the real sensor, launch stub binaries that mimic each agent, perform detectable actions (reading /etc/shadow, writing cron jobs, spawning curl), and assert that the correct events fire.

What about network egress?

iron-sensor intentionally does not monitor network egress. Meaningful egress control for AI agents requires SNI-aware proxying that inspects TLS ClientHello to determine where connections are going and enforcing policy on a per-destination basis. That's a fundamentally different architecture from kernel-level file and process monitoring.

Network egress monitoring and policy enforcement are features of the broader iron.sh platform.

License

Apache License 2.0. See LICENSE for details.

Contributors

mslipper

3 commits

ironsh/iron-sensor

An eBPF-based behavioral monitor for AI coding agents.

C

134

3 commits

updated Mar 28, 2026

See the code

README

iron-sensor

An eBPF-based behavioral monitor for AI coding agents. iron-sensor detects when AI agents like Claude Code, Codex, and OpenClaw are running on a Linux system, then monitors every process they spawn, every file they touch, and every persistence mechanism they attempt.

Why this exists

AI coding agents run with your credentials, in your shell, on your infrastructure. They can do anything you can: read SSH keys, write cron jobs, modify systemd units, and escalate privileges. Most of the time, this helps you code. Some of the time, this installs a backdoor that exfiltrates your credentials. There is no way of telling which it is by default. iron-sensor solves that problem.

iron-sensor sits in the kernel via eBPF and watches what these agents actually do. Since it works in kernel-space, it is nearly zero overhead. It doesn't block anything. Rather, it emits structured events as NDJSON so you can see, alert on, and audit agent behavior.

Getting started

Download

Grab the latest pre-built binary from the GitHub Releases page:

curl -Lo iron-sensor.tar.gz https://github.com/ironsh/iron-sensor/releases/latest/download/iron-sensor_linux_amd64.tar.gz
tar xzf iron-sensor.tar.gz
sudo mv iron-sensor /usr/local/bin/

Build from source

Prerequisites:

  • Linux with kernel BTF support (5.8+)
  • Root privileges (BPF requires CAP_BPF/CAP_SYS_ADMIN)
  • Go 1.25+
  • clang, llvm, libbpf-dev (for BPF compilation)
  • just command runner

On Ubuntu/Debian:

sudo apt-get install clang llvm libbpf-dev
just build

This compiles the eBPF programs, generates Go bindings, and builds the bin/iron-sensor binary.

Run

# With default config (file sink to /var/log/iron/sensor/events.json)
sudo ./bin/iron-sensor

# With a config file
sudo ./bin/iron-sensor --config examples/config.example.yaml

# Development mode (stdout)
sudo ./bin/iron-sensor --config config.dev.yaml

Install as a service

sudo cp bin/iron-sensor /usr/local/bin/
sudo mkdir -p /etc/iron-sensor
sudo cp examples/config.example.yaml /etc/iron-sensor/config.yaml
sudo cp examples/iron-sensor.service /etc/systemd/system/
sudo systemctl enable --now iron-sensor

Configuration

iron-sensor is configured via a YAML file passed with --config. See config.example.yaml for all options.

sink_type: file

file_sink:
  output_path: /var/log/iron/sensor/events.json
  max_size: 100       # MB before rotation
  max_backups: 5
  compress: true

rules:
  min_severity: 1     # only emit alert and warn (drop info)
  overrides:
    package_manager:
      enabled: false   # suppress package manager events
    shell_spawn:
      severity: 2      # downgrade to info

Severity levels: 0 = alert, 1 = warn, 2 = info. The min_severity filter drops events above the threshold. Agent lifecycle events (start/stop) are always emitted regardless of filter.

When no --config is provided, iron-sensor uses sensible defaults (file sink, all rules enabled, no severity filter).

Event format

Events are emitted as newline-delimited JSON. Example:

{"event_id":"01J...","ts":"2025-01-15T10:30:00.000Z","category":"process","severity":1,"pid":12345,"ppid":12340,"comm":"curl","exe":"/usr/bin/curl","argv":["curl","https://example.com"],"cwd":"/home/user/project","agent_root_pid":12300,"in_agent_subtree":true,"rule_matched":"network_tool"}

Categories: agent_lifecycle, process, file, persistence

How it works

iron-sensor attaches to six kernel tracepoints:

TracepointWhat it captures
sched/sched_process_execEvery process execution — detects new agents and child processes
sched/sched_process_forkParent-child relationships — tracks the full agent process tree
sched/sched_process_exitProcess exits — emits lifecycle events with duration and exit codes
syscalls/sys_enter_openat + sys_exit_openatFile opens — captures path, flags, and file descriptor
syscalls/sys_enter_fchmodatPermission changes — detects setuid/setgid bit manipulation

When an agent is detected (via executable name and argv matching), iron-sensor registers it in a BPF hash map. The kernel-side program then propagates tracking to every child process the agent forks, building a complete subtree. All activity within that subtree — process spawns, file access, persistence writes — is emitted as classified, severity-tagged NDJSON events.

Agent detection

AgentHow it's identified
Claude Codeargv[0] basename is claude
OpenClawargv[0] basename is openclaw-gateway
CodexExecutable is python3 and argv contains codex

Detection works both at startup (scanning /proc) and live (via the exec tracepoint).

Custom binary detections

You can configure additional binary detections via the detections section in your config file. Each entry matches on the basename of argv[0], the same way the built-in Claude Code and OpenClaw detections work. Optionally, use args_regex to also require a regex match against the process arguments.

detections:
  binaries:
    - name: exfil_agent
      binary: exfil-tool
    - name: exfil_script
      binary: bash
      args_regex: "exfil\\.sh"
FieldDescription
nameSignature name that appears in emitted events (signature_matched)
binaryBasename of argv[0] to match (e.g. my-agent matches /usr/local/bin/my-agent)
args_regexOptional regex matched against argv[1:]. If set, at least one argument must match

Custom detections are appended to the built-in set — built-in agents are always detected regardless of config.

Detection rules

Events are classified by a rule engine with three categories. First match wins within each category.

Process rules — applied to any process spawned within an agent subtree:

RuleSeverityTriggers on
privilege_escalationAlertsudo, su, doas
ptrace_toolAlertstrace, ltrace, gdb
network_toolWarncurl, wget, nc, ncat, socat
interpreter_execWarnpython/ruby/perl/node spawned by a shell
shell_spawnWarnsh, bash, zsh, dash, fish
package_managerInfoapt, pip, npm, yarn, gem, cargo

Persistence rules — checked first on file events; a match re-categorizes the event:

RuleSeverityPaths
ssh_persistenceAlertauthorized_keys, ssh config, sshd_config
cron_writeAlert/etc/cron*, /var/spool/cron/*
systemd_unit_writeAlert/etc/systemd/*, ~/.config/systemd/*
system_profile_writeAlert/etc/profile, /etc/environment
ld_preload_writeAlert/etc/ld.so.preload, /etc/ld.so.conf.d/*
sudoers_writeAlert/etc/sudoers, /etc/sudoers.d/*
shell_rc_writeWarn.bashrc, .zshrc, .profile, etc.
xdg_autostartWarn~/.config/autostart/*, /etc/xdg/autostart/*
apt_hookWarn/etc/apt/apt.conf.d/*
npmrc_writeWarn.npmrc
git_hook_writeWarn.git/hooks/*

File rules — applied to file opens not caught by persistence rules:

RuleSeverityTriggers on
ssh_key_accessAlertAny access to ~/.ssh/
proc_mem_accessAlert/proc/*/mem, /proc/*/environ
docker_socketAlertdocker.sock, containerd paths
cgroup_writeAlert/sys/fs/cgroup/* writes
sensitive_file_writeAlert/etc/shadow, /etc/passwd, etc. writes
sensitive_file_readWarnSame paths, read-only
generic_writeInfoAny file write

Testing

# Unit tests
just test

# End-to-end tests (requires root + BPF)
sudo go test -tags e2e -v -count=1 ./test/e2e/

The e2e tests boot the real sensor, launch stub binaries that mimic each agent, perform detectable actions (reading /etc/shadow, writing cron jobs, spawning curl), and assert that the correct events fire.

What about network egress?

iron-sensor intentionally does not monitor network egress. Meaningful egress control for AI agents requires SNI-aware proxying that inspects TLS ClientHello to determine where connections are going and enforcing policy on a per-destination basis. That's a fundamentally different architecture from kernel-level file and process monitoring.

Network egress monitoring and policy enforcement are features of the broader iron.sh platform.

License

Apache License 2.0. See LICENSE for details.

Contributors

mslipper

3 commits

Languages

C

97.5%

Go

2.4%