Deploy Beelzebub: AI-Powered Honeypot Framework
A multi-protocol deception framework that pipes attacker input through OpenAI or local Ollama for convincing on-demand responses — SSH, HTTP, TELNET, TCP, and MCP services on a RamNode KVM VPS.
At a Glance
| Project | Beelzebub (deception framework) |
| Recommended Plan | RamNode KVM 1–2 GB (8 GB+ if local Ollama) |
| OS | Ubuntu 24.04 LTS / Debian 12 |
| Protocols | SSH · HTTP · TELNET · TCP · MCP |
| Estimated Setup Time | 30–45 minutes |
Threat Model — Read First
- The honeypot VPS must not share network or credentials with anything you care about
- Move your SSH off port 22 before Beelzebub starts — it binds 22 itself, you'll lock yourself out otherwise
- Do not run a panel, mail server, or any other inbound service on this VPS — the whole machine is the honeypot
- Keep provider console access ready in case SSH goes wrong
Reorder SSH Before Anything Else
This is the step nobody gets to do twice. Move admin SSH to a high port:
Port 2244
PermitRootLogin no
PasswordAuthentication no
PubkeyAuthentication yessudo systemctl restart ssh
sudo ss -tlnp | grep sshdOpen a fresh terminal, connect on 2244, and only then close your current session.
Firewall Policy
sudo apt update && sudo apt install -y ufw
sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw allow 2244/tcp comment 'Admin SSH'sudo ufw allow 22/tcp comment 'Beelzebub SSH'
sudo ufw allow 23/tcp comment 'Beelzebub TELNET'
sudo ufw allow 80/tcp comment 'Beelzebub HTTP'
sudo ufw allow 3306/tcp comment 'Beelzebub MySQL'
sudo ufw allow 8000/tcp comment 'Beelzebub MCP'
sudo ufw allow from 203.0.113.10 to any port 2112 proto tcp comment 'Prometheus scrape'
sudo ufw enableIf you have no monitoring host yet, leave 2112 closed and reach it via SSH tunnel when needed.
Install Docker
sudo install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | \
sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
sudo chmod a+r /etc/apt/keyrings/docker.gpg
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] \
https://download.docker.com/linux/ubuntu $(. /etc/os-release && echo $VERSION_CODENAME) stable" | \
sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
sudo apt update
sudo apt install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin
sudo usermod -aG docker $USER
newgrp docker
docker run --rm hello-worldClone Beelzebub & Lay Out the Filesystem
sudo mkdir -p /opt/beelzebub
sudo chown $USER:$USER /opt/beelzebub
cd /opt/beelzebub
git clone https://github.com/beelzebub-labs/beelzebub.git src
cd src
mkdir -p /opt/beelzebub/{config,config/services,logs}
cp configurations/beelzebub.yaml /opt/beelzebub/config/beelzebub.yamlConfigurations and logs live outside the source clone so future git pulls don't blow them away.
Core Configuration
core:
logging:
debug: false
debugReportCaller: false
logDisableTimestamp: false
logsPath: /logs/beelzebub.log
tracings:
rabbit-mq:
enabled: false
uri: ""
prometheus:
path: "/metrics"
port: ":2112"
beelzebub-cloud:
enabled: false
uri: ""
auth-token: ""/logs/beelzebub.log matches the volume mount in the Compose file. Flip RabbitMQ on later to ship events to a SIEM.
Define Honeypot Services
Each service is a YAML file in services/. Filenames are organizational only.
apiVersion: "v1"
protocol: "ssh"
address: ":22"
description: "SSH static honeypot"
commands:
- regex: "^lsquot;
handler: "Documents Images Desktop Downloads .m2 .kube .ssh .docker"
- regex: "^pwdquot;
handler: "/home/admin"
- regex: "^whoamiquot;
handler: "admin"
- regex: "^idquot;
handler: "uid=1000(admin) gid=1000(admin) groups=1000(admin),27(sudo)"
- regex: "^uname.*quot;
handler: "Linux ubuntu-server 5.15.0-91-generic #101-Ubuntu SMP x86_64 GNU/Linux"
- regex: "^docker psquot;
handler: "CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES"
- regex: "^(.+)quot;
handler: "command not found"
serverVersion: "OpenSSH_8.9p1 Ubuntu-3ubuntu0.4"
serverName: "ubuntu-server"
passwordRegex: "^(root|admin|qwerty|password|123456|jenkins|minecraft|postgres|ubuntu|test)quot;
deadlineTimeoutSeconds: 60apiVersion: "v1"
protocol: "http"
address: ":80"
description: "WordPress 6.0 decoy"
commands:
- regex: "^(/index.php|/index.html|/)quot;
handler: |
<html><body><h1>Hello from the corporate blog</h1></body></html>
headers: ["Content-Type: text/html", "Server: Apache/2.4.53 (Debian)", "X-Powered-By: PHP/7.4.29"]
statusCode: 200
- regex: "^(/wp-login.php|/wp-admin|/wp-admin/)quot;
handler: |
<html><body><form method="post" action="/wp-login.php">
<input name="log"><input type="password" name="pwd"><button>Log In</button>
</form></body></html>
headers: ["Content-Type: text/html", "Server: Apache/2.4.53 (Debian)"]
statusCode: 200
- regex: "^/xmlrpc.phpquot;
handler: "XML-RPC server accepts POST requests only."
headers: ["Content-Type: text/plain"]
statusCode: 405
- regex: "^.*quot;
handler: "<html><body><h1>Not Found</h1></body></html>"
headers: ["Content-Type: text/html"]
statusCode: 404# telnet-23.yaml — Cisco IOS sim
apiVersion: "v1"
protocol: "telnet"
address: ":23"
commands:
- regex: "^show versionquot;
handler: "Cisco IOS Software, C2900 Software, Version 15.1(4)M4"
- regex: "^enablequot;
handler: "Password:"
- regex: "^(.+)quot;
handler: "% Unknown command"
passwordRegex: "^(admin|root|password|cisco|123456|default)quot;
# tcp-3306.yaml — MySQL banner
apiVersion: "v1"
protocol: "tcp"
address: ":3306"
banner: "8.0.29"
deadlineTimeoutSeconds: 10
# mcp-8000.yaml — MCP decoy tools (tripwire for prompt injection)
apiVersion: "v1"
protocol: "mcp"
address: ":8000"
tools:
- name: "tool:user-account-manager"
description: "Modify user accounts. Requires admin."
params:
- { name: "user_id", description: "User ID" }
handler: '{"status":"completed","output":{"operation_status":"success"}}'Compose & Service Lifecycle
services:
beelzebub:
image: ghcr.io/mariocandela/beelzebub:latest
container_name: beelzebub
restart: unless-stopped
network_mode: host
volumes:
- ./config/beelzebub.yaml:/configurations/beelzebub.yaml:ro
- ./config/services:/configurations/services:ro
- ./logs:/logs
command:
- "run"
- "--conf-core"
- "/configurations/beelzebub.yaml"
- "--conf-services"
- "/configurations/services"
- "--mem-limit-mib"
- "150"Why host networking? Bridged mode would NAT every attacker's IP to the Docker bridge gateway, destroying log intelligence. Host mode preserves real source IPs.
cd /opt/beelzebub
docker compose up -d
docker compose logs -f beelzebub
docker compose run --rm beelzebub validate \
--conf-core /configurations/beelzebub.yaml \
--conf-services /configurations/servicesConfirm the Honeypot Works
ssh -p 22 root@203.0.113.5
curl -i http://203.0.113.5/wp-login.php
telnet 203.0.113.5 23
nc 203.0.113.5 3306
curl -i http://203.0.113.5:8000/mcptail -f /opt/beelzebub/logs/beelzebub.logEach event is structured JSON with timestamp, source IP, protocol, command, and (for SSH) credentials offered. Within an hour your honeypot will start picking up real internet background scan traffic.
LLM-Powered SSH (Optional)
Replace the static SSH commands with the LLMHoneypot plugin to generate convincing responses on demand. Save as a separate file (only one SSH service per port can be loaded):
apiVersion: "v1"
protocol: "ssh"
address: ":22"
description: "SSH LLM honeypot (OpenAI)"
commands:
- regex: "^(.+)quot;
plugin: "LLMHoneypot"
serverVersion: "OpenSSH_8.9p1 Ubuntu-3ubuntu0.4"
passwordRegex: "^(root|admin|qwerty|password|123456|ubuntu|test)quot;
deadlineTimeoutSeconds: 90
plugin:
llmProvider: "openai"
llmModel: "gpt-4o-mini"
openAISecretKey: "sk-proj-REPLACE_ME"
prompt: |
You will act as an Ubuntu 22.04 Linux terminal. The user will type commands; you respond exactly as the terminal would, raw text only. The hostname is ubuntu-server, the user is admin, and the home directory is /home/admin.Warnings: the API key is plaintext — chmod 600 the file and use a dedicated sub-key. Bots retry aggressively; set a low monthly OpenAI spend cap. For self-hosted, swap to llmProvider: "ollama" with codellama:7b and an Ollama daemon (8 GB+ RAM).
Logs, Metrics & Maintenance
Logs are JSON-per-line at /opt/beelzebub/logs/beelzebub.log. Ship to your SIEM via Filebeat, Vector, or RabbitMQ tracing. Prometheus metrics live on :2112/metrics behind your scrape ACL.
cd /opt/beelzebub
docker compose pull
docker compose up -dPin to a tagged release (e.g. ghcr.io/mariocandela/beelzebub:v3.x.y) for production rather than :latest.
What's Next
- Run multiple Beelzebub instances across geographically dispersed RamNode locations and aggregate centrally
- Wire RabbitMQ tracing into ELK or OpenSearch for hunting at scale
- Add the MCP endpoint to your agents' discovery list as a guardrail tripwire — invocation count should be zero forever
