wiki(networking): add MACVLAN/IPvlan and Deployment Modes pages
175
Deployment-Modes.md
Normal file
175
Deployment-Modes.md
Normal file
@@ -0,0 +1,175 @@
|
|||||||
|
# Deployment Modes: UNIHOST and SWARM
|
||||||
|
|
||||||
|
DECNET's `deploy` command takes a `--mode` flag that selects how the fleet of
|
||||||
|
deckies is laid out across real hardware. Two modes are defined:
|
||||||
|
|
||||||
|
- `unihost` — a single real host runs all deckies as containers.
|
||||||
|
- `swarm` — multiple real hosts each run a subset of deckies (multi-host).
|
||||||
|
|
||||||
|
The mode is validated in `decnet/cli.py` (`deploy --mode`) and stored on the
|
||||||
|
`DecnetConfig` object consumed by `decnet/engine/deployer.py`. The orchestrator
|
||||||
|
in `decnet/composer.py` emits the Docker Compose file used by either mode.
|
||||||
|
|
||||||
|
See also: [CLI reference](CLI-Reference), [INI format](INI-Config-Format),
|
||||||
|
[Networking: MACVLAN and IPvlan](Networking-MACVLAN-IPVLAN),
|
||||||
|
[Teardown](Teardown-and-State).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## When to use which
|
||||||
|
|
||||||
|
| Criterion | UNIHOST | SWARM |
|
||||||
|
|---------------------------------|----------------------------|-------------------------------------|
|
||||||
|
| Hosts involved | 1 | N |
|
||||||
|
| Setup complexity | low | higher (needs fleet orchestration) |
|
||||||
|
| Deckies per host | all of them | split across hosts |
|
||||||
|
| Fits home lab / single server | yes | overkill |
|
||||||
|
| Fits multi-subnet / multi-site | no | yes |
|
||||||
|
| Failure blast radius | whole fleet on one box | isolated per host |
|
||||||
|
| Recommended for first runs | yes | no |
|
||||||
|
|
||||||
|
UNIHOST is the default (`--mode unihost`) and what the README's quickstart
|
||||||
|
assumes. SWARM is the deployment posture for a real honeynet spanning multiple
|
||||||
|
boxes.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## UNIHOST
|
||||||
|
|
||||||
|
```
|
||||||
|
attacker LAN 192.168.1.0/24
|
||||||
|
+---------------------------------------------------+
|
||||||
|
| |
|
||||||
|
| [host] eth0 decnet_lan (MACVLAN / IPvlan L2) |
|
||||||
|
| |- decky-01 .10 |
|
||||||
|
| |- decky-02 .11 |
|
||||||
|
| |- decky-03 .12 |
|
||||||
|
| |- decky-04 .13 |
|
||||||
|
| '- decky-05 .14 |
|
||||||
|
+---------------------------------------------------+
|
||||||
|
|
|
||||||
|
(isolated mgmt path)
|
||||||
|
v
|
||||||
|
logging / SIEM network
|
||||||
|
```
|
||||||
|
|
||||||
|
One host, one MACVLAN or IPvlan L2 network, N deckies. The host also runs the
|
||||||
|
collector/API and forwards logs out-of-band to the isolated logging network.
|
||||||
|
|
||||||
|
### CLI
|
||||||
|
|
||||||
|
```
|
||||||
|
# Dry-run: generate compose, no containers
|
||||||
|
decnet deploy --mode unihost --deckies 3 --randomize-services --dry-run
|
||||||
|
|
||||||
|
# Full deploy (root required for MACVLAN)
|
||||||
|
sudo decnet deploy \
|
||||||
|
--mode unihost \
|
||||||
|
--deckies 5 \
|
||||||
|
--interface eth0 \
|
||||||
|
--randomize-services
|
||||||
|
```
|
||||||
|
|
||||||
|
Flags worth knowing (full table in [CLI reference](CLI-Reference)):
|
||||||
|
|
||||||
|
- `--deckies / -n` — number of deckies on this host.
|
||||||
|
- `--interface / -i` — parent NIC. Auto-detected via `detect_interface()`.
|
||||||
|
- `--ipvlan` — switch to IPvlan L2. Required on WiFi.
|
||||||
|
- `--services`, `--randomize-services`, `--archetype` — service selection.
|
||||||
|
- `--distro`, `--randomize-distros` — OS heterogeneity.
|
||||||
|
- `--mutate-interval` — rotate services every N minutes.
|
||||||
|
- `--dry-run` — write the compose file under `deploy/` without starting.
|
||||||
|
|
||||||
|
### INI
|
||||||
|
|
||||||
|
The same deployment can be expressed declaratively via `--config`:
|
||||||
|
|
||||||
|
```
|
||||||
|
decnet deploy --mode unihost --config ./my-fleet.ini
|
||||||
|
```
|
||||||
|
|
||||||
|
See [INI format](INI-Config-Format) for the full schema; `cli.py`'s config
|
||||||
|
path calls `load_ini` and `build_deckies_from_ini`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## SWARM (multihost)
|
||||||
|
|
||||||
|
```
|
||||||
|
attacker network(s)
|
||||||
|
------------------------------
|
||||||
|
| | |
|
||||||
|
[host-A] [host-B] [host-C]
|
||||||
|
decky-01 decky-04 decky-07
|
||||||
|
decky-02 decky-05 decky-08
|
||||||
|
decky-03 decky-06 decky-09
|
||||||
|
|
||||||
|
\__________________|__________________/
|
||||||
|
|
|
||||||
|
isolated mgmt / SIEM
|
||||||
|
```
|
||||||
|
|
||||||
|
Each real host runs a UNIHOST-shaped deployment over its own slice of the IP
|
||||||
|
space. An external orchestrator (Ansible, sshpass-driven scripts, etc.)
|
||||||
|
invokes `decnet deploy --mode swarm ...` on each host in turn. The CLI
|
||||||
|
currently accepts `swarm` as a valid mode — the fleet-wide orchestration layer
|
||||||
|
lives outside the DECNET binary and is the operator's responsibility. See the
|
||||||
|
README's architecture section for the intended shape.
|
||||||
|
|
||||||
|
### CLI
|
||||||
|
|
||||||
|
Run on each host, coordinating IP ranges so deckies do not collide:
|
||||||
|
|
||||||
|
```
|
||||||
|
# host-A
|
||||||
|
sudo decnet deploy \
|
||||||
|
--mode swarm \
|
||||||
|
--deckies 3 \
|
||||||
|
--interface eth0 \
|
||||||
|
--ip-start 192.168.1.10 \
|
||||||
|
--randomize-services
|
||||||
|
|
||||||
|
# host-B
|
||||||
|
sudo decnet deploy \
|
||||||
|
--mode swarm \
|
||||||
|
--deckies 3 \
|
||||||
|
--interface eth0 \
|
||||||
|
--ip-start 192.168.1.20 \
|
||||||
|
--randomize-services
|
||||||
|
```
|
||||||
|
|
||||||
|
`--ip-start` is the operator's primary tool for partitioning the subnet across
|
||||||
|
hosts; `allocate_ips` in `decnet/network.py` starts sequentially from that
|
||||||
|
address and skips reserved / in-use IPs.
|
||||||
|
|
||||||
|
### INI
|
||||||
|
|
||||||
|
For reproducible swarm rollouts, give each host its own INI and drive the
|
||||||
|
rollout from Ansible (or similar):
|
||||||
|
|
||||||
|
```
|
||||||
|
decnet deploy --mode swarm --config ./host-A.ini
|
||||||
|
decnet deploy --mode swarm --config ./host-B.ini
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## What is common to both modes
|
||||||
|
|
||||||
|
- Compose generation in `decnet/composer.py` is mode-agnostic; the `mode`
|
||||||
|
field is recorded on `DecnetConfig` for state/telemetry but the compose
|
||||||
|
shape (base container + service containers sharing its network namespace
|
||||||
|
via `network_mode: service:<base>`) is identical.
|
||||||
|
- Network driver selection (MACVLAN vs IPvlan L2) is per-host, controlled by
|
||||||
|
`--ipvlan`. See [Networking: MACVLAN and IPvlan](Networking-MACVLAN-IPVLAN).
|
||||||
|
- Teardown is per-host: run `sudo decnet teardown --all` on each host in the
|
||||||
|
swarm. See [Teardown](Teardown-and-State).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Quick checklist
|
||||||
|
|
||||||
|
- Single box, one LAN -> UNIHOST.
|
||||||
|
- Multiple boxes across one or more LANs -> SWARM, driven by external
|
||||||
|
orchestration, with non-overlapping `--ip-start` per host.
|
||||||
|
- Not sure -> start with UNIHOST and a small `--deckies` count.
|
||||||
163
Networking-MACVLAN-IPVLAN.md
Normal file
163
Networking-MACVLAN-IPVLAN.md
Normal file
@@ -0,0 +1,163 @@
|
|||||||
|
# Networking: MACVLAN and IPvlan
|
||||||
|
|
||||||
|
DECNET's deckies must look like real, independent machines on the LAN. Each
|
||||||
|
decky owns an IP (and ideally a MAC) drawn from the same subnet as the host's
|
||||||
|
real NIC, so that attackers scanning the network see a heterogeneous fleet of
|
||||||
|
hosts rather than a single container host.
|
||||||
|
|
||||||
|
This page covers how that is wired up: the two Docker network drivers DECNET
|
||||||
|
supports, the host-side hairpin interface it creates, and the limitations you
|
||||||
|
will hit on WiFi or WSL.
|
||||||
|
|
||||||
|
Source of truth: `decnet/network.py` (constants `MACVLAN_NETWORK_NAME`,
|
||||||
|
`HOST_MACVLAN_IFACE`, `HOST_IPVLAN_IFACE`; functions `setup_host_macvlan`,
|
||||||
|
`teardown_host_macvlan`, `setup_host_ipvlan`, `teardown_host_ipvlan`).
|
||||||
|
|
||||||
|
See also: [CLI reference](CLI-Reference), [INI format](INI-Config-Format),
|
||||||
|
[Teardown](Teardown-and-State).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Topology
|
||||||
|
|
||||||
|
```
|
||||||
|
Internet / attacker
|
||||||
|
|
|
||||||
|
| (public IP, port forwards)
|
||||||
|
|
|
||||||
|
+-----------------+
|
||||||
|
| DECNET host | eth0 -> decnet_lan (MACVLAN / IPvlan L2)
|
||||||
|
+--------+--------+
|
||||||
|
|
|
||||||
|
==============+=================== LAN 192.168.1.0/24
|
||||||
|
| | |
|
||||||
|
decky-01 decky-02 decky-03 (each = own IP,
|
||||||
|
.10 .11 .12 MACVLAN = own MAC too)
|
||||||
|
|
||||||
|
|
|
||||||
|
+-------------+-------------+
|
||||||
|
| decnet_macvlan0 (host) | hairpin: host <-> deckies reachability
|
||||||
|
+---------------------------+
|
||||||
|
|
||||||
|
--- isolated mgmt path ---
|
||||||
|
host -> logging/SIEM network
|
||||||
|
(not reachable from decnet_lan)
|
||||||
|
```
|
||||||
|
|
||||||
|
The decoy LAN is attacker-facing. The logging/aggregation network is reachable
|
||||||
|
only from the host and must not be routable from `decnet_lan`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Driver 1: MACVLAN (default)
|
||||||
|
|
||||||
|
MACVLAN is the default driver. Each decky gets a kernel-assigned MAC address on
|
||||||
|
the parent interface, so it is a distinct L2 endpoint on the LAN.
|
||||||
|
|
||||||
|
- Docker network name: `decnet_lan` (constant `MACVLAN_NETWORK_NAME`).
|
||||||
|
- Parent NIC: `--interface` (auto-detected via `detect_interface()` if omitted).
|
||||||
|
- Mode: `macvlan / bridge` (see `create_macvlan_network` and the
|
||||||
|
`ip link add ... type macvlan mode bridge` call in `setup_host_macvlan`).
|
||||||
|
- Requires the parent NIC to support **promiscuous mode**. Most wired NICs do.
|
||||||
|
|
||||||
|
### Host hairpin (`decnet_macvlan0`)
|
||||||
|
|
||||||
|
MACVLAN has a well-known limitation: the host cannot talk directly to its own
|
||||||
|
MACVLAN children on the parent NIC. Without a workaround, `decnet status` and
|
||||||
|
the log collector (which run on the host) would be unable to reach deckies.
|
||||||
|
|
||||||
|
DECNET fixes this by creating a second host-side MACVLAN interface named
|
||||||
|
`decnet_macvlan0` (constant `HOST_MACVLAN_IFACE`) attached to the same parent
|
||||||
|
NIC, assigning it a /32 on the host, and routing the decky IP range out of it.
|
||||||
|
The relevant sequence in `setup_host_macvlan`:
|
||||||
|
|
||||||
|
```
|
||||||
|
ip link add decnet_macvlan0 link <iface> type macvlan mode bridge
|
||||||
|
ip addr add <host_macvlan_ip>/32 dev decnet_macvlan0
|
||||||
|
ip link set decnet_macvlan0 up
|
||||||
|
ip route add <decky_range> dev decnet_macvlan0
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Driver 2: IPvlan L2 (`--ipvlan`)
|
||||||
|
|
||||||
|
Pass `--ipvlan` to opt into IPvlan L2. Each decky gets a unique IP but all
|
||||||
|
share the host MAC. This is the fallback for environments where MACVLAN is not
|
||||||
|
workable:
|
||||||
|
|
||||||
|
- **WiFi.** Most access points drop frames whose source MAC is not registered
|
||||||
|
with the radio, so random MACVLAN MACs never reach the LAN. IPvlan L2 works
|
||||||
|
because every frame leaves with the (registered) host MAC.
|
||||||
|
- NICs that refuse promiscuous mode.
|
||||||
|
|
||||||
|
### Host hairpin (`decnet_ipvlan0`)
|
||||||
|
|
||||||
|
Same trick as MACVLAN, different link type. `setup_host_ipvlan` adds a
|
||||||
|
host-side interface named `decnet_ipvlan0` (constant `HOST_IPVLAN_IFACE`) of
|
||||||
|
type `ipvlan mode l2`, gives it a /32, and routes the decky range through it.
|
||||||
|
`teardown_host_ipvlan` removes the route and the link on teardown.
|
||||||
|
|
||||||
|
### Trade-offs
|
||||||
|
|
||||||
|
| Aspect | MACVLAN | IPvlan L2 |
|
||||||
|
|--------------------|----------------------------------|---------------------------------|
|
||||||
|
| Per-decky MAC | yes (unique, kernel-assigned) | no (shares host MAC) |
|
||||||
|
| Works on WiFi | usually no | yes |
|
||||||
|
| Promisc NIC needed | yes | no |
|
||||||
|
| Decoy realism | higher (distinct L2 identities) | lower (all share one MAC) |
|
||||||
|
| Attacker fingerprinting via `arp -a` | sees many MACs | sees one MAC, many IPs |
|
||||||
|
|
||||||
|
Prefer MACVLAN on wired, bare-metal or VM deployments. Use IPvlan L2 when the
|
||||||
|
environment forces it.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## WiFi and WSL limitations
|
||||||
|
|
||||||
|
- **WiFi:** MACVLAN typically does not work. Use `--ipvlan`.
|
||||||
|
- **WSL2:** MACVLAN has known limitations under WSL. Bare metal or a full Linux
|
||||||
|
VM is the supported testing environment (README "Requirements" section).
|
||||||
|
- **Promiscuous mode:** some virtualization platforms (including vSphere with
|
||||||
|
default security policies) block promiscuous mode on the vNIC; either relax
|
||||||
|
the policy on the virtual switch or use IPvlan.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Teardown
|
||||||
|
|
||||||
|
Teardown of the Docker network and the host hairpin interface is automatic
|
||||||
|
when you run `decnet teardown --all`. The relevant steps, from
|
||||||
|
`decnet/engine/deployer.py`:
|
||||||
|
|
||||||
|
```
|
||||||
|
if config.ipvlan:
|
||||||
|
teardown_host_ipvlan(decky_range)
|
||||||
|
else:
|
||||||
|
teardown_host_macvlan(decky_range)
|
||||||
|
remove_macvlan_network(client)
|
||||||
|
```
|
||||||
|
|
||||||
|
`teardown_host_macvlan` / `teardown_host_ipvlan` each do:
|
||||||
|
|
||||||
|
```
|
||||||
|
ip route del <decky_range> dev <iface> # decnet_macvlan0 or decnet_ipvlan0
|
||||||
|
ip link del <iface>
|
||||||
|
```
|
||||||
|
|
||||||
|
Both use `check=False`, so a partial or previously-cleaned state is safe.
|
||||||
|
Removing the Docker network `decnet_lan` happens separately via
|
||||||
|
`remove_macvlan_network`.
|
||||||
|
|
||||||
|
See [Teardown](Teardown-and-State) for the full teardown flow including
|
||||||
|
container removal and state-file cleanup.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Quick checklist
|
||||||
|
|
||||||
|
- Host NIC is wired and supports promiscuous mode -> MACVLAN, no flag needed.
|
||||||
|
- Host NIC is WiFi, or promisc is blocked -> pass `--ipvlan`.
|
||||||
|
- Running under WSL -> switch to a Linux VM or bare metal.
|
||||||
|
- `decnet status` says a decky is unreachable from the host -> confirm
|
||||||
|
`decnet_macvlan0` (or `decnet_ipvlan0`) exists with `ip link show`.
|
||||||
Reference in New Issue
Block a user