feat: improve service detection — expanded signatures, port hints, better nmap args
- Expand service_signatures.json from 35 → ~120 entries covering *arr apps, smart-home (HA, MQTT, ESPHome), cameras (RTSP, Dahua, Tapo, Reolink), network gear (MikroTik, UniFi, Pi-hole), auth (Authelia, Authentik, Vault), monitoring (Grafana, InfluxDB, Loki, Uptime Kuma), containers (Portainer), and many more home lab services - Expand nmap port range to cover home lab ports (8989, 7878, 8123, 554, 1883, etc.) and increase --host-timeout from 30s to 120s for reliable -sV detection - Add _PORT_TYPE_HINTS fallback in suggest_node_type for ports without signatures (cameras→iot, MQTT→iot, Proxmox→proxmox, MikroTik→router, etc.) - Show actual port/protocol (e.g. TCP/9999) instead of "unknown_service" so all open ports are visible even when not matched - Update tests to reflect new unknown-port label format
This commit is contained in:
@@ -44,24 +44,59 @@ def fingerprint_ports(open_ports: list[dict[str, Any]]) -> list[dict[str, Any]]:
|
||||
"category": sig.get("category"),
|
||||
})
|
||||
else:
|
||||
proto = p.get("protocol", "tcp").upper()
|
||||
results.append({
|
||||
"port": p["port"],
|
||||
"protocol": p.get("protocol", "tcp"),
|
||||
"service_name": "unknown_service",
|
||||
"service_name": f"{proto}/{p['port']}",
|
||||
"icon": None,
|
||||
"category": None,
|
||||
})
|
||||
return results
|
||||
|
||||
|
||||
_PORT_TYPE_HINTS: dict[int, str] = {
|
||||
# Proxmox
|
||||
8006: "proxmox",
|
||||
# NAS / storage
|
||||
5000: "nas", # Synology DSM
|
||||
5001: "nas", # Synology DSM HTTPS
|
||||
548: "nas", # AFP
|
||||
873: "nas", # rsync
|
||||
# Routers / network devices
|
||||
8291: "router", # MikroTik Winbox
|
||||
179: "router", # BGP
|
||||
# Cameras / RTSP → iot
|
||||
554: "iot",
|
||||
8554: "iot",
|
||||
37777: "iot", # Dahua
|
||||
34567: "iot", # Amcrest
|
||||
2020: "iot", # Tapo
|
||||
# Smart-home / MQTT → iot
|
||||
1883: "iot",
|
||||
8883: "iot",
|
||||
6052: "iot", # ESPHome
|
||||
# AP / wireless
|
||||
8880: "ap", # UniFi HTTP
|
||||
8443: "ap", # UniFi HTTPS
|
||||
# Switches
|
||||
161: "switch", # SNMP
|
||||
162: "switch", # SNMP trap
|
||||
}
|
||||
|
||||
|
||||
def suggest_node_type(open_ports: list[dict[str, Any]]) -> str:
|
||||
"""Suggest a node type based on the most specific matched signature."""
|
||||
priority = ["proxmox", "nas", "router", "lxc", "vm", "server", "ap", "iot", "switch"]
|
||||
found: set[str] = set()
|
||||
for p in open_ports:
|
||||
sig = match_port(p["port"], p.get("protocol", "tcp"))
|
||||
port = p["port"]
|
||||
proto = p.get("protocol", "tcp")
|
||||
sig = match_port(port, proto)
|
||||
if sig and sig.get("suggested_node_type"):
|
||||
found.add(sig["suggested_node_type"])
|
||||
elif port in _PORT_TYPE_HINTS:
|
||||
found.add(_PORT_TYPE_HINTS[port])
|
||||
for t in priority:
|
||||
if t in found:
|
||||
return t
|
||||
|
||||
@@ -27,7 +27,17 @@ def _nmap_scan(target: str) -> list[dict[str, Any]]:
|
||||
|
||||
nm = nmap.PortScanner()
|
||||
try:
|
||||
nm.scan(hosts=target, arguments="-sV --open -T4 --host-timeout 30s")
|
||||
# Home lab port range: standard top-1000 + common self-hosted service ports
|
||||
extra_ports = (
|
||||
"80,443,22,21,23,25,53,110,143,161,162,179,389,445,548,"
|
||||
"554,636,873,1883,1880,1935,2020,2375,2376,3000,3001,3306,"
|
||||
"3389,4711,5000,5001,5432,5601,5900,5984,6052,6379,6432,6443,"
|
||||
"6767,6789,6800,7878,8000,8006,8080,8081,8086,8088,8090,8096,"
|
||||
"8112,8123,8200,8291,8428,8443,8554,8686,8789,8843,8880,8883,"
|
||||
"8971,8989,9000,9001,9090,9091,9092,9093,9100,9117,9200,9300,"
|
||||
"9411,9443,9696,10051,16686,34567,37777,51413,64738"
|
||||
)
|
||||
nm.scan(hosts=target, arguments=f"-sV --open -T4 --host-timeout 120s -p {extra_ports}")
|
||||
except Exception as exc:
|
||||
logger.error("nmap scan failed: %s", exc)
|
||||
raise RuntimeError(str(exc)) from exc
|
||||
|
||||
Reference in New Issue
Block a user