feat(system): persist kernel.yama.ptrace_scope=0 so the keylog spin capture can attach - #334
Open
ZacxDev wants to merge 1 commit into
Open
feat(system): persist kernel.yama.ptrace_scope=0 so the keylog spin capture can attach#334ZacxDev wants to merge 1 commit into
ZacxDev wants to merge 1 commit into
Conversation
…ture works
keylog-spin-capture uses py-spy to dump keylog.service — a SIBLING
`systemd --user` unit, never a descendant — so Yama's default scope=1
("descendants only") blocks both PTRACE_ATTACH and process_vm_readv. The
watcher then hits its sysctl gate and exits silently every 5 minutes:
enabled, but structurally incapable of succeeding.
The workbench read 0 for most of 2026-07-30 only because of a manual
`echo 0 | sudo tee` during the investigation. It was never persisted, so
the 2026-08-04 reboot reset it to 1 and the watcher went inert. An
earlier revision of the perf script had this as a prompted step; the
audit correctly removed it as buying nothing WHEN the live value was
already 0. That reasoning is now stale — the reboot is what changed it.
The trade is documented in the script header rather than buried: scope=0
is the traditional Linux default (same-UID only, no cross-user or root
grant), but on a box routinely running agent-spawned code as this user it
widens what a compromised same-user process can read from other processes'
memory. Two narrower alternatives are rejected with reasons — a
`systemd --user` service cannot gain CAP_SYS_PTRACE, and running py-spy as
root ad hoc defeats a mechanism whose whole point is catching an
unpredictable spin while you are not watching.
Includes the systemd-sysctl restart, and this is the case where it is
genuinely warranted: `nixos-rebuild switch` does not restart that unit
(nixpkgs#289174), so a NEW boot.kernel.sysctl edit stays inert on the
running kernel. The earlier script dropped the restart because nothing
was pending; here something is.
Verified against a scratch copy of the live configuration.nix: non-TTY
run completes rc=0, second run is a clean no-op, resulting diff is
exactly one attribute inside boot.kernel.sysctl, parses clean.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Staged
/etc/nixoschange (Claude can'tsudo nixos-rebuild), run with:Why
keylog-spin-captureuses py-spy to dumpkeylog.service— a siblingsystemd --userunit, never a descendant of the capture. Yama's defaultscope=1("descendants only") blocks bothPTRACE_ATTACHandprocess_vm_readv, so the watcher hits its sysctl gate and exits silently every 5 minutes. It is enabled and structurally incapable of ever succeeding.The workbench read
0for most of 2026-07-30 only because of a manualecho 0 | sudo teeduring the investigation. That was never persisted, so the 2026-08-04 reboot reset it to 1 and the watcher went inert.An earlier revision of the perf script had this as a prompted step. The audit removed it — correctly, since the live value was already 0 and it would have bought nothing. That reasoning is now stale; the reboot is what changed the facts.
The trade, stated rather than buried
scope=0is the traditional Linux default: any process may ptrace another running as the same UID. It grants no cross-user or root access. But on a box routinely running agent-spawned code as this user, it widens what a compromised same-user process can read from other processes' memory — browser sessions, kubeconfigs loaded into a running tool, ssh-agent.Two narrower alternatives, rejected with reasons in the script header:
systemd --userservices can't gain capabilities. It would have to become a root system unit, which is a larger privilege grant, not a smaller one.If you'd rather not take the trade, the honest alternative is to drop the watcher — it degrades safely (detects
scope!=0, exits quietly, no dump/toast/retry), but it will never fire.Includes the systemd-sysctl restart — and here it's warranted
nixos-rebuild switchdoes not restartsystemd-sysctl(nixpkgs#289174), so a newboot.kernel.sysctledit stays inert on the running kernel. The earlier perf script deliberately dropped this restart because nothing was pending. Here something is.Verification
Tested against a scratch copy of the live
configuration.nix:Backs up first, restores on any failure before validation, prompts before
nixos-rebuild(skipped safely on non-TTY), prints a rollback line — including if the rebuild itself fails.🤖 Generated with Claude Code