nmap Script (Hosts ist in meinem Netzwerk)

nmap Script zum Auflisten der Hosts im Netzwerk

Da dies mein erstes Script ist, bin ich für Verbesserungsvorschläge dankbar ! (ab und an denke ich wahrscheinlich zu kompliziert) :slight_smile:

Ich wollte EINEN Host pro Zeile ausgeben mit den Infos „IP, Hostname, MAC, Vendor“, zum einen für die Übersichtlichkeit, zum anderen um Variablen zu sparen.


Version 1.2 (Bei Aktualisierung des Scripts, zuerst bestehende Variablen unter der Dummy-Instanz löschen!)

<?
// nmap Script für ip-Symcon, zum Auflisten der Hosts im Netzwerk
// 1. Erstelle eine Dummy-Instanz im Objektbaum, darunter werden die neuen Variablen erzeugt !
// 2. Installiere nmap -> apt install nmap
// 3. Anpassung der $Subnet Variable
// 4. Testlauf / Ausführen
// 5. Zeitgesteurtes Ereignis für dieses Script erstellen
// Info: Der Aufruf kann lokal [usr/bin/nmap] oder auf Remotesystem [ssh root@host -t nmap] erfolgen,
//       bei Remoteaufruf muss auch Zeile 30 (Kopierbefehl) angepasst werden.
// -------------------------------------------------------------------------- V A R I A B L E N ---
$Instanz = 59717;                                              // Objekt-ID der DUMMY Instanz !
$Nmap = "/usr/bin/nmap";                                       // Pfad zur nmap [apt install nmap]
#$Nmap = "ssh root@px4 -t nmap";                               // Remoteaufruf auf anderem Linux-Host (ssh-copy-id!)
$Subnet = "192.168.0.0/24";                                    // Subnet im CIDR Format "192.168.1.0/24"
$Cleanup = 14;                                                 // Lösche Einträge älter als X Tage
$Debug = true;
// ------------------------------------------------------------------------------------------------
if ( strpos($Nmap,"ssh ") === false ) {
    if (file_exists($Nmap) == false ) {
        echo "$Nmap existiert nicht!
Lösung: apt install nmap";
        die();
    }  
}  

$File = str_replace(".","_",$Subnet);
$File = str_replace("/24",".xml",$File);
$File = "/tmp/".$File;

shell_exec($Nmap." -sn "." $Subnet -oX ".$File);               // Aufruf und Ausgabe in xml 
if ( strpos($Nmap,"ssh ") !== false ) shell_exec("scp root@px4:$File $File"); // --- Remoteaufruf Copybefehl anpassen! ---
$xml = simplexml_load_file($File);

//Profil "nmap" wird erstellt (einmalig)
if (IPS_VariableProfileExists("nmap") == false) {
	IPS_CreateVariableProfile("nmap", 0);
	IPS_SetVariableProfileAssociation("nmap", 0, "Offline", "", 0xff0000);
	IPS_SetVariableProfileAssociation("nmap", 1, "Online", "", 0x2EFE2E);
}

//if($Debug) print_r($xml);
$Counter = 0;
foreach ($xml->host as $host) {
    //IP
    $IP = $xml->host[$Counter]->address[0]['addr'];
    //if($Debug) echo $IP,"
";
    //Hostname
    $Hostname = $xml->host[$Counter]->hostnames->hostname['name'];
    if($Debug) echo $Hostname,"
";
    //Mac
    $MAC = $xml->host[$Counter]->address[1]['addr'];
    if($Debug) echo $MAC,"
";
    //Vendor
    $Vendor = $xml->host[$Counter]->address[1]['vendor'];
    if($Debug) echo $Vendor,"
-----------------------------
";

    CreateVariable($IP,$Hostname,$MAC,$Vendor);

    $Counter++;
}
$summary = $xml->runstats->finished['summary'];
IPS_SetName($Instanz, "$Subnet - $summary");                                  // Schreibe Ergebniss in Instanz-Variable

CleanUp();

