feat(smtp_relay): move probe forwarding to realism worker via bus

Attacker probe emails are now forwarded by the master (realism worker)
rather than inside the MACVLAN container, which has no internet gateway.

- New smtp.probe.pending bus topic: ingester publishes when smtp_relay
  message_stored fires; worker subscribes and does the actual delivery
- decnet/orchestrator/drivers/smtp_relay.py: pure-sync forward_probe()
  reads the .eml from disk and sends via smtplib on a thread executor
- worker.py: _run_smtp_probe_listener + _handle_probe_pending subtask;
  limit enforced via count_probe_relays() (DB-backed, restart-safe)
- bounties.py: count_probe_relays() query on probe_relay bounty type
- fleet.py: get_fleet_decky_by_name() to pull service config from DB
- services/smtp_relay.py: upstream_* and probe_limit fields defined in
  config_schema but NOT injected into container env (credentials stay
  out of docker env vars)
- ingester.py: stripped of smtplib; publishes probe.pending and exits
- tests: assert upstream keys absent from container environment
This commit is contained in:
2026-04-30 12:10:58 -04:00
parent 4c0a1309f0
commit 8ae7b9636e
8 changed files with 231 additions and 39 deletions

View File

@@ -28,7 +28,10 @@ def test_smtp_relay_dockerfile_context():
assert ctx.is_dir()
def test_smtp_relay_upstream_cfg():
def test_smtp_relay_upstream_cfg_not_in_container_env():
"""Upstream relay config is stored in decky_config and consumed by the
realism worker — it must NOT be injected into the container environment
(credentials don't belong in container env vars)."""
svc = SMTPRelayService()
fragment = svc.compose_fragment(
"test-decky",
@@ -41,18 +44,10 @@ def test_smtp_relay_upstream_cfg():
},
)
env = fragment["environment"]
assert env["SMTP_UPSTREAM_HOST"] == "smtp.sendgrid.net"
assert env["SMTP_UPSTREAM_PORT"] == "587"
assert env["SMTP_UPSTREAM_USER"] == "apikey"
assert env["SMTP_UPSTREAM_PASS"] == "SG.secret"
assert env["SMTP_PROBE_LIMIT"] == "2"
def test_smtp_relay_upstream_not_set_by_default():
svc = SMTPRelayService()
fragment = svc.compose_fragment("test-decky")
env = fragment["environment"]
assert "SMTP_UPSTREAM_HOST" not in env
assert "SMTP_UPSTREAM_PORT" not in env
assert "SMTP_UPSTREAM_USER" not in env
assert "SMTP_UPSTREAM_PASS" not in env
assert "SMTP_PROBE_LIMIT" not in env