Add per-service customization, stealth hardening, and BYOS support

- 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>
This commit is contained in:
2026-04-04 04:08:27 -03:00
parent 07c06e3c0a
commit cf1e00af28
102 changed files with 974 additions and 309 deletions

View File

@@ -8,7 +8,7 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
ENV PIP_BREAK_SYSTEM_PACKAGES=1
RUN pip3 install --no-cache-dir twisted jinja2
COPY ftp_honeypot.py /opt/ftp_honeypot.py
COPY server.py /opt/server.py
COPY entrypoint.sh /entrypoint.sh
RUN chmod +x /entrypoint.sh

View File

@@ -1,3 +1,3 @@
#!/bin/bash
set -e
exec python3 /opt/ftp_honeypot.py
exec python3 /opt/server.py

View File

@@ -1,6 +1,6 @@
#!/usr/bin/env python3
"""
FTP honeypot using Twisted's FTP server infrastructure.
FTP server using Twisted's FTP server infrastructure.
Accepts any credentials, logs all commands and file requests,
forwards events as JSON to LOG_TARGET if set.
"""
@@ -15,7 +15,7 @@ from twisted.internet import defer, protocol, reactor
from twisted.protocols.ftp import FTP, FTPFactory
from twisted.python import log as twisted_log
HONEYPOT_NAME = os.environ.get("HONEYPOT_NAME", "ftpserver")
NODE_NAME = os.environ.get("NODE_NAME", "ftpserver")
LOG_TARGET = os.environ.get("LOG_TARGET", "")
@@ -34,7 +34,7 @@ def _log(event_type: str, **kwargs) -> None:
event = {
"ts": datetime.now(timezone.utc).isoformat(),
"service": "ftp",
"host": HONEYPOT_NAME,
"host": NODE_NAME,
"event": event_type,
**kwargs,
}
@@ -42,22 +42,22 @@ def _log(event_type: str, **kwargs) -> None:
_forward(event)
class HoneypotFTP(FTP):
class ServerFTP(FTP):
def connectionMade(self):
peer = self.transport.getPeer()
_log("connection", src_ip=peer.host, src_port=peer.port)
super().connectionMade()
def ftp_USER(self, username):
self._honeypot_user = username
self._server_user = username
_log("user", username=username)
return super().ftp_USER(username)
def ftp_PASS(self, password):
_log("auth_attempt", username=getattr(self, "_honeypot_user", "?"), password=password)
# Accept everything — we're a honeypot
_log("auth_attempt", username=getattr(self, "_server_user", "?"), password=password)
# Accept everything — we're a server
self.state = self.AUTHED
self._user = getattr(self, "_honeypot_user", "anonymous")
self._user = getattr(self, "_server_user", "anonymous")
return defer.succeed((230, "Login successful."))
def ftp_RETR(self, path):
@@ -71,12 +71,12 @@ class HoneypotFTP(FTP):
super().connectionLost(reason)
class HoneypotFTPFactory(FTPFactory):
protocol = HoneypotFTP
class ServerFTPFactory(FTPFactory):
protocol = ServerFTP
if __name__ == "__main__":
twisted_log.startLogging(sys.stdout)
_log("startup", msg=f"FTP honeypot starting as {HONEYPOT_NAME} on port 21")
reactor.listenTCP(21, HoneypotFTPFactory())
_log("startup", msg=f"FTP server starting as {NODE_NAME} on port 21")
reactor.listenTCP(21, ServerFTPFactory())
reactor.run()