//Functions
function CreateVariable($IP,$Hostname,$MAC,$Vendor) {
    global $Instanz, $Debug;
    $IP = IP_Cidr($IP);                                                       // Funktion um IP Adresse 16stellig anzuzeigen (Sortierung!)
    if ($Hostname == "" ) $Hostname = "N/A";
    if ($MAC == "" ) $MAC = "N/A";
    if ($Vendor == "" ) $Vendor = "N/A";
    $TIMEST = date("Y.m.d - H:i");
    $VarName = $IP." --- MAC: ".$MAC." --- Name: ".$Hostname." --- Vendor: ".$Vendor." --- Gesehen am: ".$TIMEST;

    foreach(IPS_GetChildrenIDs($Instanz) as $VarSearchID) {                   // Durchsuche Objektbaum ob die IP schon vorhanden ist..
        $Beschreibung = IPS_GetObject($VarSearchID)['ObjectInfo'];
        if ($IP === $Beschreibung) {
            $VarID = $VarSearchID;
            if($Debug) echo $IP." exist (".$VarID.") 
";
            break;
        }
    }
    if (!isset($VarID)) {                                                     // Wenn keine vorhandene Variable gefunden, neue Variable erzeugen, und einsortieren
        $VarID = IPS_CreateVariable(0);
        if($Debug) echo $IP." create : (".$VarID.") 
";
        IPS_SetParent($VarID, $Instanz);                                      // Variable einsortieren unter dem Objekt mit der ID..
        IPS_SetVariableCustomProfile($VarID, "nmap");
        IPS_SetInfo($VarID, $IP);                                             // IP in Beschreibungsfeld schreiben
    }
    IPS_SetName($VarID, $VarName);                                            // Variable benennen
    SetValue($VarID, true);
}

function CleanUp() {
    global $Instanz, $Cleanup, $Debug;
    foreach(IPS_GetChildrenIDs($Instanz) as $VarID1) {
        //$IP1 = mb_substr(IPS_GetName($VarID1), 0, 15);
        $IP1 = IPS_GetObject($VarID1)['ObjectInfo'];
        //if ($Debug) echo "CleanUp1: ".$IP1." ".$VarID1."
";
        if ( time() - IPS_GetVariable($VarID1)['VariableUpdated'] > $Cleanup*3600*24 ) {    // Lösche nicht geänderte Einträge älter als..
               if ($Debug) echo $IP." (".$VarID1.") wird gelöscht.";
               IPS_DeleteVariable($VarID1);
        }
        if ( time() - IPS_GetVariable($VarID1)['VariableUpdated'] > 40 ) {    // Wenn Variable nicht aktualisiert wurde, Offline setzen..
            if ($Debug) echo $IP." (".$VarID1.") wird Offline gesetzt 
";
            SetValue($VarID1, false);
        }
        foreach(IPS_GetChildrenIDs($Instanz) as $VarID2) {  // Suche ob schon ein Eintrag mit dieser IP besteht, wenn ja den älteren löschen
            //$IP2 = mb_substr(IPS_GetName($VarID2), 0, 15);
            $IP2 = IPS_GetObject($VarID2)['ObjectInfo'];
            if ( $VarID1 !== $VarID2 && $IP1 === $IP2) {
                if ($Debug) echo "CleanUp2: ".$IP2." ".$VarID2."
";
                if (time() - IPS_GetVariable($VarID2)['VariableUpdated'] > time() - IPS_GetVariable($VarID1)['VariableUpdated']) {
                    IPS_DeleteVariable($VarID2);
                } else {
                    IPS_DeleteVariable($VarID1);
                }
            }
        }
    } 
}

function IP_Cidr($IP) {
  return implode('.', array_map(function ($entry) {
    return str_pad($entry, 3, '0', STR_PAD_LEFT);
  }, explode('.', $IP)));
}

Das sieht doch cool aus! Ich habe es mal in Anleitung/Nützliche Skripte verschoben.

Vielleicht hast du Lust ein Modul daraus zu machen? :slight_smile:
Wir haben bei YouTube ein paar Einsteiger Webinare dazu.

paresy

Hi,
nettes Script. Wenn man im IPS-Docker nmap nachinstalliert läuft es da auch.

Ralf

Vielen Dank.
Die Modulerstellung möchte ich mir bei Zeit mal ansehen.

