
Экспериментальный брокер приманок
Контейнеризированная сетевая приманка (honeypot), которая рекламирует SSH, RDP и SMB, наблюдает за каждым входящим соединением с помощью eBPF и обратным прокси-сервером перенаправляет каждый сеанс в изолированный контейнер-приманку.
Архитектура разделяет две задачи:
Это защитный инструмент для обнаружения и изучения несанкционированной активности в сетях, которыми вы владеете или на мониторинг которых уполномочены. Развёртывайте его только там, где у вас есть такие полномочия.
flowchart TB
A["Атакующий / Сканер"]
subgraph host["Хост-приманка"]
direction TB
NIC["eth0 брокера<br/>опубликованные: 22, 3389, 445"]
subgraph brk["контейнер брокера"]
direction TB
E["Классификатор eBPF TC<br/>регистрирует каждый SYN<br/>видит реальный IP-источник"]
P["обратный прокси<br/>CONNECT к бэкенду"]
L["структурированные JSON-логи"]
end
subgraph dec["decoynet (внутренняя, без маршрута на хост)"]
direction LR
S["ssh-decoy<br/>OpenCanary ssh<br/>порт 2222"]
D["rdp-decoy<br/>OpenCanary rdp<br/>порт 3389"]
M["smb-decoy<br/>Impacket SMB сервер<br/>порт 445"]
end
end
A --> NIC
NIC --> E
NIC --> P
E --> L
P --> S
P --> D
P --> M
Всего четыре контейнера:
| Контейнер | Роль | Сеть |
|---|---|---|
broker | Публичный вход: eBPF-наблюдение + обратный прокси | edge + decoynet |
ssh-decoy | Модуль ssh OpenCanary (реальное рукопожатие, захват учётных данных) | только decoynet |
rdp-decoy | Модуль rdp OpenCanary (имитация NLA, захват имён пользователей) | только decoynet |
smb-decoy | SimpleSMBServer из Impacket (реальный SMB2/3, захват аутентификации) | только decoynet |
Приманки живут во внутренней сети Docker (decoynet) без маршрута к хосту или внешнему миру. Только брокер может до них добраться. Ничто из того, что атакующий сделает внутри приманки, не достигнет сети хоста напрямую.
Брокер публикует порты 22, 3389 и 445 на хосте, поэтому входящие пакеты поступают на eth0 брокера. Затем с каждым пакетом происходит две вещи:
broker/bpf/decoy.bpf.c) разбирает заголовки Ethernet, IP и TCP, и для каждой новой попытки соединения (SYN установлен, ACK сброшен) записывает conn_event в кольцевой буфер: IP-источник и порт, порт назначения, флаги TCP и является ли порт рекламируемым сервисом. Пакет пропускается без изменений (TC_ACT_OK).CONNECT к бэкенду-приманке для этого сервиса, затем передаёт байты в обоих направлениях.Карта eBPF advertised_ports заполняется при запуске из config.yaml, поэтому классификатор может пометить, попал ли зонд на обслуживаемый порт или на нерекламируемый. Это делает видимыми горизонтальные сканирования портов, даже если проксируются только три порта.
Если вы хотите рекламировать «всё открыто» и направлять произвольные порты назначения в брокер, расширьте классификатор для перезаписи порта назначения или используйте перенаправление TPROXY / bpf_sk_assign. Текущая версия не изменяет путь пакета и ограничивается наблюдением, что является более безопасным поведением по умолчанию.
cyber-decoy/
├── README.md
├── docker-compose.yml # Стек из 4 контейнеров
├── docker-compose.override.yml # Локальная разработка на macOS: без eBPF-возможностей, переназначение порта 22
├── Makefile # Сборка / запуск / остановка / помощь с eBPF
├── LICENSE
├── scripts/
│ └── setup.sh # Предварительные проверки хоста
├── broker/
│ ├── Dockerfile # Компилирует eBPF-объект + Go-бинарник
│ ├── config.yaml # Рекламируемые сервисы (настраивается)
│ ├── go.mod
│ ├── main.go # Точка входа
│ ├── bpf/
│ │ └── decoy.bpf.c # Классификатор eBPF TC
│ └── internal/
│ ├── config/config.go # Загрузчик конфигурации
│ ├── proxy/proxy.go # TCP-обратный прокси
│ └── bpf/loader.go # Загружает и прикрепляет eBPF, передаёт события
└── decoys/ # Все три запускают OpenCanary
├── ssh/
│ ├── Dockerfile
│ └── opencanary.conf # Модуль ssh, порт 2222
├── rdp/
│ ├── Dockerfile
│ └── opencanary.conf # Модуль rdp, порт 3389
└── smb/
├── Dockerfile # Один Python-процесс, не root
├── smb_decoy.py # SimpleSMBServer из Impacket + JSON-логирование
└── requirements.txt # impacket (зафиксирован)
docker-compose.override.yml, который полагается на теги !reset / !override).sudo mount -t bpf bpf /sys/fs/bpf.Образ брокера определяет архитектуру сборки и передаёт соответствующий макрос __TARGET_ARCH_* компилятору clang, поэтому он собирается как на x86_64, так и на aarch64 (Apple Silicon, Graviton). Обратите внимание, что gcc-multilib намеренно не установлен: это пакет только для x86 без кандидата для arm64, и его включение ломает сборку на arm64 с кодом выхода apt 100. Для компиляции eBPF-объекта нужны только clang и libbpf-dev.
Docker Desktop на macOS запускает контейнеры внутри виртуальной машины LinuxKit, а не в вашем хост-ядре, поэтому прикрепление TC/TCX eBPF обычно не будет работать там. Это не критично: eBPF по замыслу работает по принципу «best effort», поэтому брокер регистрирует ebpf disabled: attach failed, а обратный прокси и все три приманки работают и регистрируются нормально. Вы можете разрабатывать и тестировать весь путь прокси локально, а затем получить реальное eBPF-наблюдение при развёртывании на хосте Linux.
docker-compose.override.yml загружается автоматически и делает это удобным: он удаляет возможности eBPF (бесполезные в виртуальной машине) и переназначает хост-порт 22 на 2022, поскольку на Mac собственный sshd занимает порт 22.
docker compose up --build # локальная разработка, override применён
docker compose -f docker-compose.yml up -d # реальное развёртывание, override обойдён
Сначала выполните предварительную проверку:
./scripts/setup.sh
# 1. Сборка всех четырёх образов (компилирует eBPF-объект внутри образа брокера)
make build
# 2. Запуск стека
make up
# 3. Просмотр логов
make logs
Затем протестируйте с другой машины (или localhost для быстрой проверки):
ssh -p 22 user@HOST_ПРИМАНКИ # попадает в SSH-приманку
nc HOST_ПРИМАНКИ 3389 # попадает в RDP-приманку
nc HOST_ПРИМАНКИ 445 # попадает в SMB-приманку
nc HOST_ПРИМАНКИ 8080 # нерекламируемый: наблюдается eBPF, без прокси