Skip to content

shield

shield

Adapter for terok-shield egress firewall.

Creates per-task :class:Shield instances from the sandbox configuration. Each task gets its own state_dir under {task_dir}/shield/.

make_shield(task_dir, cfg=None)

Construct a per-task :class:Shield from sandbox configuration.

Builds a :class:ShieldConfig with state_dir scoped to task_dir.

Source code in src/terok_sandbox/shield.py
def make_shield(task_dir: Path, cfg: SandboxConfig | None = None) -> Shield:
    """Construct a per-task :class:`Shield` from sandbox configuration.

    Builds a :class:`ShieldConfig` with ``state_dir`` scoped to *task_dir*.
    """
    c = _cfg(cfg)
    config = ShieldConfig(
        state_dir=task_dir / "shield",
        mode=ShieldMode.HOOK,
        default_profiles=c.shield_profiles,
        loopback_ports=(c.gate_port, c.proxy_port, c.ssh_agent_port),
        audit_enabled=c.shield_audit,
        profiles_dir=c.shield_profiles_dir,
    )
    return Shield(config)

pre_start(container, task_dir, cfg=None)

Return extra podman run args for egress firewalling.

Returns an empty list (no firewall args) when the dangerous bypass_firewall_no_protection override is active.

Raises :class:SystemExit with setup instructions when the podman environment requires one-time hook installation.

Source code in src/terok_sandbox/shield.py
def pre_start(container: str, task_dir: Path, cfg: SandboxConfig | None = None) -> list[str]:
    """Return extra ``podman run`` args for egress firewalling.

    Returns an empty list (no firewall args) when the dangerous
    ``bypass_firewall_no_protection`` override is active.

    Raises :class:`SystemExit` with setup instructions when the
    podman environment requires one-time hook installation.
    """
    if _cfg(cfg).shield_bypass:
        warnings.warn(_BYPASS_WARNING, stacklevel=2)
        return []
    try:
        return make_shield(task_dir, cfg).pre_start(container)
    except ShieldNeedsSetup as exc:
        raise SystemExit(str(exc)) from None

down(container, task_dir, *, allow_all=False, cfg=None)

Set shield to bypass mode (allow egress) for a running container.

When allow_all is True, also permits private-range (RFC 1918) traffic.

Source code in src/terok_sandbox/shield.py
def down(
    container: str, task_dir: Path, *, allow_all: bool = False, cfg: SandboxConfig | None = None
) -> None:
    """Set shield to bypass mode (allow egress) for a running container.

    When *allow_all* is True, also permits private-range (RFC 1918) traffic.
    """
    if _cfg(cfg).shield_bypass:
        return
    make_shield(task_dir, cfg).down(container, allow_all=allow_all)

up(container, task_dir, cfg=None)

Set shield to deny-all mode for a running container.

Source code in src/terok_sandbox/shield.py
def up(container: str, task_dir: Path, cfg: SandboxConfig | None = None) -> None:
    """Set shield to deny-all mode for a running container."""
    if _cfg(cfg).shield_bypass:
        return
    make_shield(task_dir, cfg).up(container)

state(container, task_dir, cfg=None)

Return the live shield state for a running container.

Queries actual nft state even when bypass is set, because containers started before bypass was enabled may still have active rules.

Source code in src/terok_sandbox/shield.py
def state(container: str, task_dir: Path, cfg: SandboxConfig | None = None) -> ShieldState:
    """Return the live shield state for a running container.

    Queries actual nft state even when bypass is set, because containers
    started *before* bypass was enabled may still have active rules.
    """
    return make_shield(task_dir, cfg).state(container)

status(cfg=None)

Return shield status dict from the sandbox configuration.

Source code in src/terok_sandbox/shield.py
def status(cfg: SandboxConfig | None = None) -> dict:
    """Return shield status dict from the sandbox configuration."""
    c = _cfg(cfg)
    result: dict = {
        "mode": "hook",
        "profiles": list(c.shield_profiles),
        "audit_enabled": c.shield_audit,
    }
    if c.shield_bypass:
        result["bypass_firewall_no_protection"] = True
    return result

check_environment(cfg=None)

Check the podman environment for shield compatibility.

Returns a synthetic :class:EnvironmentCheck with bypass info when the dangerous bypass override is active.

Source code in src/terok_sandbox/shield.py
def check_environment(cfg: SandboxConfig | None = None) -> EnvironmentCheck:
    """Check the podman environment for shield compatibility.

    Returns a synthetic :class:`EnvironmentCheck` with bypass info when the
    dangerous bypass override is active.
    """
    if _cfg(cfg).shield_bypass:
        return EnvironmentCheck(
            ok=False,
            health="bypass",
            issues=["bypass_firewall_no_protection is set — egress firewall disabled"],
        )
    with tempfile.TemporaryDirectory() as tmp:
        return make_shield(Path(tmp), cfg).check_environment()

run_setup(*, root=False, user=False)

Install global OCI hooks for shield egress firewalling.

Global hooks are required on all podman versions to survive container stop/start cycles (terok-shield#122).

Raises :class:SystemExit when neither --root nor --user is given.

Source code in src/terok_sandbox/shield.py
def run_setup(*, root: bool = False, user: bool = False) -> None:
    """Install global OCI hooks for shield egress firewalling.

    Global hooks are required on all podman versions to survive
    container stop/start cycles (terok-shield#122).

    Raises :class:`SystemExit` when neither ``--root`` nor ``--user`` is given.
    """
    if not root and not user:
        raise SystemExit(
            "Specify --root (system-wide, uses sudo) or --user (user-local).\n"
            "  shield setup --root   # /etc/containers/oci/hooks.d\n"
            "  shield setup --user   # ~/.local/share/containers/oci/hooks.d"
        )
    setup_hooks_direct(root=root)

setup_hooks_direct(*, root=False)

Install global hooks via the terok-shield Python API (no subprocess).

Suitable for TUI callers that need direct control. Installs hooks to the system directory (with sudo) when root is True, otherwise to the user directory.

Source code in src/terok_sandbox/shield.py
def setup_hooks_direct(*, root: bool = False) -> None:
    """Install global hooks via the terok-shield Python API (no subprocess).

    Suitable for TUI callers that need direct control.  Installs hooks
    to the system directory (with sudo) when *root* is True, otherwise
    to the user directory.
    """
    if root:
        target = system_hooks_dir()
        setup_global_hooks(target, use_sudo=True)
    else:
        target = Path(USER_HOOKS_DIR).expanduser()
        setup_global_hooks(target)
        ensure_containers_conf_hooks_dir(target)