Aktionen

PhpIPAM MAC-Adressen per arp ermitteln und hinzufügen: Unterschied zwischen den Versionen

Aus znilwiki

Zeile 196: Zeile 196:
#echo "[INFO] Removed token"
#echo "[INFO] Removed token"
</source>
</source>
Und noch ausführbar machen:<br>
chmod +x phpipam-update-mac-from-arp_V1.1.sh
<br>
<br>
----
----
==Konfiguration==
==Konfiguration==
  URL='<nowiki>https://phpipam.znil.local/api</nowiki>'
  URL='<nowiki>https://phpipam.znil.local/api</nowiki>'

Version vom 19. Mai 2025, 11:31 Uhr

Changelog:

  • 19.05.2025 erste Version

Vorwort

phpIPAM kann zu den IP-Adressen auch die MAC-Adressen zuordnen:


Dazu wird jedoch ausschließlich SNMP verwendet. Man muss ein Gerät vom Typ Switch einrichten und die Abfrage der MAC-Adress-Tabelle per SNMP einrichten.
Das macht ja durchaus sinn, jedoch sind meine Allnet-Switche zum Beispiel nicht kompatibel damit.

Im Internet fand ich dieses Skript hier:

https://github.com/lazynooblet/scripts/tree/main/bash/phpipam-update-mac-from-arp

welches die ARP-Tabelle auf dem Linux-System nutzt vom welches es aufgerufen wird.
Ich habe das Skript in 2 Punkten abgepasst:

  • Im Original wird sich per Benutzername und Passwort angemeldet um sich den API-Key zu holen. Meine Variante nutzt einfach gleich den API Key (und versucht auch nicht diesen am Ende zu löschen)
  • Ich lasse vorher einen fping über den Bereich laufen damit der ARP-Cache auch frisch gefüllt ist.


Hinweis:

Das Skript kann natürlich nur MAC-Adressen von Systemen ermitteln die im gleichen IP-Adressbereich liegen wie die das System von dem aus es ausgeführt wird.
Das Skript muss aber auch nicht unbedingt auf dem gleichen Host wie phpIPAM laufen

Das geänderte Skript

nano phpipam-update-mac-from-arp_V1.1.sh
#!/bin/bash

# V1.1
# Original by "lazynooblet" => https://github.com/lazynooblet/scripts/tree/main/bash/phpipam-update-mac-from-arp
# Modified 2025-05-19 by Bernhard Linz => https://znil.net/index.php?title=PhpIPAM_MAC-Adressen_per_arp_ermitteln_und_hinzuf%C3%BCgen
# Changes V1.1:
# - Remove USER and PASSWORD Authentification, use TOKEN instead
# - Add fping before checking MAC-Adresses to fill up ARP-Cache

URL='https://phpipam.znil.local/api'
APP='ARP-CronJob'
#USER='user'
#PASS='passwort'
TOKEN='dI4iAJlvuo9z78xlCUt5N4ubmy1FPyoL'

SUBNET='192.168.0.0/22'
INTERFACE='ens33'
INVALIDTAGS=("3" "4")

#echo "[INFO] Fetching token"
#TOKEN=$(curl -X POST --user ${USER}:${PASS} ${URL}/${APP}/user/ -s | jq --raw-output .data.token)
#if [ "$?" != "0" ]; then
#    echo "[FAIL] Error fetching token from PHPIPAM" 1>&2
#    exit 1
#fi
#if [ -z "${TOKEN}" ]; then
#    echo "[FAIL] Didn't receive valid token from PHPIPAM" 1>&2
#    exit 1
#fi

#echo "[INFO] Fetching token expiry (token test)"
#EXPIRES=$(curl -X GET --header "token: ${TOKEN}" ${URL}/${APP}/user/ -s | jq --raw-output .data.expires)
#if [ "$?" != "0" ]; then
#    echo "[FAIL] Error fetching token expiry from PHPIPAM" 1>&2
#    exit 1
#fi
#if [ -z "${EXPIRES}" ]; then
#    echo "[FAIL] Didn't receive valid token expiry from PHPIPAM" 1>&2
#    exit 1
#fi
echo "[INFO] fping subnet"
fping -4 -a -q -g ${SUBNET}

echo "[INFO] Fetching subnet id"
SUBNETID=$(curl -X GET --header "token: ${TOKEN}" ${URL}/${APP}/subnets/search/${SUBNET} -s | jq --raw-output .data[0].id)
if [ "$?" != "0" ]; then
    echo "[FAIL] Error fetching subnet id from PHPIPAM" 1>&2
    exit 1
fi
if [ -z "${SUBNETID}" ]; then
    echo "[FAIL] Didn't receive valid subnet id from PHPIPAM" 1>&2
    exit 1
fi

