Cowrie SSH/Telnet Honeypot on Ubuntu 24.04 on Azure User Guide
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.servicerunning the honeypot as the unprivilegedcowrieuser- Emulated SSH honeypot on
2222and Telnet honeypot on2223 cowrie-firstboot.servicefor 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
2222and2223(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.

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.

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.

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.

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.