
Broker di decoy sperimentale
Un'esca di rete containerizzata (honeypot) che pubblicizza SSH, RDP e SMB, osserva ogni connessione in entrata con eBPF e fa da proxy inverso per ogni sessione in un container di decoy isolato.
Il design separa due aspetti:
Questo è uno strumento difensivo per rilevare e studiare attività non autorizzate su reti di tua proprietà o che sei autorizzato a monitorare. Distribuiscilo solo dove hai tale autorità.
flowchart TB
A["Attaccante / Scanner"]
subgraph host["Host Decoy"]
direction TB
NIC["broker eth0<br/>pubblicate: 22, 3389, 445"]
subgraph brk["container broker"]
direction TB
E["classificatore eBPF TC<br/>registra ogni SYN<br/>vede il vero IP sorgente"]
P["proxy inverso<br/>CONNECT al backend"]
L["log JSON strutturati"]
end
subgraph dec["decoynet (interna, senza route host)"]
direction LR
S["ssh-decoy<br/>OpenCanary ssh<br/>porta 2222"]
D["rdp-decoy<br/>OpenCanary rdp<br/>porta 3389"]
M["smb-decoy<br/>Server SMB Impacket<br/>porta 445"]
end
end
A --> NIC
NIC --> E
NIC --> P
E --> L
P --> S
P --> D
P --> M
Quattro container in totale:
| Container | Ruolo | Rete |
|---|---|---|
broker | Porta pubblica: osservazione eBPF più proxy inverso | edge + decoynet |
ssh-decoy | Modulo ssh OpenCanary (handshake reale, cattura credenziali) | solo decoynet |
rdp-decoy | Modulo rdp OpenCanary (imitazione NLA, cattura nomi utente) | solo decoynet |
smb-decoy | SimpleSMBServer Impacket (SMB2/3 reale, cattura autenticazione) | solo decoynet |
I decoy vivono su una rete Docker internal (decoynet) senza route verso
l'host o il mondo esterno. Solo il broker può raggiungerli. Niente che un
attaccante faccia all'interno di un decoy può raggiungere direttamente la rete host.
Il broker pubblica le porte 22, 3389 e 445 all'host, quindi i pacchetti in entrata
arrivano su eth0 del broker. Due cose accadono quindi a ogni pacchetto:
broker/bpf/decoy.bpf.c) analizza le
intestazioni Ethernet, IP e TCP, e per ogni tentativo di nuova connessione (SYN
impostato, ACK non impostato) scrive un conn_event in un ring buffer: IP e
porta sorgente, porta di destinazione, flag TCP e se la porta è un servizio
pubblicizzato. Il pacchetto viene inoltrato inalterato (TC_ACT_OK).CONNECT al backend decoy per quel servizio,
quindi inoltra i byte in entrambe le direzioni.La mappa eBPF advertised_ports viene popolata all'avvio da config.yaml,
così il classificatore può etichettare se un probe ha colpito una porta servita
o una non richiesta. Questo rende visibili gli scan di porte orizzontali anche se
solo tre porte sono proxyate.
Se vuoi pubblicizzare "tutto è aperto" e convogliare porte di destinazione arbitrarie
nel broker, estendi il classificatore per riscrivere la porta di destinazione o usa
un reindirizzamento TPROXY / bpf_sk_assign. La versione corrente lascia il percorso
dei pacchetti intatto e si limita all'osservazione, che è l'impostazione predefinita
più sicura.
cyber-decoy/
├── README.md
├── docker-compose.yml # Stack a 4 container
├── docker-compose.override.yml # Sviluppo locale macOS: nessuna capacità eBPF, rimappatura porta 22
├── Makefile # Helper build / up / down / bpf
├── LICENSE
├── scripts/
│ └── setup.sh # Controlli preliminari host
├── broker/
│ ├── Dockerfile # Compila oggetto eBPF + binario Go
│ ├── config.yaml # Servizi pubblicizzati (configurabili)
│ ├── go.mod
│ ├── main.go # Punto di ingresso
│ ├── bpf/
│ │ └── decoy.bpf.c # Classificatore eBPF TC
│ └── internal/
│ ├── config/config.go # Caricatore configurazione
│ ├── proxy/proxy.go # Proxy inverso TCP
│ └── bpf/loader.go # Carica + collega eBPF, flusso eventi
└── decoys/ # Tutti e tre eseguono OpenCanary
├── ssh/
│ ├── Dockerfile
│ └── opencanary.conf # Modulo ssh, porta 2222
├── rdp/
│ ├── Dockerfile
│ └── opencanary.conf # Modulo rdp, porta 3389
└── smb/
├── Dockerfile # Singolo processo Python, non-root
├── smb_decoy.py # SimpleSMBServer Impacket + logging JSON
└── requirements.txt # impacket (bloccato)
docker-compose.override.yml in bundle, che si basa sui tag !reset / !override).sudo mount -t bpf bpf /sys/fs/bpf.L'immagine del broker rileva la sua architettura di build e passa la macro
__TARGET_ARCH_* corrispondente a clang, quindi si compila sia su x86_64 che su aarch64
(Apple Silicon, Graviton). Nota che gcc-multilib è deliberatamente non installato:
è un pacchetto solo x86 senza candidato arm64, e includerlo rompe la build su arm64 con
codice di uscita apt 100. Solo clang e libbpf-dev sono necessari per compilare l'oggetto eBPF.
Docker Desktop su macOS esegue i container all'interno di una VM LinuxKit anziché sul kernel
host, quindi l'attacco TC/TCX eBPF generalmente non funzionerà lì. Questo non è
fatale: eBPF è best effort per progettazione, quindi il broker registra ebpf disabled: attach failed e il proxy inverso più tutti e tre i decoy funzionano e registrano normalmente. Puoi
sviluppare e testare l'intero percorso del proxy localmente, quindi ottenere la vera osservazione
eBPF quando distribuisci su un host Linux.
docker-compose.override.yml viene caricato automaticamente e rende piacevole questa esperienza:
rimuove le capacità eBPF (inutili nella VM) e rimappa la porta host 22 sulla 2022,
poiché il sshd del Mac possiede la 22.
docker compose up --build # sviluppo locale, override applicato
docker compose -f docker-compose.yml up -d # distribuzione reale, override bypassato
Esegui prima il controllo preliminare:
./scripts/setup.sh
# 1. Compila tutte e quattro le immagini (compila l'oggetto eBPF all'interno dell'immagine broker)
make build
# 2. Avvia lo stack
make up
# 3. Guarda cosa succede
make logs
Quindi sondalo da un'altra macchina (o localhost per un test rapido):
ssh -p 22 utente@DECOY_HOST # colpisce il decoy SSH
nc DECOY_HOST 3389 # colpisce il decoy RDP
nc DECOY_HOST 445 # colpisce il decoy SMB
nc DECOY_HOST 8080 # non pubblicizzato: osservato da eBPF, nessun proxy
Il broker emette JSON per eventi di probe eBPF e sessioni proxyate; ogni decoy emette eventi JSON OpenCanary. Per vedere le credenziali che arrivano: