Building a Small Rust eBPF EDR 5: Optional Network Telemetry

This post covers network telemetry in rand-guard. Network events can help explain behavior such as reverse shells, suspicious listeners, and unusual outbound connections. But network telemetry can be noisy and easy to overinterpret.

For that reason, rand-guard does not enable network collection by default. It only attaches connect, bind, and listen syscall tracepoints when the user explicitly enables them.

Network Is Opt-In

The default config looks like this.

[events]
network = false

[network]
enabled = false
hooks = ["connect", "bind", "listen"]
collect_dns = false
collect_payload = false

To collect network events, both flags must be enabled.

[events]
network = true

[network]
enabled = true
hooks = ["connect", "bind", "listen"]
collect_dns = false
collect_payload = false

This double flag reduces the chance of enabling network telemetry by accident. Network events can be more sensitive than process or file events, and they can produce more first-run noise.

Collection Targets

The current implementation lives in crates/ebpf/src/network.rs.

  • sys_enter_connect
  • sys_enter_bind
  • sys_enter_listen

connect and bind receive a sockaddr pointer and read address family, port, and address. listen collects fd and backlog, but because there is no socket lifecycle table today, it does not fully connect the listener to the address and port that were previously bound.

This limit matters. The project should not claim that a listen event alone reconstructs the listener port completely. Full socket lifecycle correlation is not implemented yet.

Sockaddr Parsing

The eBPF program first reads the sockaddr address family. Today it parses AF_INET and AF_INET6 in a limited way.

For IPv4, it reads the port and IPv4 address. For IPv6, it reads the port and 16-byte IPv6 address. Unknown families are left without address metadata.

This parsing is bounded. If the sockaddr pointer is null or the length is too short, the event is discarded. This avoids emitting plausible-looking but incomplete network events.

Direction Representation

Normalized network events roughly fall into three types.

  • network_connect
  • network_bind
  • network_listen

connect is treated as outbound. bind and listen are represented from a listener perspective. Userspace output can include fields such as family, socket fd, remote or local address, port, and backlog.

An example looks like this.

{"event_type":"network_connect","comm":"nc","family":"ipv4","remote_addr":"127.0.0.1","remote_port":4444}

Real records include additional fields such as timestamp, pid, uid, gid, process enrichment, and detection metadata.

Suspicious Port Detection

The config includes suspicious network port detection.

[[detections.network]]
name = "suspicious_outbound_port"
directions = ["outbound"]
ports = [4444, 1337, 31337]

This detection compares direction and port. Optionally, process_names can be used to restrict the match to specific process names. That is a simple but useful way to reduce false positives.

Port numbers alone cannot prove an attack. Port 4444 is often used in netcat labs, CTFs, local tests, and debug tunnels. The detection is named suspicious, but the result is an investigation signal rather than a conclusion.

Demo Scenario

After enabling network collection and restarting the agent, run a loopback listener and client.

nc -l 127.0.0.1 4444

From another terminal, connect to it.

printf 'demo\n' | nc 127.0.0.1 4444

The output can include network_listen or network_connect records. If suspicious port detections include 4444, detection metadata or a separate alert record can also appear.

This demo is not an attack. It is a safe loopback scenario for checking how network telemetry is emitted.

What Is Not Implemented

Network telemetry is especially easy to overstate. The current rand-guard implementation does not include:

  • DNS collection.
  • Payload collection.
  • TLS visibility.
  • accept or accept4 telemetry.
  • Socket lifecycle correlation.
  • Socket table enrichment connecting bind and listen.

Because of these limits, a network event alone cannot prove a reverse shell. A shell process making an outbound connection to a suspicious port is meaningful, but it is not enough to conclude that an attack occurred.

Part 5 Summary

This post covered network telemetry.

  • Network collection is disabled by default and must be enabled explicitly.
  • The current collection targets are connect, bind, and listen syscall tracepoints.
  • IPv4 and IPv6 sockaddrs are parsed in a bounded way.
  • Suspicious port detection is an investigation signal.
  • DNS, payload, accept, and socket lifecycle correlation are not implemented.

The next post covers the MVP rule engine. It explains how built-in detections and config-based [[rules]] evaluate normalized events and produce stable alert records.