Cs
Security Azure

Cowrie SSH/Telnet Honeypot on Ubuntu 24.04 on Azure User Guide

| Product: Cowrie on Ubuntu 24.04 LTS on Azure

Overview

Cowrie is a widely deployed open source SSH and Telnet honeypot. It presents an emulated, deliberately inviting shell to attackers, then records everything they do: the usernames and passwords they try, every command they run, and every file they try to download. Because the emulated environment is a sandbox with no access to the real machine, it gives you rich, safe visibility into who is probing your infrastructure and how, for threat intelligence, attacker technique research and early breach detection.

The cloudimg image installs Cowrie into a self contained Python virtualenv at /opt/cowrie/cowrie-env, runs it as the cowrie.service systemd unit under a dedicated unprivileged cowrie user, and exposes the emulated attacker surface on dedicated high ports: fake SSH on 2222 and fake Telnet on 2223.

Two cleanly separated planes. The image keeps the honeypot and your real administration apart:

  • Admin plane: the operating system's own SSH on port 22, key only (your Azure SSH key), with no image baked password and no shared bootstrap credential. This is how you administer the box. The honeypot never listens on port 22.
  • Honeypot plane: the emulated SSH (2222) and Telnet (2223) listeners. These intentionally accept weak logins and drop the attacker into a sandboxed emulated shell. That is the product working as designed, and nothing an attacker does there touches the real system.

Per VM identity, no shared fingerprint. A honeypot host key baked once and shared across every image would let attackers fingerprint the deployment. On each VM's first boot, cowrie-firstboot.service generates fresh, unique honeypot host keys and records their fingerprints to a root only info note, so no two deployments share an identity and the image gives nothing away about its origin.

What is included:

  • Cowrie (verified at 3.0.6, BSD 3-Clause) in a self contained virtualenv at /opt/cowrie
  • cowrie.service running the honeypot as the unprivileged cowrie user
  • Emulated SSH honeypot on 2222 and Telnet honeypot on 2223
  • cowrie-firstboot.service for per VM honeypot host keys and the instance info note
  • Structured JSON event log and full TTY session replay capture
  • Real admin SSH kept key only on port 22, separate from the honeypot
  • Ubuntu 24.04 LTS base, fully patched, unattended security upgrades enabled
  • 24/7 cloudimg support, 24h response SLA

Prerequisites

An active Azure subscription, an SSH key pair, and a VNet with a subnet. Cowrie is lightweight; Standard_B2s (2 vCPU, 4 GB RAM) is ample for most deployments. For a honeypot exposed to heavy internet scanning, a Standard_B2ms or Standard_D2s_v5 gives more headroom for session logging. Session logs and TTY captures live on the OS disk, so expand the disk if you expect to collect data over long periods.

Step 1: Deploy the Virtual Machine

Option A: Azure Portal

Search the Marketplace for Cowrie SSH/Telnet Honeypot on Ubuntu 24.04, choose your VM size, and provide your SSH public key. Attach a network security group that allows:

  • TCP 22 (your real admin SSH) from your management network only.
  • TCP 2222 and 2223 (the emulated honeypot) from the networks you want to expose it to. To collect real internet attack traffic, allow these from the internet; to run an internal deception sensor, allow them only from the internal ranges you are monitoring.

Option B: Azure CLI

RG="cowrie-prod"; LOCATION="eastus"; VM_NAME="cowrie-01"
GALLERY_IMAGE_ID="/subscriptions/<sub-id>/resourceGroups/azure-cloudimg/providers/Microsoft.Compute/galleries/cloudimgGallery/images/cowrie-ubuntu-24-04/versions/<version>"
SSH_KEY="$(cat ~/.ssh/id_rsa.pub)"
az group create --name "$RG" --location "$LOCATION"
az network vnet create -g "$RG" --name cowrie-vnet --address-prefix 10.90.0.0/16 --subnet-name cowrie-subnet --subnet-prefix 10.90.1.0/24
az network nsg create -g "$RG" --name cowrie-nsg
az network nsg rule create -g "$RG" --nsg-name cowrie-nsg --name allow-admin-ssh --priority 100 \
  --source-address-prefixes "<your-mgmt-cidr>" --destination-port-ranges 22 --access Allow --protocol Tcp
