Descoperire locală controlată pentru candidați cameră/NVR. Port probe only, fără parole, fără autentificare, fără stream, fără snapshot.
M10.1 introduces the first local-only camera discovery test. The mini PC performs a manual TCP port probe on the local LAN only, looking for possible camera/NVR candidates. It does not authenticate, does not store passwords, does not open RTSP streams, does not capture snapshots and does not upload media.
#!/bin/bash
set -e
echo "=================================================="
echo "DERIOX GUARD - LOCAL CAMERA DISCOVERY M10.1"
echo "=================================================="
echo "Local LAN only. Run only on a network where you have permission."
echo "No passwords. No authentication. No RTSP stream open. No snapshots. No media upload."
echo ""
CONF="/etc/deriox-guard/agent.env"
GUARD_URL="https://guard.deriox.ro"
AGENT_NAME="mini-pc-pilot-01"
GATEWAY_UUID=""
PORTS="80 443 554 8000 8080 8554 8899 5000 37777"
MAX_HOSTS="254"
if [ -f "$CONF" ]; then
# shellcheck disable=SC1090
source "$CONF" || true
if [ -n "${DERIOX_GUARD_URL:-}" ]; then GUARD_URL="$DERIOX_GUARD_URL"; fi
if [ -n "${DERIOX_AGENT_NAME:-}" ]; then AGENT_NAME="$DERIOX_AGENT_NAME"; fi
if [ -n "${DERIOX_GATEWAY_UUID:-}" ]; then GATEWAY_UUID="$DERIOX_GATEWAY_UUID"; fi
fi
HOST_NAME="$(hostname 2>/dev/null || echo unknown)"
MACHINE_ID="$(cat /etc/machine-id 2>/dev/null || hostname)"
MACHINE_HASH="$(printf "%s" "$MACHINE_ID" | sha256sum | awk '{print $1}')"
IFACE="$(ip route 2>/dev/null | awk '/default/ {print $5; exit}')"
LOCAL_IP="$(ip -4 addr show dev "$IFACE" 2>/dev/null | awk '/inet / {print $2; exit}' | cut -d/ -f1)"
if [ -z "$LOCAL_IP" ]; then
LOCAL_IP="$(hostname -I 2>/dev/null | awk '{print $1}' || echo '')"
fi
if [ -z "$GATEWAY_UUID" ]; then
GATEWAY_UUID="manual-${HOST_NAME}-$(printf "%s" "$MACHINE_HASH" | awk '{print substr($1,1,16)}')"
fi
if ! printf "%s" "$LOCAL_IP" | grep -Eq '^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$'; then
echo "EROARE: Nu pot determina IP local IPv4."
exit 1
fi
BASE_NET="$(printf "%s" "$LOCAL_IP" | awk -F. '{print $1"."$2"."$3}')"
SUBNET_HINT="${BASE_NET}.0/24"
OUT_DIR="/var/lib/deriox-guard/discovery/m1001"
mkdir -p "$OUT_DIR"
chmod 700 /var/lib/deriox-guard /var/lib/deriox-guard/discovery "$OUT_DIR" 2>/dev/null || true
STAMP="$(date +%Y%m%d_%H%M%S)"
CAND_FILE="$OUT_DIR/candidates-$STAMP.jsonl"
: > "$CAND_FILE"
chmod 600 "$CAND_FILE" 2>/dev/null || true
echo "Gateway UUID: $GATEWAY_UUID"
echo "Interface: ${IFACE:-unknown}"
echo "Local IP: $LOCAL_IP"
echo "Subnet: $SUBNET_HINT"
echo "Ports: $PORTS"
echo ""
HOSTS_TESTED=0
OPEN_PORT_COUNT=0
CANDIDATE_COUNT=0
for i in $(seq 1 "$MAX_HOSTS"); do
IP="${BASE_NET}.${i}"
if [ "$IP" = "$LOCAL_IP" ]; then
continue
fi
HOSTS_TESTED=$((HOSTS_TESTED+1))
OPEN_PORTS=""
for PORT in $PORTS; do
if timeout 1 bash -c "cat < /dev/null > /dev/tcp/${IP}/${PORT}" 2>/dev/null; then
OPEN_PORTS="${OPEN_PORTS}${PORT},"
OPEN_PORT_COUNT=$((OPEN_PORT_COUNT+1))
fi
done
if [ -n "$OPEN_PORTS" ]; then
OPEN_PORTS="${OPEN_PORTS%,}"
CONFIDENCE=45
echo "$OPEN_PORTS" | grep -Eq '(^|,)554(,|$)' && CONFIDENCE=$((CONFIDENCE+20))
echo "$OPEN_PORTS" | grep -Eq '(^|,)80|8080|8000(,|$)' && CONFIDENCE=$((CONFIDENCE+10))
echo "$OPEN_PORTS" | grep -Eq '(^|,)37777|8899|8554(,|$)' && CONFIDENCE=$((CONFIDENCE+15))
if [ "$CONFIDENCE" -gt 95 ]; then CONFIDENCE=95; fi
CANDIDATE_COUNT=$((CANDIDATE_COUNT+1))
printf '{"local_ip":"%s","open_ports":"%s","confidence_score":%s,"candidate_type":"possible_ip_camera_or_nvr","local_network_only":1,"port_probe_only":1,"no_authentication_attempt":1,"no_stream_open":1,"no_snapshot_capture":1,"no_media_upload":1}\n' "$IP" "$OPEN_PORTS" "$CONFIDENCE" >> "$CAND_FILE"
echo "Candidate: $IP ports=$OPEN_PORTS confidence=$CONFIDENCE"
fi
done
if command -v python3 >/dev/null 2>&1; then
PAYLOAD_FILE="$OUT_DIR/payload-$STAMP.json"
python3 - "$CAND_FILE" "$PAYLOAD_FILE" <<PY
import json, sys, os
cand_file, payload_file = sys.argv[1], sys.argv[2]
candidates = []
if os.path.exists(cand_file):
with open(cand_file, "r", encoding="utf-8") as f:
for line in f:
line=line.strip()
if line:
candidates.append(json.loads(line))
payload = {
"gateway_uuid": "$GATEWAY_UUID",
"agent_name": "$AGENT_NAME",
"agent_version": "m10.1-local-camera-discovery",
"host_name": "$HOST_NAME",
"interface_name": "${IFACE:-unknown}",
"local_ip": "$LOCAL_IP",
"subnet_hint": "$SUBNET_HINT",
"ports_tested": "$PORTS",
"hosts_tested": $HOSTS_TESTED,
"open_port_count": $OPEN_PORT_COUNT,
"candidate_count": $CANDIDATE_COUNT,
"manual_run": 1,
"local_network_only": 1,
"port_probe_only": 1,
"no_authentication_attempt": 1,
"no_password_storage": 1,
"no_stream_open": 1,
"no_snapshot_capture": 1,
"no_media_upload": 1,
"service_autostart_enabled": 0,
"cron_enabled": 0,
"production_go_live_enabled": 0,
"remote_camera_access_enabled": 0,
"stream_link_enabled": 0,
"media_enabled": 0,
"billing_enabled": 0,
"candidates": candidates[:500],
}
with open(payload_file, "w", encoding="utf-8") as f:
json.dump(payload, f, ensure_ascii=False)
PY
curl -k -sS \
-H "Content-Type: application/json" \
-X POST "$GUARD_URL/api/gateway/mini-pc/camera-discovery-local/report" \
--data-binary "@$PAYLOAD_FILE"
else
curl -k -sS \
-H "Content-Type: application/json" \
-X POST "$GUARD_URL/api/gateway/mini-pc/camera-discovery-local/report" \
--data "{\"gateway_uuid\":\"$GATEWAY_UUID\",\"agent_name\":\"$AGENT_NAME\",\"agent_version\":\"m10.1-local-camera-discovery\",\"host_name\":\"$HOST_NAME\",\"interface_name\":\"${IFACE:-unknown}\",\"local_ip\":\"$LOCAL_IP\",\"subnet_hint\":\"$SUBNET_HINT\",\"ports_tested\":\"$PORTS\",\"hosts_tested\":$HOSTS_TESTED,\"open_port_count\":$OPEN_PORT_COUNT,\"candidate_count\":$CANDIDATE_COUNT,\"manual_run\":1,\"local_network_only\":1,\"port_probe_only\":1,\"no_authentication_attempt\":1,\"no_password_storage\":1,\"no_stream_open\":1,\"no_snapshot_capture\":1,\"no_media_upload\":1}"
fi
echo ""
echo "M10.1 local discovery complete."
echo "Candidates file: $CAND_FILE"
echo "No authentication, no stream, no snapshot, no media upload."