TrapPot is an AI-powered IoT honeypot system for SSH and Telnet activity.
It runs a full local pipeline:
Attacker SSH/Telnet session
|
v
Cowrie honeypot logs
|
v
Zeek conn.log and ssh.log
|
v
Random Forest detector
|
v
Logstash -> Elasticsearch -> Kibana dashboard
- Cowrie records fake SSH/Telnet attacker sessions.
- Zeek watches the Cowrie network traffic and writes
conn.logandssh.log. - AI detector reads Zeek
conn.logand runs the trained Random Forest model. - Logstash cleans the logs and sends them to Elasticsearch.
- Elasticsearch stores Cowrie, Zeek, and detector records with login protection enabled.
- Kibana shows the ready-made TrapPot dashboard.
This guide targets Arch Linux.
Install the required packages:
sudo pacman -Syu
sudo pacman -S --needed git docker docker-compose docker-buildx opensshStart Docker:
sudo systemctl enable --now dockerAllow your user to run Docker:
sudo usermod -aG docker "$USER"
newgrp dockerCheck Docker:
docker --version
docker compose versionSet the Elasticsearch kernel value:
sudo sysctl -w vm.max_map_count=1048576Clone the repo:
git clone https://github.com/bxrayxd/graduation-project.git
cd graduation-project/Trap-PotOptional: create a local password file if you want to change the default lab passwords:
cp .env.example .envDefault local credentials:
Kibana username: elastic
Kibana password: trappotadmin
Cowrie username: root
Cowrie password: admin
TrapPot can start without .env; Docker Compose uses the same local defaults shown above. If you create .env, you can change the Elastic passwords before the first run. Keep KIBANA_ENCRYPTION_KEY at least 32 characters long.
Do not commit Trap-Pot/.env. The repo ignores it on purpose.
Start the system:
docker compose up --build -dCheck the containers:
docker compose psYou should see:
cowrie
zeek
ai_detector
elasticsearch
elastic_setup
logstash
kibana
kibana_setup
elastic_setup and kibana_setup should exit with code 0. That is normal. They create users, templates, data views, and the dashboard.
Open a fake SSH session:
ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no root@localhost -p 2222Use this password:
admin
Run a few commands:
whoami
pwd
uname -a
mkdir test_dir
cat /etc/passwd
exitWait about 30 seconds. Cowrie, Zeek, the AI detector, Logstash, Elasticsearch, and Kibana need a short moment to process the session.
For SSH brute-force, command execution, and Telnet test steps, see Trap-Pot/ATTACK_TESTS.md.
Cowrie logs:
docker compose logs cowrieZeek connection log:
cat ./zeek/logs/conn.logZeek SSH log:
cat ./zeek/logs/ssh.logAI detector:
docker compose logs ai_detectorExpected detector output:
TrapPot AI is watching the network...
ALERT: Normal detected!
Logstash should not show indexing errors:
docker compose logs logstashOpen:
http://localhost:5601
Log in with:
username: elastic
password: trappotadmin
Open Dashboards, then open:
TrapPot Overview
The dashboard shows:
- Cowrie events
- brute-force attempts
- captured commands
- file uploads
- Zeek connections
- observed attack types
- Random Forest network decisions
- top source IPs, usernames, credentials, and commands
GeoIP fields can stay empty during a local Docker test because the source IP is private. GeoIP becomes useful when traffic comes from public IP addresses.
TrapPot enables Elastic username/password security by default.
The local demo uses these accounts:
elastic: Kibana login and admin account.kibana_system: Kibana service account.trappot_writer: Logstash writer account fortrappot-*indices.
Elasticsearch port 9200 is not published to your host. Kibana port 5601 is published so you can open the dashboard.
This setup still uses HTTP inside the local Docker lab. Do not expose Kibana or Cowrie to a public network without approval and firewall rules.
Check that Elasticsearch rejects requests without a login:
docker compose exec elasticsearch curl -s -o /dev/null -w "%{http_code}\n" http://localhost:9200Expected result:
401
Check that the Elastic login works:
docker compose exec elasticsearch curl -s -u "elastic:$ELASTIC_PASSWORD" http://localhost:9200/_security/_authenticateMain runtime files:
Trap-Pot/docker-compose.yml
Trap-Pot/.env.example
Trap-Pot/cowrie/etc/cowrie.cfg
Trap-Pot/cowrie/etc/userdb.txt
Trap-Pot/ai_detector/detector.py
Trap-Pot/logstash/pipeline/trappot.conf
Trap-Pot/elasticsearch/templates/
Trap-Pot/kibana/
Generated logs:
Trap-Pot/zeek/logs/conn.log
Trap-Pot/zeek/logs/ssh.log
Trap-Pot/zeek/logs/detections.json
Stop the containers:
docker compose downDelete stored Cowrie and Elasticsearch data:
docker compose down -vUse down -v when you want a clean test.
Run a clean reset:
docker compose down -v
docker compose up --build -dIf you change .env after Elasticsearch already started, run the clean reset command. Elasticsearch stores passwords inside its Docker volume.
2222: Cowrie SSH2223: Cowrie Telnet5601: Kibana
If port 2222 is busy, stop the other service or change the Cowrie port in Trap-Pot/docker-compose.yml.
If Elasticsearch does not start, run:
sudo sysctl -w vm.max_map_count=1048576If Kibana opens but shows no data, run the SSH test again and wait 30 seconds.
If the AI detector shows no alerts, check that Zeek created:
./zeek/logs/conn.log
If the AI detector prints Detector skipped malformed Zeek row, check conn.log. The detector expects the normal Zeek connection fields.
If the dashboard or data views are missing, check:
docker compose logs kibana_setupIf Logstash cannot write to Elasticsearch, check:
docker compose logs logstash
docker compose logs elastic_setupRun TrapPot in a lab network. Do not expose it to the public internet or a university network without written approval.