feat(cloak): egress SYN-ACK mangler + T2/T3 probe-response synthesizer
In-decky-netns NFQUEUE rewriter (window/option-order/IP-ID) and raw-socket synthesizer for nmap probes Linux drops but the target OS answers (T2/T3), driven by os_fingerprint.OS_MANGLE. Packet-shaping logic is pure and unit-tested offline; scapy/netfilterqueue import lazily in the runtime loops. Entry: python -m decnet.cloak (run by the base container; CAP_NET_ADMIN).
This commit is contained in:
53
decnet/cloak/__main__.py
Normal file
53
decnet/cloak/__main__.py
Normal file
@@ -0,0 +1,53 @@
|
||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
"""
|
||||
Cloak entrypoint — run inside the decky base container (CAP_NET_ADMIN).
|
||||
|
||||
python -m decnet.cloak
|
||||
|
||||
Config via env (set by the composer when nmap_os has a mangle profile):
|
||||
DECNET_NMAP_OS nmap_os slug (e.g. "windows", "windows_server")
|
||||
DECNET_OPEN_PORTS comma-separated TCP ports the decky serves (for T2/T3)
|
||||
DECKY_IP this decky's IP (BPF scope for the responder)
|
||||
|
||||
Starts the mangler and responder, each in its own thread. A slug with no mangle
|
||||
profile exits 0 immediately — harmless to launch unconditionally.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
import threading
|
||||
|
||||
from decnet.cloak import mangler, responder
|
||||
from decnet.logging import get_logger
|
||||
from decnet.os_fingerprint import get_os_mangle
|
||||
|
||||
log = get_logger("cloak")
|
||||
|
||||
|
||||
def _open_ports() -> frozenset[int]:
|
||||
raw = os.environ.get("DECNET_OPEN_PORTS", "")
|
||||
return frozenset(int(p) for p in raw.split(",") if p.strip().isdigit())
|
||||
|
||||
|
||||
def main() -> int:
|
||||
nmap_os = os.environ.get("DECNET_NMAP_OS", "linux")
|
||||
if get_os_mangle(nmap_os) is None:
|
||||
log.info("cloak: no mangle profile for %r — exiting", nmap_os)
|
||||
return 0
|
||||
|
||||
threads = [
|
||||
threading.Thread(target=mangler.run, args=(nmap_os,),
|
||||
name="cloak-mangler", daemon=True),
|
||||
threading.Thread(target=responder.run, args=(nmap_os, _open_ports()),
|
||||
name="cloak-responder", daemon=True),
|
||||
]
|
||||
for t in threads:
|
||||
t.start()
|
||||
log.info("cloak: started for nmap_os=%r", nmap_os)
|
||||
for t in threads:
|
||||
t.join()
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
Reference in New Issue
Block a user