Add comprehensive README.md
Covers how it works, requirements, installation, quick start, full CLI reference, archetypes table, services table, OS fingerprint spoofing, distro profiles, INI config file format with field reference, logging, network drivers (MACVLAN vs IPvlan), architecture overview, plugin authoring guide, and test suite summary. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
662
README.md
662
README.md
@@ -1,139 +1,633 @@
|
||||
# DECNET
|
||||
|
||||
A honeypot/deception network framework. Deploys fake machines (**deckies**) with realistic services (SSH, SMB, RDP, FTP, HTTP) that appear as real LAN hosts — complete with their own MACs and IPs — to lure, detect, and profile attackers. All interactions are forwarded to an isolated logging pipeline (ELK / SIEM).
|
||||
A honeypot deception network framework. Spin up a fleet of fake machines — called **deckies** — that appear as real, heterogeneous LAN hosts to anyone scanning the network. Each decky gets its own MAC address, IP, hostname, services, OS fingerprint, and log pipeline.
|
||||
|
||||
Attackers probe the network, DECNET traps every interaction, and you watch from a safe, isolated logging stack.
|
||||
|
||||
---
|
||||
|
||||
## Table of Contents
|
||||
|
||||
- [How It Works](#how-it-works)
|
||||
- [Requirements](#requirements)
|
||||
- [Installation](#installation)
|
||||
- [Quick Start](#quick-start)
|
||||
- [CLI Reference](#cli-reference)
|
||||
- [Archetypes](#archetypes)
|
||||
- [Services](#services)
|
||||
- [OS Fingerprint Spoofing](#os-fingerprint-spoofing)
|
||||
- [Distro Profiles](#distro-profiles)
|
||||
- [Config File](#config-file)
|
||||
- [Logging](#logging)
|
||||
- [Network Drivers](#network-drivers)
|
||||
- [Architecture](#architecture)
|
||||
- [Writing a Custom Service Plugin](#writing-a-custom-service-plugin)
|
||||
- [Development & Testing](#development--testing)
|
||||
|
||||
---
|
||||
|
||||
## How It Works
|
||||
|
||||
```
|
||||
attacker ──► decoy network (deckies)
|
||||
Attacker scans 192.168.1.110–119
|
||||
│
|
||||
└──► log forwarder ──► isolated SIEM (ELK)
|
||||
▼
|
||||
┌──────────────────────────────────────────────┐
|
||||
│ DECNET LAN (MACVLAN) │
|
||||
│ │
|
||||
│ decky-01 192.168.1.110 ssh + http │
|
||||
│ decky-02 192.168.1.111 rdp + smb + mssql │
|
||||
│ decky-03 192.168.1.112 mqtt + snmp │
|
||||
│ ... │
|
||||
└──────────────────────────────────────────────┘
|
||||
│
|
||||
▼ all interactions forwarded via RFC 5424 syslog
|
||||
┌──────────────────────┐
|
||||
│ ELK / SIEM stack │ (isolated network — not reachable from decoys)
|
||||
└──────────────────────┘
|
||||
```
|
||||
|
||||
Each decky is a small cluster of Docker containers sharing one network namespace:
|
||||
|
||||
- **Base container** — holds the MACVLAN IP, sets TCP/IP stack sysctls for OS fingerprint spoofing, runs `sleep infinity`.
|
||||
- **Service containers** — one per honeypot service, all sharing the base's network so they appear to come from the same IP.
|
||||
|
||||
From the outside a decky looks identical to a real machine: it has its own MAC address (assigned by MACVLAN), its own IP, its own hostname, and its TCP/IP stack behaves like the OS it is pretending to be.
|
||||
|
||||
---
|
||||
|
||||
## Requirements
|
||||
|
||||
- Python ≥ 3.11
|
||||
- Docker + Docker Compose
|
||||
- Root / `sudo` for MACVLAN networking (bare metal or VM recommended; WSL has known limitations)
|
||||
- Linux host (bare metal or VM — WSL has MACVLAN limitations)
|
||||
- Docker Engine 24+
|
||||
- Python 3.11+
|
||||
- Root / `sudo` for network setup (MACVLAN creation, host interface config)
|
||||
- NIC in promiscuous mode for MACVLAN (or use `--ipvlan` on WiFi)
|
||||
|
||||
---
|
||||
|
||||
## Install
|
||||
## Installation
|
||||
|
||||
```bash
|
||||
git clone <repo-url> DECNET
|
||||
cd DECNET
|
||||
pip install -e .
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Usage
|
||||
Verify:
|
||||
|
||||
```bash
|
||||
# List available honeypot service plugins
|
||||
decnet services
|
||||
|
||||
# Dry-run — generate compose file, no containers started
|
||||
decnet deploy --mode unihost --deckies 3 --randomize-services --dry-run
|
||||
|
||||
# Deploy 5 deckies with random services
|
||||
sudo decnet deploy --mode unihost --deckies 5 --interface eth0 --randomize-services
|
||||
|
||||
# Deploy with specific services and log forwarding
|
||||
sudo decnet deploy --mode unihost --deckies 3 --services ssh,smb --log-target 192.168.1.5:5140
|
||||
|
||||
# Deploy from an INI config file
|
||||
sudo decnet deploy --config decnet.ini
|
||||
|
||||
# Status
|
||||
decnet status
|
||||
|
||||
# Teardown
|
||||
sudo decnet teardown --all
|
||||
sudo decnet teardown --id decky-01
|
||||
decnet --help
|
||||
decnet services # list all 25 registered honeypot services
|
||||
decnet archetypes # list machine archetype profiles
|
||||
decnet distros # list available OS distro profiles
|
||||
```
|
||||
|
||||
### Key flags
|
||||
---
|
||||
|
||||
## Quick Start
|
||||
|
||||
### Dry run — generate compose, no containers
|
||||
|
||||
```bash
|
||||
decnet deploy --mode unihost --deckies 5 --randomize-services --dry-run
|
||||
```
|
||||
|
||||
### Deploy with random services
|
||||
|
||||
```bash
|
||||
sudo decnet deploy --mode unihost --deckies 5 --interface eth0 --randomize-services
|
||||
```
|
||||
|
||||
### Deploy a specific role
|
||||
|
||||
```bash
|
||||
sudo decnet deploy --mode unihost --deckies 3 --archetype windows-workstation
|
||||
```
|
||||
|
||||
### Deploy from a config file
|
||||
|
||||
```bash
|
||||
sudo decnet deploy --config test-full.ini
|
||||
```
|
||||
|
||||
### Check status
|
||||
|
||||
```bash
|
||||
decnet status
|
||||
```
|
||||
|
||||
### Tear everything down
|
||||
|
||||
```bash
|
||||
sudo decnet teardown --all
|
||||
sudo decnet teardown --id decky-02 # single decky
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## CLI Reference
|
||||
|
||||
### `decnet deploy`
|
||||
|
||||
| Flag | Default | Description |
|
||||
|---|---|---|
|
||||
| `--mode` | `unihost` | Deployment mode: `unihost` or `swarm` |
|
||||
| `--deckies` / `-n` | — | Number of deckies to deploy (required without `--config`) |
|
||||
| `--interface` / `-i` | auto-detected | Host NIC to attach MACVLAN to |
|
||||
| `--subnet` | auto-detected | LAN subnet CIDR, e.g. `192.168.1.0/24` |
|
||||
| `--ip-start` | auto | First IP to assign to deckies |
|
||||
| `--services` | — | Comma-separated service slugs, e.g. `ssh,smb,rdp` |
|
||||
| `--randomize-services` | false | Assign random services to each decky |
|
||||
| `--distro` | auto-cycled | Comma-separated distro slugs, e.g. `debian,ubuntu22` |
|
||||
| `--randomize-distros` | false | Assign a random distro to each decky |
|
||||
| `--archetype` / `-a` | — | Machine archetype slug (sets services + OS family automatically) |
|
||||
| `--log-target` | — | Forward logs to `ip:port` (RFC 5424 syslog) |
|
||||
| `--log-file` | — | Write logs to this path inside containers |
|
||||
| `--ipvlan` | false | Use IPvlan L2 instead of MACVLAN (required on WiFi) |
|
||||
| `--dry-run` | false | Generate compose file without starting containers |
|
||||
| `--no-cache` | false | Force rebuild all images |
|
||||
| `--config` / `-c` | — | Path to INI config file |
|
||||
|
||||
### `decnet status`
|
||||
|
||||
Print a table of all deployed deckies, their IPs, services, hostnames, and container states.
|
||||
|
||||
### `decnet teardown`
|
||||
|
||||
| Flag | Description |
|
||||
|---|---|
|
||||
| `--mode` | `unihost` (single host) or `swarm` (multi-host) |
|
||||
| `--deckies N` | Number of fake machines to spin up |
|
||||
| `--interface` | Host NIC (auto-detected if omitted) |
|
||||
| `--subnet` | LAN subnet CIDR (auto-detected if omitted) |
|
||||
| `--ip-start` | First decky IP (auto if omitted) |
|
||||
| `--services` | Comma-separated list: `ssh,smb,rdp,ftp,http` |
|
||||
| `--randomize-services` | Assign random service mix to each decky |
|
||||
| `--log-target` | Forward logs to `ip:port` (e.g. Logstash) |
|
||||
| `--dry-run` | Generate compose file without starting containers |
|
||||
| `--no-cache` | Force rebuild all images |
|
||||
| `--config` | Path to INI config file |
|
||||
| `--all` | Tear down all deckies and remove the MACVLAN network |
|
||||
| `--id <name>` | Stop and remove a single decky by name |
|
||||
|
||||
### `decnet services`
|
||||
|
||||
List all registered honeypot service plugins with their ports and Docker images.
|
||||
|
||||
### `decnet distros`
|
||||
|
||||
List all available OS distro profiles.
|
||||
|
||||
### `decnet archetypes`
|
||||
|
||||
List all machine archetype profiles with their default services and descriptions.
|
||||
|
||||
---
|
||||
|
||||
## Deployment Modes
|
||||
## Archetypes
|
||||
|
||||
**UNIHOST** — one real host spins up _n_ deckies via Docker Compose. Simplest setup, single machine.
|
||||
Archetypes are pre-packaged machine identities. One slug sets services, preferred distros, and OS fingerprint all at once — no need to think about individual components.
|
||||
|
||||
**SWARM (MULTIHOST)** — _n_ real hosts each running deckies. Orchestrated via Ansible or similar tooling.
|
||||
| Slug | Services | OS Fingerprint | Description |
|
||||
|---|---|---|---|
|
||||
| `windows-workstation` | smb, rdp | windows | Corporate Windows desktop |
|
||||
| `windows-server` | smb, rdp, ldap | windows | Windows domain member |
|
||||
| `domain-controller` | ldap, smb, rdp, llmnr | windows | Active Directory DC |
|
||||
| `linux-server` | ssh, http | linux | General-purpose Linux host |
|
||||
| `web-server` | http, ftp | linux | Public-facing web host |
|
||||
| `database-server` | mysql, postgres, redis | linux | Data tier host |
|
||||
| `mail-server` | smtp, pop3, imap | linux | SMTP/IMAP/POP3 relay |
|
||||
| `file-server` | smb, ftp, ssh | linux | SMB/FTP/SFTP storage node |
|
||||
| `printer` | snmp, ftp | embedded | Network-attached printer |
|
||||
| `iot-device` | mqtt, snmp, telnet | embedded | Embedded/IoT device |
|
||||
| `industrial-control` | conpot, snmp | embedded | ICS/SCADA node |
|
||||
| `voip-server` | sip | linux | SIP PBX / VoIP gateway |
|
||||
| `monitoring-node` | snmp, ssh | linux | Infrastructure monitoring host |
|
||||
| `devops-host` | docker_api, ssh, k8s | linux | CI/CD / container host |
|
||||
|
||||
#### CLI
|
||||
|
||||
```bash
|
||||
sudo decnet deploy --deckies 4 --archetype windows-workstation
|
||||
```
|
||||
|
||||
#### INI
|
||||
|
||||
```ini
|
||||
[corp-workstations]
|
||||
archetype = windows-workstation
|
||||
amount = 4
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Services
|
||||
|
||||
25 honeypot services are registered out of the box. Use their slug in `--services` or `services=` in a config file.
|
||||
|
||||
| Slug | Ports | Protocol / Role |
|
||||
|---|---|---|
|
||||
| `ssh` | 22 | SSH (Cowrie honeypot) |
|
||||
| `http` | 80, 443 | HTTP/HTTPS web server |
|
||||
| `ftp` | 21 | FTP file transfer |
|
||||
| `tftp` | 69 | TFTP (trivial file transfer) |
|
||||
| `smb` | 445, 139 | SMB/CIFS file shares |
|
||||
| `rdp` | 3389 | Remote Desktop Protocol |
|
||||
| `telnet` | 23 | Telnet remote access |
|
||||
| `vnc` | 5900 | VNC remote desktop |
|
||||
| `smtp` | 25, 587 | SMTP mail relay |
|
||||
| `imap` | 143, 993 | IMAP mail access |
|
||||
| `pop3` | 110, 995 | POP3 mail access |
|
||||
| `ldap` | 389, 636 | LDAP / Active Directory |
|
||||
| `llmnr` | 5355, 5353 | LLMNR / mDNS (Windows name resolution) |
|
||||
| `mysql` | 3306 | MySQL database |
|
||||
| `postgres` | 5432 | PostgreSQL database |
|
||||
| `mssql` | 1433 | Microsoft SQL Server |
|
||||
| `mongodb` | 27017 | MongoDB document store |
|
||||
| `redis` | 6379 | Redis key-value store |
|
||||
| `elasticsearch` | 9200 | Elasticsearch REST API |
|
||||
| `mqtt` | 1883 | MQTT IoT broker |
|
||||
| `snmp` | 161 | SNMP network management |
|
||||
| `sip` | 5060 | SIP VoIP protocol |
|
||||
| `k8s` | 6443, 8080 | Kubernetes API server |
|
||||
| `docker_api` | 2375, 2376 | Docker Remote API |
|
||||
| `conpot` | 502, 161, 80 | ICS/SCADA (Modbus, S7, DNP3) |
|
||||
|
||||
List live at any time with `decnet services`.
|
||||
|
||||
### Per-service persona config
|
||||
|
||||
Most services accept persona configuration to make honeypot responses more convincing. Config is passed via INI subsections (`[decky-name.service]`) or the `service_config` field in code.
|
||||
|
||||
```ini
|
||||
[decky-webmail.http]
|
||||
server_header = Apache/2.4.54 (Debian)
|
||||
fake_app = wordpress
|
||||
|
||||
[decky-winbox.smb]
|
||||
workgroup = CORP
|
||||
server_name = WINSRV-DC01
|
||||
os_version = Windows Server 2016
|
||||
|
||||
[decky-legacy.ssh]
|
||||
ssh_version = OpenSSH_7.4p1 Debian-10+deb9u7
|
||||
kernel_version = 4.9.0-19-amd64
|
||||
users = root:root,admin:password
|
||||
```
|
||||
|
||||
### Bring-your-own service (BYOS)
|
||||
|
||||
Drop in a custom service definition using the `custom-` prefix in an INI config:
|
||||
|
||||
```ini
|
||||
[custom-myapp]
|
||||
binary = my-docker-image:latest
|
||||
exec = /usr/bin/myapp -p 9999
|
||||
ports = 9999
|
||||
```
|
||||
|
||||
The service is registered at runtime and can be referenced as `myapp` in any decky's `services=` list.
|
||||
|
||||
---
|
||||
|
||||
## OS Fingerprint Spoofing
|
||||
|
||||
DECNET injects Linux kernel TCP/IP stack parameters (`sysctls`) into each decky's base container so that active OS detection (e.g. `nmap -O`) returns the expected OS rather than "Linux".
|
||||
|
||||
The most important probe nmap uses is the IP TTL. Secondary tuning covers TCP SYN retry behaviour and initial receive window size.
|
||||
|
||||
### OS families
|
||||
|
||||
| Family | TTL | `tcp_syn_retries` | Notes |
|
||||
|---|---|---|---|
|
||||
| `linux` | 64 | 6 | Default |
|
||||
| `windows` | 128 | 2 | + 8 MB recv buffer |
|
||||
| `bsd` | 64 | 6 | FreeBSD / macOS-style |
|
||||
| `embedded` | 255 | 3 | Printers, IoT, PLCs |
|
||||
| `cisco` | 255 | 2 | Network devices |
|
||||
|
||||
Because service containers share the base container's network namespace (`network_mode: service:<base>`), the spoofed stack applies to **all** traffic from the decky — no per-service config needed.
|
||||
|
||||
### Automatic via archetype
|
||||
|
||||
Archetypes set `nmap_os` automatically. A `windows-workstation` decky comes with TTL 128 out of the box.
|
||||
|
||||
### Explicit in INI
|
||||
|
||||
```ini
|
||||
[decky-winbox]
|
||||
services = rdp, smb, mssql
|
||||
nmap_os = windows # also accepts nmap-os=
|
||||
|
||||
[decky-iot]
|
||||
services = mqtt, snmp
|
||||
nmap_os = embedded
|
||||
|
||||
[decky-legacy]
|
||||
services = telnet, vnc, ssh
|
||||
nmap_os = bsd
|
||||
```
|
||||
|
||||
Priority: **explicit `nmap_os=`** > archetype default > `linux`.
|
||||
|
||||
### Verify with nmap
|
||||
|
||||
```bash
|
||||
sudo nmap -O 192.168.1.114 # should report Windows
|
||||
sudo nmap -O 192.168.1.117 # should report embedded / network device
|
||||
```
|
||||
|
||||
> **Note:** Linux kernel containers cannot perfectly replicate every nmap OS probe (sequence generation, ECN flags, etc.). TTL and TCP window tuning cover the most reliable detection vectors. Full impersonation would require a userspace TCP stack.
|
||||
|
||||
---
|
||||
|
||||
## Distro Profiles
|
||||
|
||||
The distro controls which Docker base image is used for the IP-holding base container, giving each decky a different OS identity at the image layer and varying the hostname style.
|
||||
|
||||
| Slug | Docker Image | Display Name |
|
||||
|---|---|---|
|
||||
| `debian` | `debian:bookworm-slim` | Debian 12 (Bookworm) |
|
||||
| `ubuntu22` | `ubuntu:22.04` | Ubuntu 22.04 LTS (Jammy) |
|
||||
| `ubuntu20` | `ubuntu:20.04` | Ubuntu 20.04 LTS (Focal) |
|
||||
| `rocky9` | `rockylinux:9-minimal` | Rocky Linux 9 |
|
||||
| `centos7` | `centos:7` | CentOS 7 |
|
||||
| `alpine` | `alpine:3.19` | Alpine Linux 3.19 |
|
||||
| `fedora` | `fedora:39` | Fedora 39 |
|
||||
| `kali` | `kalilinux/kali-rolling` | Kali Linux (Rolling) |
|
||||
| `arch` | `archlinux:latest` | Arch Linux |
|
||||
|
||||
When no distro is specified, DECNET cycles through all profiles in round-robin to maximise heterogeneity automatically.
|
||||
|
||||
```bash
|
||||
# Explicit single distro
|
||||
sudo decnet deploy --deckies 3 --services ssh --distro rocky9
|
||||
|
||||
# Mix of distros (cycled)
|
||||
sudo decnet deploy --deckies 6 --services ssh --distro debian,ubuntu22,rocky9
|
||||
|
||||
# Fully random
|
||||
sudo decnet deploy --deckies 5 --randomize-services --randomize-distros
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Config File
|
||||
|
||||
For anything beyond a handful of deckies, use an INI config file. It gives you per-decky IPs, per-service personas, archetype pools, and custom service definitions all in one place.
|
||||
|
||||
```bash
|
||||
decnet deploy --config mynet.ini --dry-run
|
||||
sudo decnet deploy --config mynet.ini --log-target 192.168.1.200:5140
|
||||
```
|
||||
|
||||
### Structure
|
||||
|
||||
```ini
|
||||
# ── Global settings ───────────────────────────────────────────────────────────
|
||||
|
||||
[general]
|
||||
net = 192.168.1.0/24 # subnet CIDR
|
||||
gw = 192.168.1.1 # gateway IP
|
||||
interface = eth0 # host NIC (optional, auto-detected if omitted)
|
||||
log_target = 192.168.1.200:5140 # syslog forwarding target (optional)
|
||||
|
||||
# ── Decky sections ────────────────────────────────────────────────────────────
|
||||
|
||||
[decky-01]
|
||||
ip = 192.168.1.110 # optional; auto-allocated if omitted
|
||||
services = ssh, http # comma-separated service slugs
|
||||
nmap_os = linux # OS fingerprint family (optional, default: linux)
|
||||
|
||||
# ── Per-service persona ───────────────────────────────────────────────────────
|
||||
|
||||
[decky-01.ssh]
|
||||
ssh_version = OpenSSH_8.9p1 Ubuntu-3ubuntu0.6
|
||||
kernel_version = 5.15.0-91-generic
|
||||
users = root:toor,admin:admin123
|
||||
|
||||
[decky-01.http]
|
||||
server_header = nginx/1.18.0
|
||||
fake_app = wordpress
|
||||
|
||||
# ── Archetype shorthand ───────────────────────────────────────────────────────
|
||||
|
||||
[corp-workstations]
|
||||
archetype = windows-workstation # sets services, distros, and nmap_os automatically
|
||||
amount = 10 # spawn 10 deckies from this definition
|
||||
|
||||
# ── Bring-your-own service ────────────────────────────────────────────────────
|
||||
|
||||
[custom-myapp]
|
||||
binary = my-image:latest
|
||||
exec = /usr/bin/myapp -p 9999
|
||||
ports = 9999
|
||||
```
|
||||
|
||||
### Field reference
|
||||
|
||||
#### `[general]`
|
||||
|
||||
| Key | Required | Description |
|
||||
|---|---|---|
|
||||
| `net` | Yes | Subnet CIDR for the decoy LAN |
|
||||
| `gw` | Yes | Gateway IP |
|
||||
| `interface` | No | Host NIC; auto-detected if absent |
|
||||
| `log_target` | No | `ip:port` for RFC 5424 syslog forwarding |
|
||||
|
||||
#### Decky sections
|
||||
|
||||
| Key | Required | Description |
|
||||
|---|---|---|
|
||||
| `ip` | No | Static IP; auto-allocated from subnet if absent |
|
||||
| `services` | See note | Comma-separated service slugs |
|
||||
| `archetype` | See note | Archetype slug; sets services + nmap_os unless overridden |
|
||||
| `nmap_os` | No | OS fingerprint family: `linux` / `windows` / `bsd` / `embedded` / `cisco` |
|
||||
| `amount` | No | Spawn N deckies from this block (default: 1); cannot combine with `ip=` |
|
||||
|
||||
> One of `services=`, `archetype=`, or `--randomize-services` is required per decky.
|
||||
|
||||
#### Per-service subsections `[decky-name.service]`
|
||||
|
||||
Key/value pairs are passed directly to the service plugin as persona config. Common keys:
|
||||
|
||||
| Service | Accepted keys |
|
||||
|---|---|
|
||||
| `ssh` | `ssh_version`, `kernel_version`, `users` |
|
||||
| `http` | `server_header`, `response_code`, `fake_app` |
|
||||
| `smtp` | `smtp_banner`, `smtp_mta` |
|
||||
| `smb` | `workgroup`, `server_name`, `os_version` |
|
||||
| `rdp` | `os_version`, `build` |
|
||||
| `mysql` | `mysql_version`, `mysql_banner` |
|
||||
| `redis` | `redis_version` |
|
||||
| `postgres` | `pg_version` |
|
||||
| `mongodb` | `mongo_version` |
|
||||
| `elasticsearch` | `es_version`, `cluster_name` |
|
||||
| `ldap` | `base_dn`, `domain` |
|
||||
| `snmp` | `snmp_community`, `sys_descr` |
|
||||
| `mqtt` | `mqtt_version` |
|
||||
| `sip` | `sip_server`, `sip_domain` |
|
||||
| `k8s` | `k8s_version` |
|
||||
| `docker_api` | `docker_version` |
|
||||
| `vnc` | `vnc_version` |
|
||||
| `mssql` | `mssql_version` |
|
||||
|
||||
When using `amount=`, a subsection like `[group-name.ssh]` automatically propagates to all expanded deckies (`group-name-01`, `group-name-02`, …).
|
||||
|
||||
### Full example
|
||||
|
||||
See [`test-full.ini`](test-full.ini) — covers all 25 services across 10 role-themed deckies with per-service personas, archetype pools, OS fingerprint assignments, and inline comments explaining each choice.
|
||||
|
||||
---
|
||||
|
||||
## Logging
|
||||
|
||||
All attacker interactions are forwarded off the decoy network to an isolated logging sink. The log pipeline lives on a separate internal Docker bridge (`decnet_logs`) that is not reachable from the fake LAN.
|
||||
|
||||
### Syslog forwarding (RFC 5424)
|
||||
|
||||
```bash
|
||||
sudo decnet deploy --config mynet.ini --log-target 192.168.1.200:5140
|
||||
```
|
||||
|
||||
Or in `[general]`:
|
||||
|
||||
```ini
|
||||
log_target = 192.168.1.200:5140
|
||||
```
|
||||
|
||||
### File logging
|
||||
|
||||
```bash
|
||||
sudo decnet deploy --config mynet.ini --log-file /var/log/decnet/decnet.log
|
||||
```
|
||||
|
||||
The log directory is bind-mounted into every service container. Log entries follow RFC 5424 syslog format.
|
||||
|
||||
### Log target health check
|
||||
|
||||
Before deployment, DECNET probes the log target and warns if it is unreachable:
|
||||
|
||||
```
|
||||
Warning: log target 192.168.1.200:5140 is unreachable. Logs will be lost if it stays down.
|
||||
```
|
||||
|
||||
Deployment continues regardless — the log target can come up later.
|
||||
|
||||
---
|
||||
|
||||
## Network Drivers
|
||||
|
||||
### MACVLAN (default)
|
||||
|
||||
Each decky gets a unique MAC address assigned by the kernel, making it appear as a distinct physical machine on the LAN. Requires the host NIC to support promiscuous mode.
|
||||
|
||||
```bash
|
||||
sudo decnet deploy --interface eth0 --deckies 5 --randomize-services
|
||||
```
|
||||
|
||||
**Known limitation:** The host cannot communicate directly with its own MACVLAN children by default. DECNET automatically creates a `decnet_macvlan0` host-side interface as a hairpin workaround so that `decnet status` and log collection continue to work from the host.
|
||||
|
||||
### IPvlan L2 (`--ipvlan`)
|
||||
|
||||
Use IPvlan L2 when MACVLAN is not available — typically on WiFi interfaces where the access point filters non-registered MACs. IPvlan shares the host MAC and gives each decky a unique IP only.
|
||||
|
||||
```bash
|
||||
sudo decnet deploy --interface wlp6s0 --ipvlan --deckies 3 --randomize-services
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Architecture
|
||||
|
||||
- **Containers**: Docker Compose with `debian:bookworm-slim` as the default base image. Mixing Ubuntu, CentOS, and other distros is encouraged to make the decoy network look heterogeneous.
|
||||
- **Networking**: MACVLAN/IPVLAN — each decky gets its own MAC and IP, appearing as a distinct real machine on the LAN.
|
||||
- **Log pipeline**: Logstash → ELK stack → SIEM on an isolated network unreachable from the decoy network.
|
||||
- **Services**: Plugin-based registry (`decnet/services/`). Each plugin declares its ports, default image, and container config.
|
||||
|
||||
```
|
||||
decnet/
|
||||
├── cli.py # Typer CLI — deploy, status, teardown, services
|
||||
├── config.py # Pydantic models (DecnetConfig, DeckyConfig)
|
||||
├── composer.py # Docker Compose YAML generator
|
||||
├── deployer.py # Container lifecycle management
|
||||
├── network.py # IP allocation, interface/subnet detection
|
||||
├── ini_loader.py # INI config file support
|
||||
├── cli.py # Typer CLI entry point; builds DecnetConfig from flags/INI
|
||||
├── config.py # Pydantic models: DeckyConfig, DecnetConfig; state persistence
|
||||
├── composer.py # Generates docker-compose.yml from DecnetConfig
|
||||
├── deployer.py # Docker SDK: bring-up, teardown, status
|
||||
├── network.py # MACVLAN/IPvlan creation, IP allocation, hairpin interface
|
||||
├── archetypes.py # Machine archetype profiles (14 built-in)
|
||||
├── distros.py # OS distro profiles (9 built-in), hostname generation
|
||||
├── os_fingerprint.py # TCP/IP sysctl profiles per OS family for nmap spoofing
|
||||
├── ini_loader.py # INI config file parser
|
||||
├── custom_service.py # Bring-your-own service runtime registration
|
||||
├── services/
|
||||
│ ├── base.py # BaseService ABC — contract every plugin must implement
|
||||
│ ├── registry.py # Auto-discovers and registers all BaseService subclasses
|
||||
│ └── *.py # 25 individual honeypot service plugins
|
||||
├── logging/
|
||||
│ └── forwarder.py # Log target probe + forwarding
|
||||
└── services/
|
||||
├── registry.py # Plugin registry
|
||||
├── ssh.py
|
||||
├── smb.py
|
||||
├── rdp.py
|
||||
├── ftp.py
|
||||
└── http.py
|
||||
│ ├── forwarder.py # RFC 5424 syslog UDP forwarder
|
||||
│ ├── file_handler.py
|
||||
│ └── syslog_formatter.py
|
||||
└── templates/ # Dockerfiles and service entrypoint scripts
|
||||
```
|
||||
|
||||
### Container model
|
||||
|
||||
```
|
||||
decky-01 (base) ← MACVLAN IP owner; sleep infinity; sysctls applied here
|
||||
├─ decky-01-ssh ← network_mode: service:decky-01 (shares IP + MAC)
|
||||
├─ decky-01-http ← network_mode: service:decky-01
|
||||
└─ decky-01-smb ← network_mode: service:decky-01
|
||||
```
|
||||
|
||||
Service containers carry no network config of their own. From the outside, every port on a decky appears to belong to a single machine.
|
||||
|
||||
---
|
||||
|
||||
## INI Config
|
||||
## Writing a Custom Service Plugin
|
||||
|
||||
You can describe a fully custom decoy fleet in an INI file instead of CLI flags:
|
||||
1. Create `decnet/services/myservice.py`:
|
||||
|
||||
```ini
|
||||
[global]
|
||||
interface = eth0
|
||||
log_target = 192.168.1.5:5140
|
||||
```python
|
||||
from decnet.services.base import BaseService
|
||||
|
||||
[decky-01]
|
||||
services = ssh,smb
|
||||
base_image = debian:bookworm-slim
|
||||
hostname = DESKTOP-A1B2C3
|
||||
class MyService(BaseService):
|
||||
name = "myservice"
|
||||
ports = [1234]
|
||||
default_image = "my-docker-image:latest"
|
||||
|
||||
[decky-02]
|
||||
services = rdp,http
|
||||
base_image = ubuntu:22.04
|
||||
hostname = WIN-SERVER-02
|
||||
def compose_fragment(self, decky_name, log_target=None, service_cfg=None):
|
||||
cfg = service_cfg or {}
|
||||
return {
|
||||
"image": self.default_image,
|
||||
"container_name": f"{decky_name}-myservice",
|
||||
"restart": "unless-stopped",
|
||||
"environment": {
|
||||
"MY_BANNER": cfg.get("banner", "default banner"),
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
2. The registry auto-discovers all `BaseService` subclasses at import time — no registration step needed.
|
||||
|
||||
3. Use it immediately:
|
||||
|
||||
```bash
|
||||
sudo decnet deploy --config decnet.ini
|
||||
decnet services # myservice appears in the list
|
||||
sudo decnet deploy --deckies 2 --services myservice
|
||||
```
|
||||
|
||||
For services that require a custom Dockerfile, set `default_image = "build"` and override `dockerfile_context()` to return the path to your build context directory. The composer injects `BASE_IMAGE` as a build arg so your Dockerfile picks up the correct distro image automatically:
|
||||
|
||||
```dockerfile
|
||||
ARG BASE_IMAGE=debian:bookworm-slim
|
||||
FROM ${BASE_IMAGE}
|
||||
...
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Adding a Service Plugin
|
||||
## Development & Testing
|
||||
|
||||
1. Create `decnet/services/yourservice.py` implementing the `BaseService` interface.
|
||||
2. Register it in `decnet/services/registry.py`.
|
||||
3. Verify with `decnet services`.
|
||||
```bash
|
||||
pip install -e .
|
||||
python -m pytest # 478 tests, < 1 second
|
||||
```
|
||||
|
||||
The test suite covers:
|
||||
|
||||
| File | What it tests |
|
||||
|---|---|
|
||||
| `test_composer.py` | Compose generation, BASE_IMAGE injection, distro heterogeneity |
|
||||
| `test_os_fingerprint.py` | OS sysctl profiles, compose injection, archetype coverage, CLI propagation |
|
||||
| `test_ini_loader.py` | INI parsing, subsection propagation, custom services, `nmap_os` |
|
||||
| `test_services.py` | Per-service persona config, compose fragments |
|
||||
| `test_network.py` | IP allocation, range calculation |
|
||||
| `test_log_file_mount.py` | Log directory bind-mount injection |
|
||||
| `test_syslog_formatter.py` | RFC 5424 syslog formatting |
|
||||
| `test_archetypes.py` | Archetype validation and field correctness |
|
||||
| `test_cli_service_pool.py` | CLI service resolution |
|
||||
|
||||
Every new feature requires passing tests before merging.
|
||||
|
||||
Reference in New Issue
Block a user