Skip to content

Redis sensor

An exposed Redis 7.2 server with no password, held entirely in memory. It speaks RESP2 and RESP3 and answers the commands that bots and scanners send with the replies a real server gives. Unknown commands get Redis’ own error text. Each connection starts with its own small fake keyspace of 3 to 5 keys.

It listens on container port 6379. Compose maps the host port REDIS_HOST_PORT (default 6379) to it. No capability is needed.

  • Configuration, persistence and replication are only recorded. CONFIG SET dir and dbfilename followed by SAVE writes nothing. SLAVEOF records the target and never connects. MODULE LOAD and EVAL are recorded, never loaded or run.
  • Nothing is fetched. A URL found in a stored or sent value becomes a download_attempt event.
  • HTTP aimed at Redis (a line starting with POST or Host:) gets the connection closed without a reply, as a real Redis does, and is recorded as a scan.
  • AUTH produces a login_attempt and no command event. Passwords are recorded in clear text.

All events use the shared Event schema.

Event typeContent
connectThe connection is open.
login_attemptusername, password, success, method (redis-auth or redis-hello).
commandinput (the command line, 2048 characters at most), command, argc, flags, output_preview, db, and repeated_before when identical commands were folded.
download_attempturl, tool, command, via (the Redis command that carried the URL).
scanReplication handshake, cross-protocol HTTP, protocol errors, and rate-limited connections.
disconnectduration_s, reason, bytes_in, bytes_out, commands, all indicator flags, config_changes and config_history.

Rules live in ingest/rules/services.yaml. They key on the flags set by the sensor.

TagSet when a command or disconnect carries
persistencecron_write, ssh_key_write, cron_payload, ssh_key_payload
redis-rogue-masterslaveof, module_load
crypto-minerminer, kinsing
db-ransomransom_note
malware-dropAny download_attempt (Redis never fetches files legitimately).

Other flags, such as config_set, flushall, eval and lua_sandbox_escape, are kept on the event for the session view. They do not tag by themselves.

ScopeSetting (environment variable, default)
ConnectionsSENSOR_MAX_CONNECTIONS 200 in total, SENSOR_MAX_PER_IP 8 per source.
New connectionsSENSOR_SESSIONS_PER_SEC_PER_IP 2 per source, SENSOR_GLOBAL_SESSIONS_PER_SEC 50 in total.
TimeoutsFirst data 15 s, idle 45 s, whole connection 240 s, write 10 s.
Per connectionSENSOR_MAX_COMMANDS 3000 commands, SENSOR_MAX_BYTES_IN 4 MiB of input. Arguments are kept to a prefix, so a large payload does not grow memory.
Parser memorySENSOR_MAX_BUFFERED 32 MiB over all connections.
CPUGlob work per connection, per source and for the sensor (SENSOR_CPU_STEPS_*), so one KEYS pattern cannot hold the event loop.
Events200 per second for the sensor, 300 priority, 60 disconnect events. Each source has its own bucket in front.

A watchdog thread exits the process if the event loop stalls for 10 seconds, so Compose restarts the container.

Non-root (UID 10001), read-only root file system, a 16 MB tmpfs at /tmp, all capabilities dropped, no-new-privileges, 256 MB of memory with no swap, 0.5 CPU and 128 processes. Health check: python -m redis_sensor.healthcheck. The only file the sensor writes is its profile secret in the redis-sensor-data volume.