az network nsg rule create -g "$RG" --nsg-name cowrie-nsg --name allow-honeypot --priority 110 \
  --source-address-prefixes "Internet" --destination-port-ranges 2222 2223 --access Allow --protocol Tcp
az vm create -g "$RG" --name "$VM_NAME" --image "$GALLERY_IMAGE_ID" \
  --size Standard_B2s --storage-sku StandardSSD_LRS \
  --admin-username azureuser --ssh-key-values "$SSH_KEY" \
  --vnet-name cowrie-vnet --subnet cowrie-subnet --nsg cowrie-nsg --public-ip-sku Standard

Step 2: Connect via SSH

Connect on the real admin port 22 with your key. This is the administration plane, kept completely separate from the honeypot.

ssh azureuser@<vm-ip>

Find the public IP with az vm show -d -g cowrie-prod -n cowrie-01 --query publicIps -o tsv.

Step 3: Verify the honeypot is running

Cowrie runs as a systemd service under the unprivileged cowrie user. Confirm it is active and listening on the two emulated ports, while your real SSH stays on port 22.

sudo systemctl is-active cowrie
ss -tln | grep -E ':2222 |:2223 |:22 '
/opt/cowrie/cowrie-env/bin/python -c 'import importlib.metadata as m; print("cowrie", m.version("cowrie"))'

You should see cowrie reporting active, listening sockets on 0.0.0.0:2222 (honeypot SSH), 0.0.0.0:2223 (honeypot Telnet) and 0.0.0.0:22 (your real admin SSH), and the Cowrie version.

Terminal showing the cowrie service active and listening on ports 2222 and 2223 for the honeypot and port 22 for real admin SSH, with the Cowrie version reported

Step 4: Read this VM's instance info

The first boot service wrote a root only note describing the two planes and this VM's unique honeypot host key fingerprints. No two VMs share these fingerprints and none was baked into the image.

sudo cat /root/cowrie-info.txt

The note is owned root:root with mode 0600. It records the honeypot endpoints, the per VM SSH host key fingerprints, and where the event log lives.

Terminal showing the root only instance info note describing the separate admin and honeypot planes, this VM's unique honeypot host key fingerprints, and the event log location

Step 5: See the honeypot in action

Cowrie accepts weak logins on purpose. Run a quick self test against the local honeypot to watch it accept a throwaway credential and drop you into a fully emulated shell that looks like a real server. This runs against loopback on the VM itself, so it never leaves the box; real attackers reach the same emulated shell over the public IP on port 2222.

sshpass -p 'hunter2' ssh -n -p 2222 \
  -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null \
  -o PreferredAuthentications=password -o PubkeyAuthentication=no \
  root@127.0.0.1 'whoami; hostname; uname -a; ls -la /; cat /etc/passwd | head -n 3'

The login succeeds, whoami returns root, the hostname and uname present a convincing fake server, and ls -la / shows an emulated filesystem, all inside a sandbox with no access to the real machine.

Terminal showing a weak login accepted by the honeypot and an emulated shell returning a fake root user, hostname, kernel and filesystem while never touching the real system

Step 6: Read the captured attack events

Every session is written to a structured JSON event log. Inspect the events from the session you just ran: the login attempt with the exact credentials, and every command the attacker typed.

sudo tail -n 12 /opt/cowrie/var/log/cowrie/cowrie.json \
  | jq -c '{eventid, username, password, input, src_ip} | with_entries(select(.value != null))'

You will see a cowrie.login.success event with username and password, and a cowrie.command.input event capturing the exact command line the session ran. Full keystroke replays are stored under /opt/cowrie/var/lib/cowrie/tty/ and can be replayed with /opt/cowrie/cowrie-env/bin/playlog.

Terminal showing structured JSON events from the captured session including the login credentials and the exact commands the attacker ran

Step 7: Forward events to your SIEM

