All checks were successful
build-and-deploy / build (push) Successful in 2m23s
Masscan finds are rtsp:// with no snapshot_url, so the popup skipped the
<img> and the leftover source link handed the OS an rtsp:// URL (VLC).
- Popup always hits /api/cameras/{id}/snapshot (HTTP stills, then one
ffmpeg frame grab). No credentials. 10s hard timeout.
- rtsp:// is rendered as text, never as an href.
- ffmpeg added to the app image for the RTSP still/MJPEG path.
- Default MASSCAN_RATE 200 (1k/10k saturated the home uplink).
65 lines
3 KiB
Python
65 lines
3 KiB
Python
"""Active camera-discovery configuration (masscan-based, env-driven).
|
|
|
|
All knobs read from the environment with safe defaults. The scanner targets
|
|
open TCP port 554 (RTSP — the typical IP-camera port) across a configured
|
|
range and feeds results into the same `cameras` table as the passive scraper
|
|
(discovery_source='masscan'), deduped by URL hash.
|
|
|
|
ETHICS / SCOPE (mirrors camera_scraper.py):
|
|
* Detection only — a SYN port scan for OPEN hosts. No credential guessing,
|
|
no login attempts, no banner grabbing, and no access to camera feeds.
|
|
* Private / reserved ranges are excluded via MASSCAN_EXCLUDEFILE so the
|
|
scanner never probes RFC1918, loopback, link-local, multicast, or the
|
|
bogons. Fail closed if the excludefile is missing.
|
|
|
|
TIMING REALITY: at the residential-safe default of 200 pps a full IPv4
|
|
sweep (0.0.0.0/0, ~4.29B addresses) takes ~8 months. This is therefore a
|
|
CONTINUOUS ROLLING SWEEP, not a "finish in a day" job: masscan streams
|
|
open hosts to stdout and the runner ingests them incrementally, then
|
|
restarts the sweep when a pass completes. New cameras are detected as they
|
|
appear on each pass. 1k/10k pps saturated a home uplink — do not raise the
|
|
rate unless you are on a VPS / unmetered link.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import os
|
|
|
|
# Path to the masscan binary (installed on the Pi host).
|
|
MASSCAN_BIN = os.getenv("MASSCAN_BIN", "masscan")
|
|
|
|
# CIDR(s) to sweep. Default = the whole public IPv4 space.
|
|
MASSCAN_RANGE = os.getenv("MASSCAN_RANGE", "0.0.0.0/0")
|
|
|
|
# Port(s) to probe. Default 554 = RTSP, the typical IP-camera port.
|
|
MASSCAN_PORTS = os.getenv("MASSCAN_PORTS", "554")
|
|
|
|
# Packets/sec. 200 is the residential-safe default — 1k/10k pps saturated
|
|
# a home uplink. Raise only on a VPS / unmetered link.
|
|
MASSCAN_RATE = int(os.getenv("MASSCAN_RATE", "200"))
|
|
|
|
# Retransmission count. 1 maximizes unique-host coverage at low rate; the
|
|
# default (10) spends most of the budget re-probing the same hosts.
|
|
MASSCAN_RETRIES = int(os.getenv("MASSCAN_RETRIES", "1"))
|
|
|
|
# Seconds to keep listening for straggler responses after the last probe.
|
|
# 0 avoids a 10s tail per pass; tiny loss of the very last hosts is fine
|
|
# since the sweep repeats.
|
|
MASSCAN_WAIT = int(os.getenv("MASSCAN_WAIT", "0"))
|
|
|
|
# Excludefile path on the Pi host. Must contain RFC1918/loopback/link-local/
|
|
# multicast/bogons so the scanner never probes private ranges. Fail closed if
|
|
# the file is absent (the runner refuses to start rather than scan wide).
|
|
MASSCAN_EXCLUDEFILE = os.getenv(
|
|
"MASSCAN_EXCLUDEFILE", "/etc/osint-dashboard/masscan-excludes.txt"
|
|
)
|
|
|
|
# Ingest batch size — flush this many newly-seen hosts to the DB per round.
|
|
MASSCAN_FLUSH_EVERY = int(os.getenv("MASSCAN_FLUSH_EVERY", "250"))
|
|
|
|
# NATS subject newly-found cameras are published on (same feed as the
|
|
# passive scraper so the shared ingester persists them).
|
|
MASSCAN_NATS_SUBJECT = os.getenv("MASSCAN_NATS_SUBJECT", "events.camera")
|
|
|
|
# discovery_source tag written into the cameras table.
|
|
MASSCAN_DISCOVERY_SOURCE = os.getenv("MASSCAN_DISCOVERY_SOURCE", "masscan")
|