The TTP worker entry moved out of decnet/cli/workers.py into its own module so the TTP CLI surface (worker + admin verbs) is colocated, mirroring decnet/cli/canary.py / webhook.py / swarm.py. - New `decnet/cli/ttp.py` with `decnet ttp` (worker, ExecStart-stable for decnet-ttp.service) and `decnet ttp-backfill --since-days N`. - `decnet ttp-backfill` walks Attacker.commands and CanaryTrigger history, dispatches each row through the live CompositeTagger, persists tags via repo.insert_tags (idempotent INSERT OR IGNORE). --dry-run / --source command|canary|all / --batch-size supported. - Backfill deliberately bypasses bus publish — historical replay must not re-trigger SIEM/webhook fan-out per TTP_TAGGING.md §"Bus topics" loop-prevention invariant. - Added `iter_attacker_commands_since` / `iter_canary_triggers_since` read-only iterators on TTPMixin + abstract bindings on BaseRepository. - Master-only via gating; both `ttp` and `ttp-backfill` listed in MASTER_ONLY_COMMANDS.
92 lines
2.1 KiB
Python
92 lines
2.1 KiB
Python
"""
|
|
DECNET CLI — entry point for all commands.
|
|
|
|
Usage:
|
|
decnet deploy --mode unihost --deckies 5 --randomize-services
|
|
decnet status
|
|
decnet teardown [--all | --id decky-01]
|
|
decnet services
|
|
|
|
Layout: each command module exports ``register(app)`` which attaches its
|
|
commands to the passed Typer app. ``__init__.py`` builds the root app,
|
|
calls every module's ``register`` in order, then runs the master-only
|
|
gate. The gate must fire LAST so it sees the fully-populated dispatch
|
|
table before filtering.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import typer
|
|
|
|
from . import (
|
|
agent,
|
|
api,
|
|
bus,
|
|
canary,
|
|
db,
|
|
deploy,
|
|
forwarder,
|
|
geoip,
|
|
init,
|
|
inventory,
|
|
lifecycle,
|
|
listener,
|
|
orchestrator,
|
|
profiler,
|
|
realism,
|
|
reconciler,
|
|
sniffer,
|
|
swarm,
|
|
swarmctl,
|
|
topology,
|
|
ttp,
|
|
updater,
|
|
web,
|
|
webhook,
|
|
workers,
|
|
)
|
|
from .gating import _gate_commands_by_mode
|
|
from .utils import console as console, log as log
|
|
|
|
app = typer.Typer(
|
|
name="decnet",
|
|
help="Deploy a deception network of honeypot deckies on your LAN.",
|
|
no_args_is_help=True,
|
|
)
|
|
|
|
# Order matches the old flat layout so `decnet --help` reads the same.
|
|
for _mod in (
|
|
api, swarmctl, agent, updater, listener, forwarder,
|
|
swarm,
|
|
deploy, lifecycle, workers, inventory,
|
|
web, profiler, orchestrator, realism, reconciler, sniffer, db,
|
|
topology, bus, geoip, init, webhook, canary, ttp,
|
|
):
|
|
_mod.register(app)
|
|
|
|
_gate_commands_by_mode(app)
|
|
|
|
# Backwards-compat re-exports. Tests and third-party tooling import these
|
|
# directly from ``decnet.cli``; the refactor must keep them resolvable.
|
|
from .db import _db_reset_mysql_async # noqa: E402,F401
|
|
from .gating import ( # noqa: E402,F401
|
|
MASTER_ONLY_COMMANDS,
|
|
MASTER_ONLY_GROUPS,
|
|
_agent_mode_active,
|
|
_require_master_mode,
|
|
)
|
|
from .utils import ( # noqa: E402,F401
|
|
_daemonize,
|
|
_http_request,
|
|
_is_running,
|
|
_kill_all_services,
|
|
_pid_dir,
|
|
_service_registry,
|
|
_spawn_detached,
|
|
_swarmctl_base_url,
|
|
)
|
|
|
|
|
|
if __name__ == "__main__": # pragma: no cover
|
|
app()
|