The JSON event log at /opt/cowrie/var/log/cowrie/cowrie.json is designed to be shipped to a log pipeline. Point Filebeat, Fluent Bit, Vector or rsyslog at that file to forward events to Elasticsearch, Splunk, a Graylog input or any SIEM. Cowrie also ships optional output plugins (Elasticsearch, syslog, and others) that you can enable in /opt/cowrie/etc/cowrie.cfg under the matching [output_*] section; restart with sudo systemctl restart cowrie after editing.

Optional: catch attackers on the standard SSH port

By default the honeypot listens on 2222 and 2223 and your real admin SSH owns port 22, so administration always works out of the box. If you want the honeypot to catch attackers hitting the standard SSH port, you must first move your real administrative SSH to another port, then redirect inbound traffic. Doing this in the wrong order will lock you out.

# 1. FIRST move real admin SSH to 2022 and confirm you can reconnect on the new port,
#    then in a NEW session:
# 2. Redirect inbound tcp/22 to the honeypot on 2222:
sudo iptables -t nat -A PREROUTING -p tcp --dport 22 -j REDIRECT --to-port 2222

Persist the iptables rule (for example with iptables-persistent) once you have confirmed admin access on the new port and update your NSG to allow the new admin port from your management network only.

Managing the service

sudo systemctl status cowrie      # health and recent log lines
sudo systemctl restart cowrie     # apply config changes
sudo journalctl -u cowrie -f      # live service log
sudo tail -f /opt/cowrie/var/log/cowrie/cowrie.json   # live attack events

Edit /opt/cowrie/etc/cowrie.cfg to change the emulated hostname, ports, output plugins and shell behaviour, and /opt/cowrie/etc/userdb.txt to tune which credentials the honeypot accepts. Restart the service after any change.

Server Components

Component Version Path
Cowrie 3.0.6 /opt/cowrie (virtualenv /opt/cowrie/cowrie-env)
Emulated SSH honeypot 0.0.0.0:2222
Emulated Telnet honeypot 0.0.0.0:2223
Real admin SSH OpenSSH 0.0.0.0:22 (key only)

Filesystem Layout

Path Description
/opt/cowrie Cowrie state directory (config, logs, captures)
/opt/cowrie/cowrie-env Self contained Python virtualenv
/opt/cowrie/etc/cowrie.cfg Main configuration
/opt/cowrie/etc/userdb.txt Honeypot login policy (which credentials are accepted)
/opt/cowrie/var/log/cowrie/cowrie.json Structured JSON event log
/opt/cowrie/var/lib/cowrie/tty/ Full TTY session replays
/root/cowrie-info.txt Root only per VM instance info note

Persistence, first boot and updates

The Cowrie state, config and logs live under /opt/cowrie on the OS disk and are captured into the image, so a VM launched from this image is ready immediately. On first boot, cowrie-firstboot.service generates the per VM honeypot host keys, resolves the instance address, writes the root only info note, then disables itself so subsequent reboots are unaffected. The OS ships fully patched with unattended security upgrades enabled.

systemctl is-enabled cowrie cowrie-firstboot
apt-mark showhold    # expect no held packages on the fully patched base

Security Recommendations

  • Keep the NSG for port 22 restricted to your management network. Only the honeypot ports (2222/2223) should be broadly reachable.
  • Never place real secrets, credentials or data on this VM. The honeypot is a sensor, and any host exposed to attackers should be treated as disposable.
  • Forward the JSON event log off the box to your SIEM so captured intelligence survives even if the sensor is rebuilt.
  • Review captured sessions regularly and rebuild the sensor periodically from a fresh image so each deployment gets new per VM honeypot host keys.

Support

Every cloudimg deployment includes 24/7 support with a 24 hour response SLA. Contact support@cloudimg.co.uk for help with deployment, configuration or scaling.

This is a repackaged open source software product with additional charges for cloudimg support services. Cowrie is distributed under the BSD 3-Clause license and is a trademark of its respective owner. All product and company names are trademarks or registered trademarks of their respective holders. Use of them does not imply any affiliation with or endorsement by them.