run
run
¶
Subprocess execution boundary for all external commands.
Every shell-out in terok-shield flows through the CommandRunner
protocol. Production code uses SubprocessRunner; tests inject
fakes. This keeps external dependencies auditable and mockable in one
place.
CommandRunner
¶
Bases: Protocol
Protocol for executing external commands.
Decouples all subprocess calls behind a testable interface.
run(cmd, *, check=True, stdin=None, timeout=None)
¶
has(name)
¶
nft(*args, stdin=None, check=True)
¶
nft_via_nsenter(container, *args, pid=None, stdin=None, check=True)
¶
Run nft inside a running container's network namespace.
dnsmasq_via_nsenter(container, conf_path, *, binary, pid=None)
¶
Launch the dnsmasq at binary inside a running container's network namespace.
podman_inspect(container, fmt)
¶
lookup_all(domain, *, timeout=10)
¶
getent_hosts(domain, *, timeout=10)
¶
SubprocessRunner()
¶
Default CommandRunner implementation using subprocess.run.
Checks nft availability at construction time and raises
NftNotFoundError immediately if nft is not installed.
Check nft availability, raising NftNotFoundError if missing.
Source code in src/terok_shield/run.py
run(cmd, *, check=True, stdin=None, timeout=None)
¶
Run a command, return stdout. Raise ExecError on failure when check=True.
Source code in src/terok_shield/run.py
has(name)
¶
nft(*args, stdin=None, check=True)
¶
Run nft command directly (hook mode, inside container netns).
Source code in src/terok_shield/run.py
nft_via_nsenter(container, *args, pid=None, stdin=None, check=True)
¶
Run nft inside a running container's network namespace.
Source code in src/terok_shield/run.py
dnsmasq_via_nsenter(container, conf_path, *, binary, pid=None)
¶
Launch the dnsmasq at binary inside a running container's network namespace.
dnsmasq runs in the host PID namespace but the container's network namespace (like the OCI hook's own launch), so a host-side reload can relaunch it here. dnsmasq daemonizes and writes its own pid-file; the command returns once it has forked into the background.
Source code in src/terok_shield/run.py
podman_inspect(container, fmt)
¶
lookup_all(domain, *, timeout=10)
¶
Resolve domain to both IPv4 and IPv6 addresses with the host's lookup tool.
Prefers dig (one two-type query) and falls back to drill
(ldns, the Arch/Manjaro default; one type per invocation).
Validates each output line with ipaddress. Returns an empty
list on lookup failure or timeout.
Raises:
| Type | Description |
|---|---|
LookupToolNotFoundError
|
If neither tool is installed. |
Source code in src/terok_shield/run.py
getent_hosts(domain, *, timeout=10)
¶
Resolve domain via NSS (fallback when the lookup tool is missing or broken).
Queries both address families explicitly: plain getent hosts
stops at the first family glibc resolves (AAAA for dual-stack
names), which left allow_v4 empty on the one host whose lookup tool
crashes -- an allowed literal-IPv4 target then hit the terminal
reject as "Host is unreachable" (terok#1119).
timeout bounds each family query; on expiry that family yields
nothing (best-effort, matching lookup_all).
Source code in src/terok_shield/run.py
ExecError(cmd, rc, stderr)
¶
Bases: Exception
Raised when a subprocess fails.
Store command details and format the error message.
Source code in src/terok_shield/run.py
NftNotFoundError
¶
Bases: RuntimeError
Raised when the nft binary is not found on the host.
LookupToolNotFoundError
¶
Bases: RuntimeError
Raised when neither dig nor drill is found on the host.
ShieldNeedsSetup
¶
Bases: SetupRequiredError
Raised when host configuration cannot support the requested Shield policy.
The message includes the DNS-tier limitation or unavailable configured binary and the corresponding setup remedies.