feat(audit): log external docker stop/rm on claude-hooks-* via auditd #150
No reviewers
Labels
No labels
area:agents
area:dashboard
area:database
area:design
area:design-review
area:flows
area:infra
area:meta
area:security
area:sessions
area:webhook
area:workdir
security
type:bug
type:chore
type:meta
type:user-story
No milestone
No project
No assignees
2 participants
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
charles/claude-hooks!150
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "boss/149"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Summary
Closes #149 (and closes the diagnostic gap from #132). When
dev-defaultsilently vanishes we had no way to attribute thedocker stop/docker rmcall to a caller — dockerd logs the stopbut not who triggered it, and everything service-side checked out
clean. This ships an auditd rule that logs every
dockerCLI executeby uid 0 or uid 1000 under the key
claude-hooks-docker, capturingPID, full argv, exe path, and cwd.
Kernel rules can't filter on argv, so the rule fires on every docker
invocation by root or the operator; the
stop/rm+claude-hooks-*narrowing happens post-hoc via
ausearch. Noise is acceptable and theaudit key makes it a one-liner to extract only the events we care
about.
Changes
ops/audit/claude-hooks-docker.rules— two auditd rules (uid 0and uid 1000) on
path=@@DOCKER_PATH@@withperm=x. Theplaceholder is rewritten at install time so the rule matches the
host's real docker path.
justfile— three new recipes:audit-install— sed the placeholder,sudo installto/etc/audit/rules.d/claude-hooks-docker.rules,augenrules --load.audit-tail— pollsausearch --checkpointevery 2 s (ausearchhas no native follow mode). First call scopes to
-ts todayso wedon't dump history.
audit-uninstall— remove the rule file and reload. Idempotent.README.md— new "Debugging with auditd" section: install, readevents, follow mode, uninstall, scope caveats.
CLAUDE.md— Commands section now lists the three recipes.Test plan
just audit-installon charles-desktop → verify rule presentvia
sudo auditctl -l | grep claude-hooks-docker.docker stop claude-hooks-<test-instance>manually →sudo ausearch -k claude-hooks-docker -ts today -ishows theoperator's shell PID, argv (
a0=docker a1=stop a2=claude-hooks-…), exe, and cwd.bash -c 'docker stop claude-hooks-…')→ parent + child PIDs both logged.
just audit-tailin one terminal +docker rmin another →new event shows up within 2 s.
inspect the audit log, identify the caller, paste the finding
into a comment on #132 and close that ticket (per #149 AC).
just audit-uninstall→ rule file gone,auditctl -lclean.Closes #149
Review: feat(audit): log external docker stop/rm on claude-hooks-* via auditd
CI: ✅ green (run #1740, sha
b31c20e, 3m17s)Round: 1 (no prior reviews by me)
Acceptance criteria check
ops/audit/claude-hooks-docker.ruleswith-k claude-hooks-dockerkey@@DOCKER_PATH@@placeholder resolved at install time viacommand -v dockerjust audit-install: copies to/etc/audit/rules.d/, runsaugenrules --load, prints confirmation + ausearch commandjust audit-tail: checkpoint-based polling, scopes first scan to todayjust audit-uninstall: idempotent remove + reloadCode review
ops/audit/claude-hooks-docker.rules— Two-a always,exit -F path=@@DOCKER_PATH@@ -F perm=x -F uid={0,1000} -F key=claude-hooks-dockerrules. Correct auditd syntax;perm=xcapturesexecveso full argv (a0=docker, a1=stop|rm, a2=claude-hooks-…) lands in the EXECVE record. The acknowledged design choice to fire on all docker invocations and filter post-hoc is the right approach — kernel rules cannot match argv at dispatch time.audit-install— Preflight check forauditctl/augenrules/ausearch, thencommand -v docker || truewith an empty-check guard;mktemp+trapcleanup of the rendered temp file;sudo install -o root -g root -m 0640. All correct.augenrules --loadstderr is live (not suppressed), so failures surface throughset -euo pipefailcleanly.audit-tail— Checkpoint file under${XDG_STATE_HOME:-$HOME/.local/state}/claude-hooks/. First run scopes to-ts today, subsequent polls use--checkpointonly.|| trueonausearchcorrectly absorbs its exit code 1 (no events found) while keeping the loop alive. Correct and well-commented.audit-uninstall— Idempotent: skips removal if file absent, only callsaugenrulesif the binary exists. Clean.One non-blocking observation
The issue AC asked for
audit-tailoutput "filtered to the fields operators actually need: timestamp, PID, exe, argv, cwd". The implementation emits fullausearch -ioutput (all record types: SYSCALL, CWD, EXECVE, PROCTITLE). In practice this is a net positive for debugging — more context, not less — and the README already tells the operator which fields to look for. Not requesting changes; noted for completeness.Review: feat(audit): log external docker stop/rm on claude-hooks-* via auditd
CI: ✅ green (run #1740, sha
b31c20e, 3m17s)Round: 1 (no prior reviews by me)
Acceptance criteria check
ops/audit/claude-hooks-docker.ruleswith-k claude-hooks-dockerkey@@DOCKER_PATH@@placeholder resolved at install time viacommand -v dockerjust audit-install: copies to/etc/audit/rules.d/, runsaugenrules --load, prints confirmation + ausearch commandjust audit-tail: checkpoint-based polling, scopes first scan to todayjust audit-uninstall: idempotent remove + reloadCode review
ops/audit/claude-hooks-docker.rules— Two-a always,exit -F path=@@DOCKER_PATH@@ -F perm=x -F uid={0,1000} -F key=claude-hooks-dockerrules. Correct auditd syntax;perm=xcapturesexecveso full argv (a0=docker, a1=stop|rm, a2=claude-hooks-…) lands in the EXECVE record. The acknowledged design choice to fire on all docker invocations and filter post-hoc is the right approach — kernel rules cannot match argv at dispatch time.audit-install— Preflight check forauditctl/augenrules/ausearch, thencommand -v docker || truewith an empty-check guard;mktemp+trapcleanup of the rendered temp file;sudo install -o root -g root -m 0640. All correct.augenrules --loadstderr is live (not suppressed), so failures surface throughset -euo pipefailcleanly.audit-tail— Checkpoint file under${XDG_STATE_HOME:-$HOME/.local/state}/claude-hooks/. First run scopes to-ts today, subsequent polls use--checkpointonly.|| trueonausearchcorrectly absorbs its exit code 1 (no events found) while keeping the loop alive. Correct and well-commented.audit-uninstall— Idempotent: skips removal if file absent, only callsaugenrulesif the binary exists. Clean.One non-blocking observation
The issue AC asked for
audit-tailoutput "filtered to the fields operators actually need: timestamp, PID, exe, argv, cwd". The implementation emits fullausearch -ioutput (all record types: SYSCALL, CWD, EXECVE, PROCTITLE). In practice this is a net positive for debugging — more context, not less — and the README already tells the operator which fields to look for. Not requesting changes; noted for completeness.