TLS-wrapped variant of the HTTP honeypot. Auto-generates a self-signed
certificate on startup if none is provided. Supports all the same persona
options (fake_app, server_header, custom_body, etc.) plus TLS_CERT,
TLS_KEY, and TLS_CN configuration.
Cowrie was exposing an SSH daemon on port 22 alongside the telnet service
even when COWRIE_SSH_ENABLED=false, contaminating deployments that did not
request an SSH service.
New implementation mirrors the SSH service pattern:
- busybox telnetd in foreground mode on port 23
- /bin/login for real PAM authentication (brute-force attempts logged)
- rsyslog RFC 5424 bridge piped to stdout for Docker log capture
- Configurable root password and hostname via env vars
- No Cowrie dependency
real_ssh was a separate service name pointing to the same template and
behaviour as ssh. Merged them: ssh is now the single real-OpenSSH service.
- Rename templates/real_ssh/ → templates/ssh/
- Remove decnet/services/real_ssh.py
- Deaddeck archetype updated: services=["ssh"]
- Merge test_real_ssh.py into test_ssh.py (includes deaddeck + logging tests)
- Drop decnet.services.real_ssh from test_build module list
Scraps the Cowrie emulation layer. The real_ssh template now runs a
genuine sshd backed by a three-layer logging stack forwarded to stdout
as RFC 5424 for the DECNET collector:
auth,authpriv.* → rsyslogd → named pipe → stdout (logins/failures)
user.* → rsyslogd → named pipe → stdout (PROMPT_COMMAND cmds)
sudo syslog=auth → rsyslogd → named pipe → stdout (privilege escalation)
sudo logfile → /var/log/sudo.log (local backup with I/O)
The ssh.py service plugin now points to templates/real_ssh and drops all
COWRIE_* / NODE_NAME env vars, sharing the same compose fragment shape as
real_ssh.py.
The BASE_IMAGE build arg was being unconditionally overwritten by
composer.py with the decky's distro build_base (debian:bookworm-slim),
turning the conpot container into a bare Debian image with no conpot
installation — hence the silent restart loop.
Two fixes:
1. composer.py: use args.setdefault() so services that pre-declare
BASE_IMAGE in their compose_fragment() win over the distro default.
2. conpot.py: pre-declare BASE_IMAGE=honeynet/conpot:latest in build
args so it always uses the upstream image regardless of decky distro.
Also removed the USER decnet switch from the conpot Dockerfile. The
upstream image already runs as the non-root 'conpot' user; switching to
'decnet' broke pkg_resources because conpot's eggs live under
/home/conpot/.local and are only on sys.path for that user.
- decnet/services/smtp_relay.py: open relay variant of smtp, same template
with SMTP_OPEN_RELAY=1 baked into the environment
- tests/service_testing/__init__.py: init so pytest discovers the subdirectory
Introduces the 'real_ssh' service plugin backed by a genuine OpenSSH
server (not cowrie), and the 'deaddeck' archetype that uses it. The
container ships with a lived-in Linux environment and a deliberately
weak root:admin credential to invite exploitation.
- templates/real_ssh/: Dockerfile + entrypoint (configurable via env)
- decnet/services/real_ssh.py: BaseService plugin, service_cfg supports
password and hostname overrides
- decnet/archetypes.py: deaddeck archetype added
- tests/test_real_ssh.py: 17 tests covering registration, compose
fragment structure, overrides, and archetype
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- HTTP: configurable server_header, response_code, fake_app presets
(apache/nginx/wordpress/phpmyadmin/iis), extra_headers, custom_body,
static files directory mount
- SSH/Cowrie: configurable kernel_version, hardware_platform, ssh_banner,
and users/passwords via COWRIE_USERDB_ENTRIES; switched to build mode
so cowrie.cfg.j2 persona fields and userdb.txt generation work
- SMTP: configurable banner and MTA hostname
- MySQL: configurable version string in protocol greeting
- Redis: configurable redis_version and os string in INFO response
- BYOS: [custom-*] INI sections define bring-your-own Docker services
- Stealth: rename all *_honeypot.py → server.py; replace HONEYPOT_NAME
env var with NODE_NAME across all 22+ service templates and plugins;
strip "honeypot" from all in-container file content
- Config: DeckyConfig.service_config dict; INI [decky-N.svc] subsections;
composer passes service_cfg to compose_fragment
- 350 tests passing (100%)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
dtagdevsec/mailoney and dtagdevsec/elasticpot are unavailable on Docker Hub
("manifest unknown"), causing the entire deployment to abort and cascade-
interrupt all other image pulls.
- Convert smtp and elasticsearch to build services with custom Python
honeypots: smtp emulates Postfix ESMTP (EHLO/AUTH/MAIL/RCPT logging),
elasticsearch emulates ES 7.17 HTTP API (logs recon probes like /_cat/,
/_cluster/, /_nodes/, /_security/)
- Both use ARG BASE_IMAGE so they participate in per-decky distro variation
- Add _compose_with_retry() to deployer: 3 attempts with exponential backoff
(5s → 10s → 20s) for transient network failures; permanent errors
("manifest unknown", "pull access denied") are detected and not retried
- Update test_services.py and test_composer.py: smtp/elasticsearch moved
from UPSTREAM_SERVICES to BUILD_SERVICES (314 tests passing)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>