shield_exec
shield_exec
¶
Shell out to terok-shield allow|deny for a single verdict.
Lives inside the verdict feature because this is the one thing the
verdict helper process exists to do — and it's the one thing the hub
cannot do under any real systemd hardening. podman unshare
nsenter nft (which shield exec's under the covers) requires the
hub's user+mount namespace to match the pause process's, and any
seccomp-based or mount-ns-isolating unit directive breaks that
setns. The verdict helper runs unhardened; the hub, freed from
this exec, runs under NoNewPrivileges=yes + @system-service.
The logic itself is the tuple (ok, stderr_snippet) that used to
live in hub.server.ClearanceHub._run_shield — unchanged, just
pulled out so both the hub's inline path (if this ever collapses
back) and the out-of-process helper can reuse it.
run_shield(shield_binary, container, dest, action)
async
¶
Invoke terok-shield <action> <container> <dest>; return (ok, snippet).
Bounded by _SHIELD_CLI_TIMEOUT_S. Spawn errors, non-zero
exit, and timeouts all fold into (False, reason) so callers
see one shape regardless of how shield misbehaved. snippet
is capped at _STDERR_CAP_BYTES.
Source code in src/terok_clearance/verdict/shield_exec.py
find_shield_binary()
¶
Locate terok-shield — sibling venv first, then PATH, then None.
The sibling check handles the pipx / poetry case where terok-shield
ships in the same venv as terok-clearance; we prefer it over PATH
so a shell-rc PATH shim can't redirect verdicts through a
different installation. is_file alone would happily return a
non-executable artifact, so the exec-bit check prevents a broken
install from failing every verdict instead of falling through to
PATH's working copy.