@HarmonyFan
Ich habe mit Docker keine Erfahrung da ich bis dato alles mit Proxmox erledige, jedoch gehe ich schon davon aus wenn die Netzwerkschnittstelle vollständig ist, das dies funktioniert.

Ich habe das Script noch von ein logischen Unschönheit befreit, wird jedoch noch nicht die letzte Verbesserung/Änderung gewesen sein.

Moin SimonS,
das mit Docker war eher für Paresy bzw. andere Nutzer gedacht um zu sagen das es auch mit Docker klappt. @Paresy wie wäre es nmap im Standard-Docker zu übernehmen?

2 Ideen hätte ich vielleicht:

  1. Vielleicht Variablen die Anzahl maximaler und aktueller Geräte enthält statt Name mit (XY hosts up).
  2. Ich habe einen Eventtimer angelegt der alle 15 Minuten das Script aufrufen soll und da gibt es dann „Variable #51559 existiert nicht in /var/lib/symcon/scripts/34664.ips.php on line 100/102/104/106“ da das Script den Eventtimer nicht kennt.

Ralf

Hallo Ralf,

  1. Verstehe ich nicht ganz, bitte detailierter
  2. Hm, liegts nicht daran das alte Variablen nach Update auf die neue ScriptVersion nicht gelöscht wurden ?

Ich befasse mich schon mit der Modulerstellung, komme hier im Moment jedoch nicht weiter

Leider nein. Docker ist dafür gedacht ein minimales Package anzubieten. Der nächste braucht snmp und bald wir haben wir ein riesiges Docker Image :slight_smile: Das muss jeder selber nach Bedarf „nachpflegen“.

paresy

Hi paresy,
OK kann/muss damit leben:-)

@SimonS,
zu 1. Du gibst im Namen des Scriptes an wie viele Hosts gefunden wurden. Schöner wäre es wenn diese Zahl in einer Variablen stehen würde dann könnte man sie archivieren und grafisch darstellen wann wie viele Geräte aktiv waren.

zu 2. Ich habe ein TimerEvent hinzugefügt und wenn jetzt das Script erneut aufgerufen wird findet es auch dieses Event und denkt es wäre ein Host den es überprüfen müsste was aber nicht geht.

Ralf

Hallo Ralf,

  1. das hatte ich in der allerersten Version sogar schon drin, da es jedoch im Summary steht hab ich es dann wieder verworfen, kein Problem kann ich wieder reinnehmen.
  2. Schau dir bitte das Bild im ersten Beitrag an, das Script muss aushalb der Instanz liegen.

Hi SimonS,

  1. wäre schon schön oder?
  2. OK so klappt es. War mal wieder zu schnell.

Neue Idee. Status nur schreiben wenn er sich geändert hat. So könnte man einfacher Programm gesteuert prüfen wie lange ein Gerät schon läuft oder wie lange es nicht gelaufen ist. Mit den Texten ist es schwieriger.

Ralf

  1. Werde ich wieder reinnehmen
  2. Das muss sogar so sein (ist im Moment jedoch nicht der Fall) da ansonsten auch das automatisierte löschen nach X Tagen nicht funktioniert

In der Scriptlogik wahrscheinlich auch einfacher ein Gerät als Instanz anzulegen, und darunter die Variablen

[ul]
[li]IP
[/li][li]MAC
[/li][li]DNS-Name
[/li][li]Vendor
[/li][li]Status
[/li][li]Zuletzt gesehen
[/li][/ul]
zum einen wäre dann jedoch die Variablenanzahl sehr hoch (mal 6), zum anderen die Liste sehr lange zum srollen…

Ich denke das die beste Variante die Ausgabe der Hostinformationen per HTML-Box wäre. (1 Variable für alle Hosts).
Zum einen komme ich jedoch mit dem Thema Modulerstellung und dynamisches Konfigurationsformular nicht weiter,
und der 2 Punkt ist das mir noch nicht klar ist wie ich dann altes und neues Scanergebniss vergleichen soll.

Hi,

