Honeypot
    LLM-Powered

    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

    ProjectBeelzebub (deception framework)
    Recommended PlanRamNode KVM 1–2 GB (8 GB+ if local Ollama)
    OSUbuntu 24.04 LTS / Debian 12
    ProtocolsSSH · HTTP · TELNET · TCP · MCP
    Estimated Setup Time30–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
    1

    Reorder SSH Before Anything Else

    This is the step nobody gets to do twice. Move admin SSH to a high port:

    /etc/ssh/sshd_config
    Port 2244
    PermitRootLogin no
    PasswordAuthentication no
    PubkeyAuthentication yes
    Restart + verify on the new port
    sudo systemctl restart ssh
    sudo ss -tlnp | grep sshd

    Open a fresh terminal, connect on 2244, and only then close your current session.

    2

    Firewall Policy

    UFW defaults + admin SSH
    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'
    Honeypot ports
    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 enable

    If you have no monitoring host yet, leave 2112 closed and reach it via SSH tunnel when needed.

    3

    Install Docker

    Upstream Docker repo
    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-world
    4

    Clone Beelzebub & Lay Out the Filesystem

    Persistent layout outside the source tree
    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.yaml

    Configurations and logs live outside the source clone so future git pulls don't blow them away.

    5

    Core Configuration

    /opt/beelzebub/config/beelzebub.yaml
    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.

    6

    Define Honeypot Services

    Each service is a YAML file in services/. Filenames are organizational only.

    services/ssh-22.yaml — static SSH
    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: 60
    services/http-80.yaml — WordPress decoy
    apiVersion: "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
    services/telnet-23.yaml + tcp-3306.yaml + mcp-8000.yaml
    # 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"}}'
    7

    Compose & Service Lifecycle

    /opt/beelzebub/docker-compose.yml
    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.

    Bring it up + validate
    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/services
    8

    Confirm the Honeypot Works

    From your workstation
    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/mcp
    On the VPS, watch logs
    tail -f /opt/beelzebub/logs/beelzebub.log

    Each 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.

    9

    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):

    services/ssh-22-llm.yaml
    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).

    10

    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.

    Updates
    cd /opt/beelzebub
    docker compose pull
    docker compose up -d

    Pin 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