Skip to content

_setup_manual

_setup_manual

Interactive per-component hardening setup — SELinux policy, AppArmor addendum.

One [Y/s/n] flow shared by every frontend: say what state the host is in, where the change lands, and exactly which sudo bash command would make it — then run that command, or print the rules it applies. terok setup selinux / terok-executor setup selinux / terok-sandbox setup selinux (and the apparmor twins) all route through handle_setup_component, and the aggregate setup's hints name the same verbs via setup_invocation.

The split matters for sudo: everything that needs the operator's context — the venv path of the bundled installer, the resolved sandbox-live root — is resolved and rendered before privilege escalation, and the shown command is the argv that runs, built from it. Installing is therefore always interactive (sudo asks for the password mid-flow), so there is deliberately no --yes: an unattended run copies the printed command and executes it directly instead.

What the s answer prints is the installer's own input — the rendered AppArmor block, the policy source file — so an operator reviews the change itself, and can diff it against the host afterwards.

SETUP_COMPONENTS = ('selinux', 'apparmor') module-attribute

__all__ = ['SETUP_COMPONENTS', 'handle_setup_component'] module-attribute

handle_setup_component(component, *, show_only=False, cfg=None)

Run the interactive installer for one hardening component.

The whole setup <component> contract lives here, so every frontend is one routing line: a missing or unknown component name is answered from here too, spelled with the caller's own invocation.

Source code in src/terok_sandbox/_setup_manual.py
def handle_setup_component(
    component: str | None,
    *,
    show_only: bool = False,
    cfg: SandboxConfig | None = None,
) -> int:
    """Run the interactive installer for one hardening component.

    The whole ``setup <component>`` contract lives here, so every
    frontend is one routing line: a missing or unknown component name is
    answered from here too, spelled with the caller's own invocation.
    """
    from .config import SandboxConfig

    with nullcontext() if show_only else setup_lock():
        if component == "selinux":
            comp = _selinux_component(cfg or SandboxConfig())
        elif component == "apparmor":
            comp = _apparmor_component()
        else:
            from .operator_cli import setup_invocation

            named = (
                f"unknown setup component {component!r}"
                if component
                else "--show needs a component"
            )
            raise SystemExit(f"{named}: {setup_invocation()} <{'|'.join(SETUP_COMPONENTS)}>")
        if not show_only:
            from .setup import check_setup

            require_no_downgrade(check_setup(cfg))
        return _run_component(comp, show_only=show_only)