interactive
interactive
¶
NFLOG-based interactive connection handler.
Implements a JSON-lines protocol for interactive verdict flow:
- pending — emitted when a new outbound connection is detected via NFLOG.
Contains
id,dest(IP),port,proto, anddomain(if resolvable from the dnsmasq log). - verdict — received on stdin from the controlling process. Must contain
type: "verdict",id(matching a pending event), andaction("accept"or"deny"). - verdict_applied — emitted after the verdict has been persisted to the
nft ruleset and state files. Contains
id,dest,action, andok(boolean indicating success).
The handler deduplicates by destination IP — only the first packet to a given
IP triggers a pending event. Accepted IPs are added to the allow sets and
persisted to live.allowed; denied IPs are added to the deny sets and
persisted to deny.list.
InteractiveSession(*, runner, state_dir, container, io=None)
¶
Drive the interactive NFLOG verdict loop.
Creates an :class:NflogWatcher, listens for queued-connection events,
emits pending events as JSON lines on stdout, reads verdict commands
from stdin, and applies them to the nft ruleset and state files.
Initialise the session.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
runner
|
CommandRunner
|
Command runner for nft operations. |
required |
state_dir
|
Path
|
Per-container state directory. |
required |
container
|
str
|
Container name (for nft nsenter). |
required |
io
|
SessionIO | None
|
I/O protocol implementation (defaults to :class: |
None
|
Source code in src/terok_shield/cli/interactive.py
run()
¶
Enter the interactive event loop.
Creates the NFLOG watcher, sets stdin to non-blocking, installs
signal handlers, and delegates to :meth:_loop. Exits with
code 1 if the NFLOG watcher cannot be created.
Source code in src/terok_shield/cli/interactive.py
SessionIO
¶
Bases: Protocol
I/O protocol for the interactive session.
Decouples rendering and parsing from the verdict engine so the same
:class:InteractiveSession can drive both machine-readable JSON-lines
(--raw) and human-friendly CLI output.
JsonSessionIO
¶
JSON-lines session I/O — machine-readable protocol.
Emits compact JSON objects (one per line) and expects JSON verdict commands on stdin. This is the original protocol from PR #162.
emit_pending(packet_id, dest, port, proto, domain)
¶
Emit a pending event as a JSON line.
Source code in src/terok_shield/cli/interactive.py
emit_verdict_applied(verdict_id, dest, action, *, ok)
¶
Emit a verdict-applied confirmation as a JSON line.
Source code in src/terok_shield/cli/interactive.py
parse_command(line)
¶
Parse a JSON verdict command.
Expected format: {"type": "verdict", "id": 1, "action": "accept"}.
Returns (id, action) on success or None on any validation failure.
Source code in src/terok_shield/cli/interactive.py
CliSessionIO()
¶
Human-friendly interactive CLI session I/O.
Renders blocked connections as readable lines and accepts short
operator input (a/d or allow/deny). Pending packets
are tracked in a FIFO queue — input always targets the oldest.
Initialise with empty pending-packet queues.
Source code in src/terok_shield/cli/interactive.py
emit_pending(packet_id, dest, port, proto, domain)
¶
Show a [BLOCKED] line and queue the packet for verdict.
Source code in src/terok_shield/cli/interactive.py
emit_verdict_applied(verdict_id, dest, action, *, ok)
¶
Show verdict result and prompt the next queued packet if any.
On success the packet is removed from the queue. On failure it
stays queued so the operator can retry with the same a/d
input (mirrors :meth:InteractiveSession._process_command which
keeps failed verdicts in _pending_by_ip).
Source code in src/terok_shield/cli/interactive.py
parse_command(line)
¶
Map operator input to the oldest pending packet.
Accepts a, d, allow, deny (case-insensitive).
Returns None and prints a hint on unrecognised input.
Source code in src/terok_shield/cli/interactive.py
run_interactive(state_dir, container, *, raw=False)
¶
Start the interactive NFLOG handler for a container.
The NFLOG netlink socket must be inside the container's network
namespace to receive packets logged by nft rules. On first
invocation, re-execs via podman unshare nsenter into the
container's netns. The re-exec sets _TEROK_SHIELD_NFLOG_NSENTER
so the second invocation runs the handler directly.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
state_dir
|
Path
|
Per-container state directory (may be relative). |
required |
container
|
str
|
Container name. |
required |
raw
|
bool
|
If |
False
|
Raises:
| Type | Description |
|---|---|
SystemExit
|
If the interactive tier is not configured or NFLOG watcher creation fails. |