Введение
Привет! Расскажу про Cowrie SSH Honeypot, как его настроить и для чего применить в Homelab.
Статья рассчитана на тех, кто уже более-менее уверенно чувствует себя в селфхарме селфхосте, не уходит в панику при виде куска compose.yml. Используемые технологии: docker compose, grafana, victorialogs, promtail, crowdsec. Если все слова знакомые, читать будет очень просто. Как говорят в кровавом ентерпрайзе, if you have questions – please let me know.
Honeypot – это сервис - ловушка, который висит на определенном порту, и ждет, пока в него войдут. Есть ханипоты для разных сервисов: будь то SSH, Telnet, RDP… Его суть в том, чтобы в эту ловушку попали - у себя мы это логируем, строим красивые дашборды и отправляем нарушителя спокойствия в бан. Бан есть просто локальный, но можно и отправлять в сторонний сервис (у меня это Crowdsec). Ну и для дашбордиков красивых, конечно!
Примеры ханипотов:
- pisshoff: SSH Honeypot. A very simple SSH server using thrussh that exposes mocked versions of a bash shell, some commands and SSH subsystems to act as a honeypot for would-be crackers.
- endlessh: SSH tarpit that slowly sends an endless banner
- tpotce: 🍯 T-Pot - The All In One Multi Honeypot Platform 🐝
- cowrie: Cowrie SSH/Telnet Honeypot
- beelzebub: A secure low code honeypot framework, leveraging AI for System Virtualization.
Хоть они и разные ( например, просто слушать порт на микротике и банить при попытке зайти на порт - Youtube:Настройка Honeypot в Mikrotik), с разными требованиями (tpotce очень крутая, но хочет очень много ресурсов)...
… для себя я выбрал именно cowrie, так как хочу сделать красивые графики на дашборде с картой, обработку логов, беспалевный ханипот… а еще – это просто первое, что я нашел
Подробнее про Cowrie
Cowrie - это проект на python, который имитирует настоящий сервер, на который зашли по SSH/telnet. Он умеет прикольные фишки:
- Разные варианты аутентификации (опа, быстрая проверка - или “авторизации”?)
- Имитация ввода команд и их вывода
- Фейковая файловая система
- Сохранять загруженные файлы (но это я пока не юзаю)
- 2 режима окружения - в питоне, или же в настоящем qemu (тоже пока не юзаю, звучит страшновато)
- Сохранять историю ввода команд, с возможностью экспорта на asciinema
- Логирование в JSON и запуск в Docker
Может, умеет еще что-то, что я мог забыть. Но мне этого было достаточно, чтобы остановить серфинг по мировой веб паутине в поисках ханипота.
Установка и настройка
Сначала просто его поднимем с простыми настройками, после этого покажу, как его [cowrie] кастомизировать.
Вот так можно запустить на коленке этот контейнер cowrie/cowrie с докерхаба:
docker run -it --rm -p 2222:2222 cowrie/cowrie
И вот так потестить с того же компа (опции SSH отключают запоминание и проверку ключа сервера):
ssh -o "UserKnownHostsFile=/dev/null" -o "StrictHostKeyChecking no" -p 2222 root@127.0.0.1
Тут же можем осмотреться. В качестве пароля я вбил “123”
/Users/ColCh/Workspace/cowrie-setup took 2s88ms
11:40:31 ✖ ssh -o "UserKnownHostsFile=/dev/null" -o "StrictHostKeyChecking no" -p 2222 root@127.0.0.1
root@127.0.0.1's password:
The programs included with the Debian GNU/Linux system are free software;
the exact distribution terms for each program are described in the
individual files in /usr/share/doc/*/copyright.
Debian GNU/Linux comes with ABSOLUTELY NO WARRANTY, to the extent
permitted by applicable law.
root@svr04:~# ls -lah
drwx------ 1 root root 4.0K 2013-04-05 12:25 .
drwxr-xr-x 1 root root 4.0K 2013-04-05 12:03 ..
drwx------ 1 root root 4.0K 2013-04-05 11:58 .aptitude
-rw-r--r-- 1 root root 570 2013-04-05 11:52 .bashrc
-rw-r--r-- 1 root root 140 2013-04-05 11:52 .profile
drwx------ 1 root root 4.0K 2013-04-05 12:05 .ssh
root@svr04:~# cat .bashrc
root@svr04:~# cat .profile
root@svr04:~# ls .ssh
known_hosts
root@svr04:~# cat .ssh/known_hosts
root@svr04:~# cat /etc/passwd
root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/bin/sh
bin:x:2:2:bin:/bin:/bin/sh
sys:x:3:3:sys:/dev:/bin/sh
sync:x:4:65534:sync:/bin:/bin/sync
games:x:5:60:games:/usr/games:/bin/sh
man:x:6:12:man:/var/cache/man:/bin/sh
lp:x:7:7:lp:/var/spool/lpd:/bin/sh
mail:x:8:8:mail:/var/mail:/bin/sh
news:x:9:9:news:/var/spool/news:/bin/sh
uucp:x:10:10:uucp:/var/spool/uucp:/bin/sh
proxy:x:13:13:proxy:/bin:/bin/sh
www-data:x:33:33:www-data:/var/www:/bin/sh
backup:x:34:34:backup:/var/backups:/bin/sh
list:x:38:38:Mailing List Manager:/var/list:/bin/sh
irc:x:39:39:ircd:/var/run/ircd:/bin/sh
gnats:x:41:41:Gnats Bug-Reporting System (admin):/var/lib/gnats:/bin/sh
nobody:x:65534:65534:nobody:/nonexistent:/bin/sh
libuuid:x:100:101::/var/lib/libuuid:/bin/sh
sshd:x:101:65534::/var/run/sshd:/usr/sbin/nologin
phil:x:1000:1000:Phil California,,,:/home/phil:/bin/bash
root@svr04:~# uname -a
Linux svr04 3.2.0-4-amd64 #1 SMP Debian 3.2.68-1+deb7u1 x86_64 GNU/Linux
root@svr04:~#
Как видим, файлы в домашней директории пустые. Также в юзерах записан какой-то Phil.
Как по мне, довольно палевно. Если бы я был Нео внутри матрицы - я бы давно согнул эту ложку.
Давайте настроим этот ханипот.
Кастомизация
Начнем с конфига. Хоть и в документации сказано, что можно использовать env переменные, у меня не получилось. Он просто игнорировал мои env переменные (хотя, судя по коду, должно работать). Но сейчас проверил - и заработало…магия.
Вот так можно запустить с конфигом (пока без compose, на коленке):
docker run -it --rm -p 2222:2222 \
-e=COWRIE_HONEYPOT_HOSTNAME="droplet" \
-e=COWRIE_HONEYPOT_TIMEZONE="Europe/Moscow" \
-e=COWRIE_HONEYPOT_FAKEADDR="192.168.1.26" \
-e=COWRIE_HONEYPOT_AUTH_CLASS="AuthRandom" \
-e=COWRIE_HONEYPOT_AUTH_CLASS_PARAMETERS="1,2,10" \
-e=COWRIE_SHELL_KERNEL_VERSION="6.8.1-4-amd64" \
-e=COWRIE_SHELL_KERNEL_BUILD_STRING="#1 SMP Debian 6.8.11-1+deb12u1" \
-e=COWRIE_SSH_SFTP_ENABLED="false" \
-e=COWRIE_SSH_FORWARDING="false" \
-e=COWRIE_SSH_FORWARD_REDIRECT="false" \
cowrie/cowrie
Дефолтный конфиг тут, на GH. Чтобы написать его как env: берем секцию, имя опции, добавляем спереди cowrie, и оформляем это как constant case
Проверим:
/Users/ColCh/Workspace/cowrie-setup took 2m59s170ms
11:57:15 ➜ ssh -o "UserKnownHostsFile=/dev/null" -o "StrictHostKeyChecking no" -p 2222 root@127.0.0.1
root@127.0.0.1
root@127.0.0.1's password:
The programs included with the Debian GNU/Linux system are free software;
the exact distribution terms for each program are described in the
individual files in /usr/share/doc/*/copyright.
Debian GNU/Linux comes with ABSOLUTELY NO WARRANTY, to the extent
permitted by applicable law.
root@droplet:~# uname -a
Linux droplet 6.8.1-4-amd64 #1 SMP Debian 6.8.11-1+deb12u1 x86_64 GNU/Linux
root@droplet:~#
Выглядит рабочим. Авторизация в примере выбрана как AuthRandom, что позволяет войти с любым паролем на рандомной попытке между 1 и 2. Связка login/pass кешируется на 10 входов.
Я бы хотел сделать так, чтобы человек бот входил по уже заранее заготовленным логинам и паролям. Для этого нужно сделать userdb.
Давайте сделаем. Заодно уже покажу compose.yml с секцией configs
services:
cowrie:
image: cowrie/cowrie
container_name: cowrie
ports:
- "2222:2222"
environment:
- COWRIE_HONEYPOT_HOSTNAME=droplet
- COWRIE_HONEYPOT_TIMEZONE=Europe/Moscow
- COWRIE_HONEYPOT_FAKEADDR=192.168.1.26
- COWRIE_SHELL_KERNEL_VERSION=6.8.1-4-amd64
- COWRIE_SHELL_KERNEL_BUILD_STRING="#1 SMP Debian 6.8.11-1+deb12u1"
- COWRIE_SSH_SFTP_ENABLED=false
- COWRIE_SSH_FORWARDING=false
- COWRIE_SSH_FORWARD_REDIRECT=false
configs:
- source: cowrie_userdb
target: /cowrie/cowrie-git/etc/userdb.txt
configs:
cowrie_userdb:
content: |
root:x:foobar
Пробуем. Теперь проходит только пароль foobar.
NOTE: просто так пользователя написать не получится, его надо добавить в /etc/passwd
Сейчас это сделаем вместе с кастомной файловой системой.
Предлагаю стыбзить из alpine. Мой пользователь будет называться myuser:
docker run --rm -it -v $(realpath .)/fake_fs:/mnt alpine
/ # adduser myuser
/ # su myuser
/ $ cd
~ $ echo 'https://bit.ly/4kW5tWu' > passwords.txt
~ $ exit
/ # apk add rsync
/ # rsync -aAXv --exclude='/mnt/' --exclude='/proc' --exclude='/sys' --exclude='/dev' / /mnt/
/ # exit
Теперь в папке fake_fs есть настоящая файловая система из alpine. Сгенерим для нее pickle, как того хочет cowrie. Для этого придется установить проект. Предлагаю сделать это в докере:
docker run --rm -it -v $(realpath .)/fake_fs:/mnt debian:12
root@28e2b4e21a38:/# apt update
root@28e2b4e21a38:/# apt install -y git python3-venv libssl-dev libffi-dev build-essential libpython3-dev python3-minimal authbind
root@28e2b4e21a38:/# git clone [http://github.com/cowrie/cowrie](http://github.com/cowrie/cowrie)
root@28e2b4e21a38:/# cd cowrie/
root@d839f6e7c392:/cowrie# python3 -m venv cowrie-env
root@d839f6e7c392:/cowrie# source cowrie-env/bin/activate
(cowrie-env) root@d839f6e7c392:/cowrie# python -m pip install --upgrade pip
(cowrie-env) root@d839f6e7c392:/cowrie# python -m pip install --upgrade -r requirements.txt
Теперь генерим picke (это пока в том же контейнере):
(cowrie-env) root@d839f6e7c392:/cowrie# ls /mnt/
bin etc home lib media opt root run sbin srv tmp usr var
(cowrie-env) root@d839f6e7c392:/cowrie# bin/createfs -l /mnt/ -d 9 -o /mnt/custom.pickle
Пока все рядом, можем запустить редактор виртуальной файловой системы. Удалю оттуда маковский мусор из “/”
(cowrie-env) root@d839f6e7c392:/cowrie# bin/fsctl /mnt/custom.pickle
/mnt/custom.pickle
Kippo/Cowrie file system interactive editor
Donovan Hubbard, Douglas Hubbard, March 2013
Type 'help' for help
custom.pickle:/$ ls
.DS_Store
.dockerenv
bin/
etc/
home/
lib/
media/
opt/
root/
run/
sbin/
srv/
tmp/
usr/
var/
custom.pickle:/$ rm .dockerenv
Deleted /.dockerenv
custom.pickle:/$ rm .DS_Store
Deleted /.DS_Store
custom.pickle:/$ cd /home/myuser
custom.pickle:/home/myuser$ ls
.ash_history
passwords.txt
custom.pickle:/home/myuser$
Когда закончили - выходим (CTRL+D или команда “exit”). Из контейнера тоже.
После этого перемещаем пикливанную (кто-то есть из Волгограда?) файловую систему поближе к докеру. Папку с файлами для генерации удалять нельзя, так как она будет использоваться для содержимого файлов. Pickle содержит только метаданные.
/Users/ColCh/Workspace/cowrie-setup
13:16:44 ➜ mv fake_fs/custom.pickle ./
Теперь осталось добавить этот файл в контейнер cowrie (и папку с файловой системой). Заодно поставим пароль для пользователя myuser:
services:
cowrie:
image: cowrie/cowrie
container_name: cowrie
ports:
- "2222:2222"
volumes:
- ./custom.pickle:/custom.pickle:ro
- ./fake_fs:/fake_fs:ro
environment:
- COWRIE_HONEYPOT_HOSTNAME=droplet
- COWRIE_HONEYPOT_TIMEZONE=Europe/Moscow
- COWRIE_HONEYPOT_FAKEADDR=192.168.1.26
- COWRIE_SHELL_KERNEL_VERSION=6.8.1-4-amd64
- COWRIE_SHELL_KERNEL_BUILD_STRING="#1 SMP Debian 6.8.11-1+deb12u1"
- COWRIE_SSH_SFTP_ENABLED=false
- COWRIE_SSH_FORWARDING=false
- COWRIE_SSH_FORWARD_REDIRECT=false
- COWRIE_HONEYPOT_CONTENTS_PATH=/fake_fs
- COWRIE_SHELL_FILESYSTEM=/custom.pickle
configs:
- source: cowrie_userdb
target: /cowrie/cowrie-git/etc/userdb.txt
configs:
cowrie_userdb:
content: |
myuser:x:secret
Пробуем:
~/Workspace/cowrie-setup took 1s18ms
14:12:45 ➜ ssh -o "UserKnownHostsFile=/dev/null" -o "StrictHostKeyChecking no" -p 2222 myuser@127.0.0.1
myuser@127.0.0.1's password:
Welcome to Alpine!
The Alpine Wiki contains a large amount of how-to guides and general
information about administrating Alpine systems.
See <https://wiki.alpinelinux.org/>.
You can setup the system with the command: setup-alpine
You may change this message by editing /etc/motd.
myuser@droplet:~$
passwords.txt .ash_history
myuser@droplet:~$ cat passwords.txt
https://bit.ly/4kW5tWu
myuser@droplet:~$
Работает!
Дома я пока ограничился только файловой системой. Еще cowrie умеет выводить prompt в shell, делать моковые команды, сохранять то, что передали по scp для анализа), использовать настоящую виртуалку в qemu, прокси… но мне пока хватит.
Теперь приступим к дашборду. Но перед этим хотел бы еще одну штуку рассказать, интересную, как по мне.
asciinema
Asciinema это сервис, куда можно выгрузить “запись” терминала. Это будто бы снимали экран, но на самом деле это записанная последовательность действий внутри терминала и вывод команд. Чтобы туда эту запись загрузить, надо ее сначала правильно записать.
Cowrie умеет это делать. В логах будет встречаться вот такое:
{"eventid":"cowrie.log.closed","ttylog":"var/lib/cowrie/tty/c8081c94dbbbf06e9e1e28867806864acdeb0447f133c8437cf9a74f8eef3e6d","size":381,"shasum":"c8081c94dbbbf06e9e1e28867806864acdeb0447f133c8437cf9a74f8eef3e6d","duplicate":false,"duration":"5.9","message":"Closing TTY Log: var/lib/cowrie/tty/c8081c94dbbbf06e9e1e28867806864acdeb0447f133c8437cf9a74f8eef3e6d after 5.9 seconds","sensor":"ca404c2fe14b","timestamp":"2025-03-23T14:16:11.319573+0000","src_ip":"172.21.0.1","session":"bfdabc03ec02"}
Если этот файлик скачать к себе на комп, его можно локально проиграть. Чтобы не качать проект cowrie локально, я буду использовать docker, где войду в контейнер cowrie и вызову команду. Cowrie не имеет shell, поэтому буду использовать debian.
Из лога выше файл лежит в var/lib/cowrie/tty/c8081c94dbbbf06e9e1e28867806864acdeb0447f133c8437cf9a74f8eef3e6d
Абсолютный путь будет: /cowrie/cowrie-git/var/lib/cowrie/tty/c8081c94dbbbf06e9e1e28867806864acdeb0447f133c8437cf9a74f8eef3e6d
Ну и проиграть прямо в текущем терминале можно так:
docker compose exec -it cowrie /cowrie/cowrie-env/bin/python3 /cowrie/cowrie-git/bin/playlog -c -b -m 1.0 /cowrie/cowrie-git/var/lib/cowrie/tty/c8081c94dbbbf06e9e1e28867806864acdeb0447f133c8437cf9a74f8eef3e6d
А закинуть на asciinema вот так (ток надо сначала установить):
asciinema upload <( docker compose exec -it cowrie /cowrie/cowrie-env/bin/python3 /cowrie/cowrie-git/bin/asciinema /cowrie/cowrie-git/var/lib/cowrie/tty/3985077e433ea3cbc0fbe4f0ba8bd6d608b6f28fdae3ba7ad1e815cc75af997b )
https://asciinema.org/a/TbAtG45c4VoV4lfG8RJlYFQ3y
Здорово. Теперь к дашборду.
Дашборд на логах
Научимся более осмысленно смотреть на то, что происходит внутри контейнера посредством визуализации. Здесь будем парсить логи через victoriametrics, и выводить на дашборд в grafana.
Пример логов, которые cowrie пишет в терминал:
cowrie | 2025-03-23T13:31:35+0000 [HoneyPotSSHTransport,0,172.21.0.1] Initialized emulated server as architecture: linux-x64-lsb
cowrie | 2025-03-23T13:31:35+0000 [cowrie.ssh.userauth.HoneyPotSSHUserAuthServer#debug] b'myuser' authenticated with b'password'
cowrie | 2025-03-23T13:31:35+0000 [cowrie.ssh.transport.HoneyPotSSHTransport#debug] starting service b'ssh-connection'
cowrie | 2025-03-23T13:31:35+0000 [cowrie.ssh.connection.CowrieSSHConnection#debug] got channel b'session' request
cowrie | 2025-03-23T13:31:35+0000 [cowrie.ssh.session.HoneyPotSSHSession#info] channel open
cowrie | 2025-03-23T13:31:43+0000 [HoneyPotSSHTransport,0,172.21.0.1] CMD: cat passwords.txt
cowrie | 2025-03-23T13:31:43+0000 [HoneyPotSSHTransport,0,172.21.0.1] Command found: cat passwords.txt
Здорово, но не очень читабельно для компьютера. Cowrie умеет писать логи в формате JSON, они лежат по определенному пути. Вытащить их можно так (compose.yml):
services:
cowrie:
…
volumes:
- ./logs/:/cowrie/cowrie-git/var/log/cowrie
Сам он файл не сможет создать, поэтому надо помочь:
mkdir logs
touch logs/cowrie.json
Если тестируете на MacOS, скорее всего, не будет хватать прав из-за UID/GID. Можно временно закостылить так:
chmod 666 logs/cowrie.json
На хомлабе права у директории вот такие:
chmod -R 0764 logs
chown -R 999:999 logs
Примеры логов (вот тут документация по логам):
{"eventid":"cowrie.session.connect","src_ip":"172.21.0.1","src_port":41966,"dst_ip":"172.21.0.2","dst_port":2222,"session":"bfdabc03ec02","protocol":"ssh","message":"New connection: 172.21.0.1:41966 (172.21.0.2:2222) [session: bfdabc03ec02]","sensor":"ca404c2fe14b","timestamp":"2025-03-23T14:16:03.314456+0000"}
{"eventid":"cowrie.login.failed","username":"myuser","fingerprint":"8c:42:29:26:4b:cc:ab:76:f1:0f:d0:94:ce:7d:db:c1","key":"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAICFpiZP85S4X5kIAsIW0WPOoeqBCUA9CdPnVbmfkUQMt","type":"ssh-ed25519","message":"public key login attempt for [myuser] failed","sensor":"ca404c2fe14b","timestamp":"2025-03-23T14:16:03.347539+0000","src_ip":"172.21.0.1","session":"bfdabc03ec02"}
{"eventid":"cowrie.login.success","username":"myuser","password":"secret","message":"login attempt [myuser/secret] succeeded","sensor":"ca404c2fe14b","timestamp":"2025-03-23T14:16:05.328592+0000","src_ip":"172.21.0.1","session":"bfdabc03ec02"}
{"eventid":"cowrie.command.input","input":"echo 'hello world'","message":"CMD: echo 'hello world'","sensor":"ca404c2fe14b","timestamp":"2025-03-23T14:16:10.590840+0000","src_ip":"172.21.0.1","session":"bfdabc03ec02"}
{"eventid":"cowrie.session.closed","duration":"8.0","message":"Connection lost after 8.0 seconds","sensor":"ca404c2fe14b","timestamp":"2025-03-23T14:16:11.341929+0000","src_ip":"172.21.0.1","session":"bfdabc03ec02"}
Они здорово и так парсятся, если у вас VictoriaLogs. Но я хотел большего: мне нужно было вычислить по айпи дополнить лог геоданными.
Сделать это можно двумя шагами (есть и другие варианты, я бы с удовольствием перестал юзать promtail, но не сегодня):
Сначала качаем базку с айпишниками ака GeoIP, я использую maxmind/geoipupdate (бесплатное):
geoipupdate:
container_name: geoipupdate
image: ghcr.io/maxmind/geoipupdate
restart: always
env_file: [.env]
environment:
# GEOIPUPDATE_ACCOUNT_ID=SEE_ENV
# GEOIPUPDATE_LICENSE_KEY=SEE_ENV
- 'GEOIPUPDATE_EDITION_IDS=GeoLite2-ASN GeoLite2-City GeoLite2-Country'
- GEOIPUPDATE_FREQUENCY=72
volumes:
- ./data:/usr/share/GeoIP
Account ID и License Key можно взять у них после регистрации
Супер! Теперь добавим геодату к айпишникам. Для этого я использую promtail
Контейнер:
promtail:
image: grafana/promtail
restart: always
expose:
- 9080
healthcheck:
test: ["CMD-SHELL", "grep -q 'promtail' /proc/*/cmdline"]
volumes:
- ./logs/:/var/log/cowrie:ro
- ./promtail.yml:/etc/promtail/promtail.yml:ro
- ./promtail-data:/var/promtail
- ../geoip/data/:/usr/share/GeoIP:ro
command:
- "--config.file=/etc/promtail/promtail.yaml"
- "--config.expand-env=true"
configs:
- source: promtail-config
target: /etc/promtail/promtail.yaml
configs:
promtail-config:
file: ./promtail.yml
Его конфижик, promtail.yml (NOTE: victorialogs:9428 - это я использую для логов):
server:
http_listen_address: 0.0.0.0
http_listen_port: 9080
log_level: warn
positions:
filename: "/var/promtail/positions.yaml"
clients:
- url: http://victorialogs:9428/insert/loki/api/v1/push?_msg_field=message&_stream_fields=instance,job
scrape_configs:
- job_name: cowrie
static_configs:
- targets:
- localhost
labels:
job: cowrie
__path__: /var/log/cowrie/cowrie.json
instance: cowrie-promtail
pipeline_stages:
- json:
expressions:
timestamp: timestamp
src_ip: src_ip
- timestamp:
source: timestamp
format: 2025-02-25T12:57:07.457597+0100
- geoip:
db: /usr/share/GeoIP/GeoLite2-City.mmdb
source: src_ip
db_type: "city"
output:
geoip_country: country.iso_code
geoip_city: city.names.en
geoip_latitude: location.latitude
geoip_longitude: location.longitude
- labels:
src_ip:
geoip_country:
geoip_city:
geoip_latitude:
geoip_longitude:
После этого данные можно увидеть в VictoriaLogs (это уже реальный лог из хомлабы):
{
"_time": "2025-03-22T00:00:06.054139657Z",
"_stream_id": "000000000000000068c424eea7c92cf2604a6c7c64ce0180",
"_stream": "{instance=\"cowrie-promtail\",job=\"cowrie\"}",
"_msg": "New connection: 182.92.68.168:41692 (172.31.3.130:2222) [session: 4de4bf864d44]",
"filename": "/var/log/cowrie/cowrie.json",
"instance": "cowrie-promtail",
"job": "cowrie",
"sensor": "cowrie",
"dst_ip": "172.31.3.130",
"dst_port": "2222",
"eventid": "cowrie.session.connect",
"geoip_city_name": "Beijing",
"geoip_continent_code": "AS",
"geoip_continent_name": "Asia",
"geoip_country_name": "China",
"geoip_location_latitude": "39.911",
"geoip_location_longitude": "116.395",
"geoip_subdivision_code": "BJ",
"geoip_subdivision_name": "Beijing",
"geoip_timezone": "Asia/Shanghai",
"protocol": "ssh",
"session": "4de4bf864d44",
"src_ip": "182.92.68.168",
"src_port": "41692",
"timestamp": "2025-03-22T01:00:06.016764+0100"
},
По вот этим geoip_*
в Grafana можно построить вот такую карту
Как?
Я не буду расписывать прям весь дашборд (его я просто скину), расскажу только про карту.
Сначала надо выгрепать логи:
{instance="cowrie-promtail"} "New connection"
Вот так вот:
Далее делаем некоторую магию в Transformations, чтоб fields стали labels. И убираем всякие поля, оставляем только широту и долготу, указываем имена полей с координатами, как сказано в документации Grafana
Указываю на имена полей с широтой и долготой
et voila теперь в таблице получится что-то такое (ну и если переключить Table View, то заработает карта с heatmap)
Сам дашборд можно скачать тут готовый: https://grafana.com/grafana/dashboards/23141
Вообще можно было на этом остановиться, но для полного набора не хватает баньки… от слова BAN.
И тут на сцену выходит Crowdsec
Crowdsec
Что это такое? https://www.crowdsec.net
Если своими словами, то:
- С одной стороны – это антифрод на основе логов (блочим плохих юзеров.
- С другой – это распределенный fail2ban (один “микросервис” вычитывает логи, другой банит, третий закрывает доступ до траффика забаненым айпишникам), с возможностью шарить блок листы*
*
- как другим пользователями, так и компании, конечно же.
Вот такая схемка у них в доке есть:
Есть вот такие компоненты – в скобочках термины из Crowdsec:
- Агенты (agents) ака машины (machines) - анализируют логи, выполняют решение (Decisions) при каких-либо сценариях (Crenarios) (напр. Кто-то пытался авторизоваться 10 раз за 1 минуту - баним на час), шлет алерты в LAPI
- LAPI (Local API) - координатор, хранилище конфигурации, блоклистов. Связывается с CAPI (Central API) - серверами CrowdSec (напр. чтоб скачать блок листы)
- Bouncer - по конфигурации из LAPI, выполняет действие над нарушителем - например, может через cloudflare показать капчу, или (если это на фаерволле) - забанить входящий траффик от нарушителя
Статья не об crowdsec, но если хочется узнать поподробнее, прошу написать в комментах :) Предположим, что у вас уже он настроен, и Вы бы хотели интегрировать туда cowrie.
Итак, давайте сделаем так, чтобы при соединении по SSH в ханипот - IP адрес банило на какое-то время.
У crowdsec уже есть готовая коллекция, но она использует формат логов у cowrie НЕ-JSON (еще есть невлитый PR), поэтому тут будет 2 стула: либо мы парсим logfmt логи через VictoriaLogs, либо мы пишем свой парсер логов для crowdsec. Я выбрал второе. Если интересуют детали того, что за опции в yml (и как это протестить), прошу в доку crowdsec.
Сам парсер лежит по пути: /crowdsec/parsers/cowrie.yaml
# yaml-language-server: $schema=https://raw.githubusercontent.com/crowdsecurity/crowdsec-yaml-schemas/refs/heads/main/parser_schema.yaml
---
# Custom parser for Cowrie JSON logs
onsuccess: next_stage
name: cowrie-json-log
description: "Parse Cowrie honeypot JSON logs and extract connection details"
filter: "evt.Parsed.program == 'cowrie-json-log'"
# filter: 1 == 1
# debug: true
nodes:
- grok:
pattern: 'New connection: %{IPV4:src_ip}:[0-9]+ \(%{IPV4:dst_ip}:%{INT:dst_port}\) \[session: %{DATA:session}\]'
expression: JsonExtract(evt.Parsed.message, "message")
statics:
- meta: service
expression: "evt.Parsed.protocol"
- meta: log_type
value: "cowrie.session.connect"
- meta: source_ip
expression: "evt.Parsed.src_ip"
- meta: dest_ip
expression: "evt.Parsed.dst_ip"
- meta: dest_port
expression: "evt.Parsed.dst_port"
- parsed: session_id
expression: "evt.Parsed.session"
Далее сценарий. Лежит по пути: scenarios/cowrie-ssh-ban.yaml
# https://docs.crowdsec.net/docs/scenarios/format/
---
type: trigger
name: "cowrie/ssh-ban"
description: "Ban any IP that makes a connection to the SSH honeypot (Cowrie)."
filter: "evt.Meta.log_type == 'cowrie.session.connect'"
groupby: evt.Meta.source_ip
blackhole: 744h # 31 days
labels:
service: ssh
confidence: 3
spoofable: 0
classification:
- attack.T1110
label: "SSH Bruteforce"
behavior: "ssh:bruteforce"
remediation: true
Дальше можем это добро запустить вот так (считаю, что crowdsec уже используется в хомлабе и LAPI уже есть и настроено):
crowdsec:
image: crowdsecurity/crowdsec
restart: always
healthcheck:
test: ["CMD", "cscli", "lapi", "status"]
environment:
PARSERS: "crowdsecurity/cowrie-logs"
LEVEL_INFO: "true"
# LEVEL_DEBUG: 'true' # Force DEBUG level for the container log
# LEVEL_TRACE: 'true' # Force TRACE level (VERY verbose) for the container log
DISABLE_LOCAL_API: "true"
LOCAL_API_URL: "http://crowdsec-lapi:8080/"
CUSTOM_HOSTNAME: "cowrie-crowdsec"
# ТУТ ВАШЕ
# AGENT_USERNAME
# AGENT_PASSWORD"
volumes:
- /etc/localtime:/etc/localtime:ro
- ./crowdsec/acquis.yaml:/etc/crowdsec/acquis.yaml:ro
- ./cowrie-var/log/cowrie:/var/log/cowrie/:ro
- ./crowdsec/db:/var/lib/crowdsec/data/
- ./crowdsec/config:/etc/crowdsec/
- ./crowdsec/parsers/cowrie.yaml:/etc/crowdsec/parsers/s01-parse/cowrie-logs-json.yaml:ro
- ./crowdsec/scenarios/cowrie-ssh-ban.yaml:/etc/crowdsec/scenarios/cowrie-ssh-ban.yaml:ro
Заключение
That’s all folks!
Живу с ханипотом примерно месяц и вот, что бы я порекомендовал:
Ограничить доступы у контейнера и его ресурсы:
cpu_count: 1
mem_limit: 512m
cap_drop:
- ALL
security_opt:
- no-new-privileges:true
tmpfs:
- /tmp/cowrie:uid=999,gid=999
- /tmp/cowrie/data:uid=999,gid=999
Еще было бы круто ему поставить “read_only: true”, но тут у меня закончилось желание и время.
Также на этом порту микротик троттлит трафик:
Но я бы не сказал, что это прям нужно:
Crowsec довольно часто кого-то банит, вот его же дашборд:
И его еще один дашборд из админ панельки
Что еще можно почитать про настройку cowrie: https://cryptax.medium.com/customizing-your-cowrie-honeypot-8542c888ca49
Надеюсь, это кому-то пригодится. Удачи в настройке приманки в вашей домашней лаборатории!
как искусство это весело, но я не готов столько ресурсов виртуалки отдавать под это дело
ставлю себе всегда endlessh (у тебя как раз он упомянут), и раз в пару месяцев смотрю логи для внутреннего удовлетворения