Threat hunting II: SSH Honeypot
- Introduction
- What is Cowrie?
- Why Podman over Docker?
- Preconditions / System setup
- Setup environment, install cowrie as container and adjust configuration
- Log Forwarding with Filebeat
- π― TL;DR β What Did We Just Do?
- Whats next
Introduction
This post provides a brief walkthrough of how to deploy a lightweight, containerized SSH honeypot using Cowrie and Podman, with the goal of capturing and analyzing malicious activity as part of my threat hunting strategy.
What is Cowrie?
Cowrie is an interactive SSH and Telnet honeypot designed to emulate a real system, capturing attacker behavior in a controlled environment. It allows defenders and researchers to observe malicious activity without exposing actual infrastructure.
Key capabilities of Cowrie include
Full session logging: Records all commands entered by the attacker, along with input/output streams and timing data. Sessions can be saved as plaintext or in formats suitable for replay.
Fake file system and shell environment: Emulates a basic Linux shell with a user-modifiable file system. Attackers can navigate directories, read/write fake files, or attempt to download/upload payloads.
Command emulation: Supports a large set of common Unix commands (`ls`, `cat`, `wget`, etc.), allowing attackers to interact naturally, as if on a real system. And can be extended with more commands
Credential logging: Captures usernames and passwords used in brute-force login attempts or interactive logins.
File download capture: Logs and optionally stores any files attackers attempt to retrieve via `wget`, `curl`, or similar tools.
JSON-formatted logging and integration’s: Outputs structured logs that are easy to parse and ingest into systems like ELK, Splunk, or custom analysis pipelines.
Cowrie is widely used in research, threat intelligence, and proactive defense efforts to gather Indicators of Compromise (IOCs) and understand attacker tactics,techniques, and procedures (TTPs).
Why Podman over Docker?
Podman offers several advantages over Docker, particularly in terms of security and system integration. It supports rootless containers, allowing users to run containers without elevated privileges, which reduces the attack surface.
Podman is daemon-less, integrating more seamlessly with systemd and existing Linux workflows. Additionally, Podman is fully compatible with the Open Container Initiative (OCI) standards, ensuring interoperability and flexibility across container ecosystems.
Preconditions / System setup
Before I proceed with the cowrie setup, I made sure the following preconditions are met:
Ubuntu Installed on Raspberry Pi 4+
I am using a Raspberry Pi 4+ running Ubuntu
System Fully Updated
After installation, I made sure system is up to date:
Podman installed and working
Run the Hello World Container.In this moment I did not had the cowrie user yet setup so I used my system user to test
tho sometimes the pulling fails like that then I had to put `docker.io` in front of the container name like:
then it would work for sure.
VLAN Tagging Configured on Network Interface
In my network setup for threathunting the honeypot requires VLAN tagging to
configured to reachable from the outside, VLAN210 is my restricted Network.
Therefore i needed to configure the vlan using nmcli so it’s persistent across reboots.
Example: Create a VLAN interface (e.g., VLAN ID 210 on main if)
con-name vlan210: Name of the new VLAN connection.dev mainif: Physical interface to tag.id 210: VLAN ID.ip4,gw4: Optional IP and gateway assignment.
This will persist the configuration and activate the VLAN interface immediately. Next I moved on to Install the honeypot.
Setup environment, install cowrie as container and adjust configuration
π§ Create a Dedicated User for Cowrie (No Login Shell)
Running the Podman container under a dedicated system user with no login shell is a recommended security best practice. Reasons include:
Privilege Separation: Isolates the container from other system processes and users, limiting the potential impact of a compromise.
Reduced Attack Surface: The user has no login shell (e.g.,
/usr/sbin/nologin), meaning it can’t be used to log into the system interactively.Auditing & Logging: Helps distinguish container activity in system logs and process lists, making monitoring easier.
Least Privilege Principle: The user has only the permissions necessary to run the container β nothing more.
1. Create the ‘cowrie’ user (no home directory, no login shell)
2. Create necessary directories and set ownership
π³ Pull and Configure Cowrie with Podman
3. As the cowrie user, pull the container image
4. Copy default config file into persistent volume
π cowrie.cfg β Basic Overview
The `cowrie.cfg` file is the main configuration for Cowrie, the SSH/Telnet honeypot we use. It uses INI-style syntax and is divided into sections. Each section begins with a header like [section_name].
π Key Sections & Settings
[ssh]
- Enable or disable SSH/Telnet and set the port to listen on::
[honeypot]
Set honeypot host name and logpath properties:
Define login behavior:
I use AuthRandom here which causes to allow access after “randint(2,5)” attempts. This means the threat actor will fail with some logins and some will be logged in immediately.
[output_jsonlog]
- Configure logging and output plugins:This sets the default log location in the file-system, this is important so that file beat later can pickup on the juicy honeypot log files.
This is the whole configuration needed to run the honeypot.
π Notes
- Restart Cowrie after configuration changes.
- The configuration can be split across multiple `.cfg` files in `cowrie.cfg.d/` for modular setup.
π Run Cowrie Container as ‘cowrie’ User
Once I had created the dedicated system user (see earlier section), I
was able to run the Cowrie container with Podman using sudo -u and UID mapping.
Step-by-Step Command explanation
Explanation
sudo -u cowrie: Runs the Podman command as the unprivilegedcowrieuser.--uidmap 0:999:1001: Maps root (UID 0) inside the container to thecowrieUID on the host.-v /opt/cowrie/etcand/opt/cowrie/var: Mounts configuration and data volumes from the host with `:Z` to apply correct SELinux labels (optional on systems without SELinux).-p 2222:2222: Forwards port 2222 from host to container (Cowrie’s SSH honeypot port).cowrie/cowrie: The container image name (use latest or specific tag as needed).
Benefits:
Container runs as non-root on the host: Even if a process inside the container thinks it’s root, it’s actually limited to the unprivileged
cowrieuser outside the container.Enhanced security: If the container is compromised, the attacker only gets access as the
cowrieuser β not real root.Avoids root-equivalent risks: Prevents privilege escalation or access to sensitive host files and devices.
π― Operating the Honeypot
View logs I think to know how to debug the container is important so we start first with the logs:
Restart container If things go left just restart that thing:
In the logs you can see that cowrie is running and accepting SSH connections:
When the log says “Ready to accept SSH connections” I tested if I could login:
Stop container Nothing special here:
π Automatically Restart Cowrie Podman Container with systemd
To keep your Cowrie container running reliably and restart it if it stops, use a systemd service with restart policies. Please make sure to double check this part on your side as I am no systemd expert at all, for me this just worked.
Step 1: Generate a systemd Service File
Create `/etc/systemd/system/cowrie-container.service` with the following content: You can create the systemd file with the command:
The resulting file looks somewhat like this
- The `–restart-policy=on-failure` makes systemd restart the container if it exits with a failure.
Step 2: Enable the Service
Step 3: (Optional) Add a Health Check Script
To detect if Cowrie stops accepting connections even if the container is still running, create a health check script running as cowrie:
Create `/usr/local/bin/check_cowrie.sh`:
This restarts the service and sends out a notification via pushover.
Make it executable:
Create systemd service `/etc/systemd/system/check_cowrie.service`:
Create systemd timer `/etc/systemd/system/check_cowrie.timer`:
Enable and start the timer:
Summary
- Used Podmanβs systemd integration for automatic restart on container failure.
- Added a health check timer to detect if Cowrie stops accepting connections and restart proactively.
π Security Notes
The `cowrie` user has no login shell (`/usr/sbin/no login`)
Running Cowrie isolated via Podman increases containment
All files are owned by `cowrie`, no root access required for normal operation
Log Forwarding with Filebeat
π¦ Install Filebeat on Ubuntu
1. Add Elasticβs GPG key and repository
2. Update APT and install Filebeat
β Configure and test Filebeat
3. Edit Filebeat config
The filebeat config is straight forward. You have to write a filebeat.input block which contains the path where the logfiles are you need to ingest. And at the end the log-destination (logstash) so that filebeat knows where to send the logs to:
4. (Optional) Test Filebeat config
π Start and Enable Filebeat
5. Enable and start Filebeat
6. Check Filebeat status and logs
π― TL;DR β What Did We Just Do?
1. We deployed Cowrie like pros.
- Ran it safely in a Podman container under a non-login user.
- No mess, no root, no regrets.
2. Logs? Sorted.
- Filebeat scooped up Cowrieβs logs and shipped them to Elasticsearch.
- Now we can actually see who’s knocking on the honeypot door.
3. Everythingβs persistent.
- Configs and logs live outside the container. Cowrie forgets nothingβeven after a reboot.
4. Setup is clean and modular.
- Each part (Cowrie, Filebeat, Elasticsearch) does its job.
- Break one, fix oneβno domino disasters.
5. Itβs nerdy, useful, and kinda fun.
- Now I built a mini threat intel system.
- Now I can sit back, sip coffee, and watch the kiddies play.
Whats next
Next I build the HTTP Honeypot