Das wäre mein Traum. Ich hatte auch Idee traute mich aber nicht es zu schreiben. Mit den Variablen hast Du natürlich recht. Wären bei mir im Laufe der Zeit um die 600. Das mit der Listenlänge hält sich doch in Grenzen wenn man pro Gerät eine Kategorie anlegt und darin die Variablen anlegt. So ist die Liste genauso lang wie bisher nur wenn man mehr Informationen sehen möchte würde man das Gerät öffnen.

Wenn Du es so machen würdest hätte ich schon eine weitere Idee mit der die Variablenzahl weiter steigt:-) Wenn ein Gerät neu aufgenommen wird mal nmap -r starten und die offenen Ports eintragen. Ich glaube das es einige Leute gibt die erstaunt wären zu erfahren wie viele Port ihre smarte Waschmaschine, Kaffemaschine oder der Kühlschrank offen hat:-)

Ralf

Ich bin schon mit der Modul Erstellung beschäfigt anstatt weiter am Script zu drehen.
Nach einem Tag testen mit „Dynamic im Konfigurationsformular“ gebe ich das auf, und löse das andersrum :rolleyes:

Die Frage ist halt ob mit Variablen oder HMTL-Box das Modul zu bauen ?
Bei HTML-Box wären es keine 10 Variablen die benötigt würden, egal wieviel Hosts und Zeilen/Spalten ausgegeben werden.

die Frage ist ob jemand wirklich Variablen benötigt um weitere Abfragen daraus zu bauen…
Das aufklappen wird nicht funktionieren da ich immer die IP oder Hostname und ONLINE-Status in einer/ersten Zeile haben möchte,
und das wiederum heisst das es keine Kategorie sein kann, sondern weiterhin Boolean ?

Bei HTML-Box stelle ich mir sowas vor wie es Bayaro in seinem Batterie-Monitor verwendet.

Hi,
Batteriemonitor ist bestimmt keine schlechte Vorlage. Die meisten Variablen sind wirklich nur interessant anzusehen. Ausnahme vielleicht online also das was Du jetzt schon hast. So kann man prüfen ob die Geräte noch so arbeiten wie gewollt.

Ich lasse mich mal von deinem Modul überraschen.

Ralf

Sehr cool, bin beim stöbern über deine Lösung gefallen. Ich hab es unabhängig von dir auch mit nmap gemacht. Allerdings speichere ich alles in eine MySql Datenbank am NAS. Ausgabe erfolgt in einer HTML-Box.
Vielleicht interessiert es dich ja

Hab es zwischenzeitlich aber schon sehr überarbeitet. Löschen und Ändern ist eingebaut.

Hi SimonS,
der parameter --system-dns bei nmap wäre nicht schlecht. Ich weiß nicht welchen DNS er nimmt wenn das nicht da steht aber ich habe bei den Gerätenamen ohne diesen Parameter viele Lücken und mit sind alle Namen vorhanden.

Ralf

Hallo Ralf,
ich bin mir ziemlich sicher das nmap die Namensauflösung vom Betriebssystem verwendet.
Ich stelle mir die Frage ob das Symcon Docker-Image einen DNS Client beinhaltet, ist /etc/resolv.conf den vorhanden?

Natürlich wäre es möglich den Schalter in das Modul einzubauen,
schöner fände ich jedoch wenn sich das darunterliegende System darum kümmert.

…ich muss mir mal den Symcon Container installieren :slight_smile:

ok Docker installiert und mal angesehen.
In meinem Container sieht die /etc/resolv.conf so aus:

# --- BEGIN PVE ---
search mydomain.net
nameserver 192.168.0.3
nameserver 192.168.0.4
# --- END PVE ---

Das heißt das Symcon Docker Image verwendet wiederum den DNS des Wirtsystems (also Docker selbst).
Also sollte das eintragen der/des Nameserver in der resolv.conf des Docker Systems zum Ziel führen.

Moin Simon,
bei mir steht es schon richtig drin:

cat /etc/resolv.conf                                                                                                
nameserver      192.168.178.2                                                                                                           
nameserver      8.8.8.8                                                                                                                 
domain  fritz.box        

192.168.178.2 ist mein PiHole als DNS-Server wo ich auch die Namen pflege.

Ralf