Skip to content

feat: node role — on-node Go buildinfo scanner with zero RBAC (ADR 0009) - #19

Merged
kfeofantov merged 1 commit into
mainfrom
feat/node-scanner-buildinfo
Aug 10, 2026
Merged

kfeofantov merged 1 commit into
mainfrom
feat/node-scanner-buildinfo

Conversation

@kfeofantov

Copy link
Copy Markdown
Contributor

What

Adds the agent's second role: agent node, a DaemonSet that scans on-node
Go binaries via debug/buildinfo. One binary, two roles — the controller
remains the default, so existing invocations are unchanged (ADR 0009).

The scanner

  • Enumerates /proc, reads debug/buildinfo from /proc/<pid>/exe: Go
    version, main module, dependencies, build settings (incl. -pgo).
  • Ties each process to its pod/container by parsing /proc/<pid>/cgroup
    (pod UID + container ID; cgroupfs and systemd driver shapes).
  • Filter early on the node: infra module paths (k8s.io/, containerd,
    coredns, etcd, prometheus, grafana, …) and our own github.com/RebuildStackCo/
    are dropped before any record is formed. Only four aggregate counters leave
    for the not-kept classes — scanned / go-found / filtered-infra / unreadable.
    Identities of filtered/unreadable processes never leave (invariant 6).

Security posture (the point of the node role)

  • Zero RBAC. The node ServiceAccount is bound to nothing; no Role/Binding
    exists for it and automountServiceAccountToken: false. It holds no
    Kubernetes client and never calls the API.
  • hostPID + CAP_SYS_PTRACE only — no CAP_BPF/CAP_PERFMON. The ptrace
    capability is required to read other containers' non-dumpable
    /proc/<pid>/exe (verified in kind); this resolves the CAP_SYS_PTRACE
    "TBD" in security.md §7
    .
  • Read-only root filesystem, seccomp RuntimeDefault, all other caps dropped,
    privileged: false, allowPrivilegeEscalation: false.

Infra & e2e

  • Agent Dockerfile; image / kind-load / node-e2e make targets.
  • Raw deploy/node-daemonset.yaml (no Helm chart yet, by design).
  • Sample Go workload (test/e2e/sample/, its own module path).
  • e2e deploys the DaemonSet from the shipped manifest into kind and asserts the
    known Go process is detected with the right version + module path, and infra
    is filtered. Passing locally:
    go_found:4, filtered_infra:20 with example.com/rebuildstack-e2e/goworkload
    detected at go1.26.1.

Docs

  • ADR 0009 (node role, hostPID rationale, zero RBAC, what is read from /proc,
    filter policy).
  • security.md §3/§4/§7/§8/§9 updated.

Scope

This slice writes findings to the node role's structured log; delivery to the
controller is a later slice. The one-way boundary is unchanged.

Testing

  • go build ./..., go test -race ./..., golangci-lint run (default + e2e tag): green.
  • Full e2e suite on kind: green; node e2e (make node-e2e): green.

🤖 Generated with Claude Code

Second role of the one agent binary. `agent node` runs a DaemonSet that
enumerates /proc, reads debug/buildinfo from /proc/<pid>/exe (Go version,
main module, dependencies, build settings incl. -pgo), and ties each
process to its pod/container by parsing /proc/<pid>/cgroup. The controller
role is the default, so existing invocations are unchanged.

Filter early on the node: infrastructure module paths (k8s.io/, containerd,
coredns, etcd, prometheus, grafana, …) and this agent's own module are
dropped before any record is formed. Only four aggregate counters describe
what is not kept (scanned / go found / filtered-infra / unreadable);
identities of filtered or unreadable processes never leave (invariant 6).

The node role holds NO Kubernetes client and NO RBAC: its ServiceAccount is
bound to nothing and its token is not mounted. Privileges are hostPID plus
CAP_SYS_PTRACE only — the ptrace capability is required to read other
containers' /proc/<pid>/exe (non-dumpable processes), and is NOT an eBPF
capability. Read-only root fs, seccomp RuntimeDefault, all other caps
dropped. This resolves the CAP_SYS_PTRACE "TBD" in security.md §7.

In this slice the result is written to the node role's structured log;
delivery to the controller is a later slice.

Infra: agent Dockerfile, image/kind-load/node-e2e make targets, raw
DaemonSet manifest (deploy/node-daemonset.yaml), and a sample Go workload
with a non-infra module path. e2e deploys the DaemonSet in kind and asserts
the known Go process is detected with the right version and module path
while infrastructure is filtered.

Docs: ADR 0009; security.md §3/§4/§7/§8/§9 updated for the node role.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@kfeofantov
kfeofantov merged commit 9e934d8 into main Aug 10, 2026
3 checks passed
@kfeofantov
kfeofantov deleted the feat/node-scanner-buildinfo branch August 10, 2026 15:51
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant