Chapter 04 / Security research
SSH Honeypot.
Observe an SSH session without exposing the host shell.
Source-verified session boundaries
- Disposable container per shell session
- 1
- Default container memory limit
- 256 MiB
- Default command execution timeout
- 30 s
- Evidence files per session
- 3
Each session produces metadata, structured events and a transcript, so a reviewer can connect attempted commands to returned output. The configured 256 MiB memory limit bounds each container, and the 30-second subprocess timeout limits how long the manager waits for a command.
Measurement scope & source
These are configurable code defaults and evidence outputs, not measured throughput or containment results. A subprocess timeout does not prove the command inside the container was terminated. Network isolation needs explicit configuration; the project remains a controlled lab implementation.
The idea
A session manager, a disposable container, and a record of what happened.
Python / Paramiko / Docker
Animated architecture
A session and its evidence
A lab architecture. Docker network isolation is a configuration requirement, not a default guarantee.
01 / Connect
02 / Prepare
03 / Interact
04 / Record and close
Control flow / Decisions & data
The lifecycle of an SSH session
The manager owns the connection and evidence. A per-session container handles shell execution, while the command loop maintains the working directory.
Scroll across the diagram to follow each branch
Reading the flow
- The manager creates metadata and records authentication attempts. Failed transport startup or a missing channel logs an error and closes the transport.
- A writable container uses memory, PID and CPU limits, no-new-privileges, and tmpfs mounts. Network isolation only applies when a network is explicitly configured.
- Exit/logout/quit or end-of-input ends the loop. Blank input redraws the prompt. The manager tracks cd state; ordinary commands execute inside the container.
- Command result events record exit code, duration, and output lengths; stdout/stderr return to the SSH client. Session cleanup stops the container, closes transport, and updates metadata.
Why / What / How
The thinking behind the system.
Why observe a session rather than a connection count?
An SSH connection log tells you that someone connected. It does not explain the sequence of commands they attempted or the output they saw. This lab explores collecting that richer interaction as a session that can be reconstructed later.
The system separates an observer, responsible for the SSH connection and evidence, from a disposable execution environment. The aim is to study behavior while keeping the manager’s own shell out of the normal command path.
What lives on each side of the boundary
The Python manager owns Paramiko transport, authentication events, shell channels and session records. Docker supplies a container for a session, and commands are executed through docker exec. Metadata, event records and a transcript preserve different views of the interaction.
A container is a useful lifecycle and resource boundary, not proof of safe isolation. Resource limits and no-new-privileges constrain execution, while network isolation still depends on explicit configuration. The intended environment is an authorized, controlled lab.
How the command loop works
A connection creates a session record and establishes the SSH transport. Once a shell channel is available, the manager prepares the session container. It reads a command, handles working-directory changes, executes the command inside the container and returns output to the SSH client.
The loop repeats until the session ends. Recording both command events and visible output helps distinguish what was requested from what the environment returned. Container cleanup ends the execution environment while the evidence remains available locally for inspection.
What an investigation can learn
A useful review follows the session timeline: authentication attempts, commands, changes of directory and returned output. For example, a command that tries to read a file is more meaningful when its response is visible alongside it. A transcript supports that reconstruction without assuming every attempted command succeeded.
The next hardening questions concern enforced network restrictions, access to the manager and its Docker privileges, and reliable cleanup under failure. The project demonstrates the session and evidence pipeline; it does not claim that exposing a container-backed honeypot publicly is automatically safe.
The starting point
Why this project?
Understanding interactive SSH behavior requires more than a connection count. This project records authentication attempts, commands, and session output for inspection.
My contribution
The work I brought to it.
Built a Python SSH honeypot with a per-session Docker execution environment and structured session records.
How it works
From input to output.
Paramiko handles authentication and shell channels. The manager starts a container, reads commands, executes them with docker exec, and sends output back over SSH. Metadata, events, and transcripts are recorded locally; the container is stopped at session end.
- SSH connection
- Paramiko session
- Container shell
- Session evidence
A design decision
Separate the shell from the observer.
A container per session separates the interaction from the Python manager. Resource limits and no-new-privileges constrain the session, but network isolation still requires explicit configuration.
The evidence
Look under the surface.
These links point to the reviewed source revision, so the implementation behind this story stays inspectable.
Current boundaries
Useful work. Honest limits.
- Network isolation depends on explicit configuration; the manager does not enforce an internal network by default.
- Intended for an authorized, controlled lab. Containerization alone does not establish safe public deployment.
Source reviewed October 3, 2026.
Let’s build something
worth protecting.
“A thoughtful conversation can be the beginning of something worth building.”Connect on LinkedIn