_update() {
    ip=${1}
    mac=${2}

    IPID=$(curl -X GET --header "token: ${TOKEN}" ${URL}/${APP}/addresses/${ip}/${SUBNETID} -s | jq --raw-output .data.id)
    if [ "$?" != "0" ]; then
        echo "[FAIL] Error fetching ip id from PHPIPAM" 1>&2
        exit 1
    fi
    if [ -z "${IPID}" ] || [ "${IPID}" = "null" ]; then
        echo "[INFO] address not in phpipam database: ${ip}"
        return
    fi

    NOUPDATE=0
    JSON=$(curl -X GET --header "token: ${TOKEN}" ${URL}/${APP}/addresses/${IPID}/ -s)
    if [ "$?" != "0" ]; then
        echo "[FAIL] Error fetching ip data from PHPIPAM" 1>&2
        exit 1
    fi
    IPTAG=$(echo ${JSON} | jq --raw-output .data.tag)
    for tag in ${INVALIDTAGS[@]}; do
        if [ "${IPTAG}" = "${tag}" ]; then
            echo "[INFO] refusing to update ip with tag: ${IPTAG}"
            NOUPDATE=1
        fi
    done
    if [ ${NOUPDATE} -eq 1 ]; then return; fi

    IPMAC=$(echo ${JSON} | jq --raw-output .data.mac)
    if [ -z "${IPMAC}" ]; then
        echo "[INFO] No mac set for ip"
    fi
    if [ "${IPMAC}" = "${mac}" ]; then
        echo "[INFO] no update required for mac"
        return
    fi

    JSON=$(curl -X PATCH --header "token: ${TOKEN}" ${URL}/${APP}/addresses/${IPID}/ -s --data "mac=${mac}")
    if [ "$?" != "0" ]; then
        echo "[FAIL] Error updating ip mac on PHPIPAM" 1>&2
        exit 1
    fi
    RESULT=$(echo ${JSON} | jq --raw-output .success)
    if [ "${RESULT}" != "true" ]; then
        echo "[FAIL] API returned error updating ip on PHPIPAM: ${JSON}"
        exit 1
    fi

    echo "[INFO] Updated id:${IPID} ip:${ip} mac:${mac}"
}

while read a b c d e; do
    # ? (192.168.1.10) at a1:b2:c3:d4:e5:f6 [ether] on eth0
    line="${a} ${b} ${c} ${d} ${e}"
    ip=$(echo ${b} | sed 's/[^0-9\.]//g')
    mac=${d}

    if [ "${mac}" = "<incomplete>" ]; then
        # we silently skip these
        continue
    fi

    if ! [[ ${mac} =~ ^([0-9A-Fa-f]{2}[:-]){5}([0-9A-Fa-f]{2})$ ]]; then
        echo "[INFO] Invalid mac: ${mac}"
        continue
    fi

    echo "[INFO] Found ip:${ip} mac:${mac}"
    if [ -z "${ip}" ]; then
        echo "[WARN] Unable to parse ip from '${b}': ${line}" 1>&2
    fi
    if [ -z "${mac}" ]; then
        echo "[WARN] Unable to parse mac from '${d}': ${line}" 1>&2
    fi

    _update ${ip} ${mac}

done < <(arp -an -i ${INTERFACE})
if [ "$?" != "0" ]; then
    echo "[FAIL] Error running arp" 1>&2
    exit 1
fi

# get json output from ip for local interfaces
IPJSON=$(ip -j a show ${INTERFACE})
if [ "$?" != "0" ]; then
    echo "[FAIL] Error running ip a show" 1>&2
    exit 1
fi
localmac=$(echo ${IPJSON} | jq --raw-output '.[0].address')

if [[ ${localmac} =~ ^([0-9A-Fa-f]{2}[:-]){5}([0-9A-Fa-f]{2})$ ]]; then
    LENGTH=$(echo ${IPJSON} | jq --raw-output '.[0].addr_info | length')
    for (( i=0; i<${LENGTH}; i++)); do
        localip=$(echo ${IPJSON} | jq --raw-output .[0].addr_info[${i}].local)
        if [ -z "${localip}" ]; then
            echo "[WARN] Unable to parse ip [${i}] from '${IPJSON}'" 1>&2
            continue
        fi
        echo "[INFO] Found interface:${INTERFACE} ip:${localip} mac:${localmac} index:$((${i} +1))/${LENGTH}"
        _update ${localip} ${localmac}
    done
else
    echo "[INFO] Invalid mac from interface: ${localmac}"
fi



#JSON=$(curl -X DELETE --header "token: ${TOKEN}" ${URL}/${APP}/user/ -s)
#if [ "$?" != "0" ]; then
#    echo "[FAIL] Error removing token" 1>&2
#    exit 1
#fi
#RESULT=$(echo ${JSON} | jq --raw-output .success)
#if [ "${RESULT}" != "true" ]; then
#    echo "[FAIL] API returned error when attempting to remove token: ${JSON}"
#    exit 1
#fi
#echo "[INFO] Removed token"

Und noch ausführbar machen:

chmod +x phpipam-update-mac-from-arp_V1.1.sh



Konfiguration

URL='https://phpipam.znil.local/api'
APP='ARP-CronJob'
TOKEN='dI4iAJlvuo9z78xlCUt5N4ubmy1FPyoL'
SUBNET='192.168.0.0/22'
INTERFACE='ens33'
INVALIDTAGS=("3" "4")

Bei der URL setzt Ihr den Aufruf zu eurer phpIPAM Installation ein.
Für APP und TOKEN geht ihr in phpIPAM auf

Administration => API

und führt dort + Create API key aus:


Den Inhalt des Feldes App id könnt Ihr selbst wählen und tragt diesen dann bei APP im Skript ein.
In TOKEN kopiert Ihr den Inhalt des Feldes App code Die Einstellungen für die Berechtigungen App permissions stellt Ihr auf Read / Write / Admin,
und bei App security nehmt Ihr SSL with App code token. Das die Oberfläche von phpIPAM per https erreichbar ist, ist also zwingend erforderlich.

SUBNET='192.168.0.0/22' ist das Netzwerk was gescannt werden soll,
INTERFACE='ens33' die Netzwerkschnittstelle. Den genauen Namen könnt ihr per

ip addr | grep BROAD

abfragen, Beispielausgabe:

2: ens33: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP group default qlen 1000

in diesem Fall also ens33. Da kann auch so etwas wie enp1s0, vmbr0 oder eth0 stehen.
{{{1}}} sorgt dafür, das er die MAC-Adresse bei Adressen vom Typ reserved oder DHCP nicht ändert.
Wenn Ihr das nicht wollt, tragt da z.B. {{{1}}} ein.