Proxmox: Erreichbarkeit auf IP von DynDNS Namen begrenzen

Proxmox VE bietet durch die integrierte Firewall bereits die Möglichkeit, die Erreichbarkeit des Hostsystems sehr einfach zu begrenzen. Wer den Zugriff auf die IP-Adresse des Internetzugangs zuhause beschränken möchte, hat es jedoch möglicherweise nicht ganz so einfach, wenn er keine statische IP-Adresse besitzt.

In dem Fall könnte nun ein VPN-Server betrieben werden, zu welchem man sich vorher verbindet und dann auch einen Zugriff auf das Hostsystem hat. Eventuell hat man durch den Einsatz einer Firewall-VM wie OPNsense, pfSense, usw. schon die idealen Voraussetzungen dafür.

Während die VPN-Variante von mir bevorzugt eingesetzt wird, möchte man teilweise vielleicht keinen VPN-Server dafür betreiben. In diesem Fall könnte man das ganze auch wie folgt lösen:

Voraussetzungen

  • DSL-Modem/Router oder NAS/Homeserver, der bei Änderung der IP-Adresse einen dynamischen DNS-Eintrag aktualisiert
  • Script auf dem Proxmox Host, welches in bestimmten Intervall den DNS-Record auflöst und bei Bedarf die IP-Adresse in der Proxmox Firewall aktualisiert

DynDNS einrichten

Auf den DynDNS Teil möchte ich hier eigentlich gar nicht so genau eingehen, wer allerdings idealerweise eine FritzBox zuhause hat, kann hier bereits sehr einfach DynDNS einrichten. Die Aufgabe zur Aktualisierung direkt am DSL-Modem/Router einzurichten, ist auf jedenfall die beste Anlaufstelle, da diesem Gerät natürlich zuerst bekannt ist, wenn sich die IP-Adresse durch einen Reconnect ändert. Alternativ kann das ganze natürlich auch über ein NAS, Raspberry Pi oder einem eigenen Homeserver realisiert werden. Hier gibt es sehr viele Möglichkeiten und ebenso viele Anleitungen im Netz.

Einrichtung auf Proxmox Host

Proxmox VE hat bereits out-of-the Box die Möglichkeit integriert, ein IPSet mit dem Namen „management“ anzulegen. Alle IP-Adressen, die in diesem IPSet eingetragen werden, haben automatisch den Zugriff auf alle relevanten Management Ports, d.h. SSH Port (sofern nicht abweichend von Port 22 konfiguriert), SPICE (3128) und die Proxmox Web-GUI via Port 8006. Wir ersparen uns über diesen Weg also die Erstellung von einzelnen Regeln für jeden Port – das übernimmt Proxmox in diesem Fall automatisch.

Unter Datacenter → IPSet legen wir also zunächst ein neues IPSet mit dem Namen „management“ an:

Nun brauchen wir auf dem Proxmox Host lediglich noch ein Script, welches regelmäßig den DynDNS Namen auflöst und die aktuelle IP-Adresse in die Proxmox Firewall einfügt.

Dazu erstellen wir das Script, beispielsweise /root/update-dynip.sh mit folgendem Inhalt:

#!/bin/bash
 
ipcheck() {
	DYN_NAME=$1
 
	# get IP from DynDNS name
	DYN_IP=$(getent ahostsv4 $DYN_NAME | awk '{ print $1 }'| head -1)
 
	# check if response is valid ip address
	if [[ $DYN_IP =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$ ]]; then
	        echo "DNS Response seems valid ip: $DYN_IP"
 
		# get current IP from PVE Firewall
		FW_IP=$(pvesh get cluster/firewall/ipset/management | grep $DYN_NAME | grep -o '[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}')
 
	        if [[ -z "$FW_IP" ]]; then
			echo "$DYN_NAME not found in cluster.fw, adding $DYN_IP to managment"
			pvesh create cluster/firewall/ipset/management -cidr $DYN_IP -comment $DYN_NAME
		elif [[ "$DYN_IP" == "$FW_IP" ]]; then
                	echo "$DYN_IP already in Firewall, nothing to do"
        	else
                	echo "$DYN_IP is different than $FW_IP - replacing..."
			pvesh delete cluster/firewall/ipset/management/$FW_IP
			pvesh create cluster/firewall/ipset/management -cidr $DYN_IP -comment $DYN_NAME
			#echo "Proxmox Firewall updated from $FW_IP to $DYN_IP"|mail -s "$(hostname -f): DynDNS IP changed" your@mail.tld
        	fi
	else
        	echo "DNS Response for $DYN_NAME seems invalid!"
	fi
 
}
 
if [ $# -eq 0 ]; then
        echo "Usage: $0 yourdyndns.name.tld"
        exit 1
else
        # call ipcheck for every argument
        for arg in "$@"
        do
                ipcheck $arg
        done
fi

Wer bei einer Änderung der IP-Adresse in der Konfiguration eine Mail erhalten möchte (sofern Mail-Versand per Postfix auf dem PVE-Node eingerichtet ist) kann das oben im Script ebenfalls wieder einkommentieren und auf seine Wünsche anpassen.

Nun machen wir das Script noch ausführbar:

chmod +x /root/update-dynip.sh

…und legen einen Cronjob an, z.B. in der /etc/crontab mit einem regelmäßigen Aufruf alle 5 Minuten:

*/5 *	* * *	root	/root/update-dynip.sh yourdyndns.name.tld > /dev/null

Beim Cron Aufruf muss „yourdyndns.name.tld“ entsprechend auf euren DynDNS Namen angepasst werden. Wer mehrere DynDNS Namen auflösen und freigeben möchte, kann diese per Leerzeichen getrennt hinter den Scriptaufruf schreiben:

*/5 *	* * *	root	/root/update-dynip.sh yourdyndns.name.tld yourseconddyndns.name.tld > /dev/null

Sobald beim Aufruf von unserem Script nun festgestellt wird, dass die IP-Adresse des DynDNS Namens von der IP-Adresse in der Proxmox Firewall abweicht, wird die IP-Adresse in der Firewall entsprechend aktualisiert.

Nach einem manuellen Scriptaufruf oder nach dem ersten Cron-Durchlauf sollte die IP-Adresse im Management IPSet hinterlegt sein. Ist dies der Fall, kann die Firewall unter Datacenter → Firewall → Options aktiviert werden, sofern noch nicht der Fall. Auch auf PVE-Node Ebene sollte diese aktiv sein, dies sollte in einer frischen Installation aber schon default so sein.

Zugriff auch auf VMs/CTs beschränken

Wer den Zugriff auch für seine betriebenen VMs und CTs auf IP-Adressen im IPSet „management“ begrenzen möchte, kann für diese ebenfalls die Firewall aktivieren und eine Regel mit der Quelle/Source +management verwenden.

5 Kommentare Schreibe einen Kommentar

  1. Hallo,

    das Script ist echt super und funktioniert einwandfrei. Eine Frage: Ich würde gerne auch das IPv6-Subnetz meines Telekom-Anschlusses freigeben (/56er-Netz), damit ich auch dies in der Firewall freigeben kann. Ich habe bereits ein DYN-DNS welcher die IP-Adresse der Fritz-Box bekommt (sowohl für v4 als auch v6; wobei v6 ja dann die v6-Adresse der Fritz aus dem Subnetz ist). Kann man diese IPv6 script-seitig „abschneiden“, so dass man das kpl. /56er-Netz extrahiert und dieses ebenfalls in der Firewall analog der v4 freigibt?
    Das ganze hätte den Vorteil, dass bei Dual-Stack-Anschlüssen ich nicht erst warten muss, bis er im Hintergrung von IPv6 auf IPv4 wechselt (da mit dem obigen Script nur die IPv4 freigegeben ist).
    Ich hoffe, du verstehst was ich meine 🙂

    Gruß
    Florian

    • Hi,

      könnte man z.B. so machen (apt install sipcalc nicht vergessen):

      #!/bin/bash
       
      # convert ipv6 address to this defined net size
      V6_NET_SIZE=56
       
      ipcheck() {
        DYN_NAME=$1
       
        ######
        # v4 #
        ######
       
        # get IPv4 from DynDNS name
        DYN_IP=$(getent ahostsv4 $DYN_NAME | awk '{ print $1 }'| head -1)
       
        # check if response is valid ip4 address
        if [[ $DYN_IP =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$ ]]; then
          echo "[DNS v4] Response seems valid ip: $DYN_IP"
       
          # get current IP from PVE Firewall
          FW_IP=$(pvesh get cluster/firewall/ipset/management | grep $DYN_NAME | grep -o '[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}')
       
          if [[ -z "$FW_IP" ]]; then
            echo "[DNS v4] $DYN_NAME not found in cluster.fw, adding $DYN_IP to managment"
            pvesh create cluster/firewall/ipset/management -cidr $DYN_IP -comment $DYN_NAME
          elif [[ "$DYN_IP" == "$FW_IP" ]]; then
            echo "[DNS v4] $DYN_IP already in Firewall, nothing to do"
          else
            echo "[DNS v4] $DYN_IP is different than $FW_IP - replacing..."
            pvesh delete cluster/firewall/ipset/management/$FW_IP
            pvesh create cluster/firewall/ipset/management -cidr $DYN_IP -comment $DYN_NAME
            #echo "Proxmox Firewall updated from $FW_IP to $DYN_IP"|mail -s "$(hostname -f): DynDNS IP changed" your@mail.tld
          fi
        else
          echo "[DNS v4] Response for $DYN_NAME seems invalid!"
        fi
       
        ######
        # v6 #
        ######
       
        if ! command -v sipcalc &> /dev/null
        then
          echo "[DNS v6] sipcalc not found, please install this tool for ipv6 if needed"
          exit
        fi
       
        # get IPv6 from DynDNS name
        DYN_IP6=$(getent ahostsv6 $DYN_NAME | awk '{ print $1 }'| grep -v '::ffff'| head -1)
       
        # check if response is valid ip6 address
        if [[ $DYN_IP6 =~ (([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])) ]]; then
          echo "[DNS v6] Response seems valid ip6: $DYN_IP6"
       
          # get current IP6 from PVE Firewall
          FW_IP6=$(pvesh get cluster/firewall/ipset/management | grep $DYN_NAME | grep -oE -e '(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))')
       
          # convert IPv6 from DNS Response to defined $V6_NET_SIZE
          DYN_IP6_NET=$(sipcalc $DYN_IP6/$V6_NET_SIZE|fgrep 'Subnet prefix'|cut -d '-' -f 2|sed 's/ //g')
       
          if [[ -z "$FW_IP6" ]]; then
            echo "[DNS v6] $DYN_NAME not found in cluster.fw, adding $DYN_IP6_NET to managment"
            pvesh create cluster/firewall/ipset/management -cidr $DYN_IP6_NET -comment $DYN_NAME
       
          elif [[ "$DYN_IP6_NET" == "$FW_IP6/$V6_NET_SIZE" ]]; then
            echo "[DNS v6] $DYN_IP6_NET already in Firewall, nothing to do"
       
          else
            echo "[DNS v6] $DYN_IP6_NET is different than $FW_IP6/$V6_NET_SIZE - replacing..."
            pvesh delete cluster/firewall/ipset/management/$FW_IP6/$V6_NET_SIZE
            pvesh create cluster/firewall/ipset/management -cidr $DYN_IP6_NET -comment $DYN_NAME  
            #echo "Proxmox Firewall updated from $FW_IP6/$V6_NET_SIZE to $DYN_IP6_NET"|mail -s "$(hostname -f): DynDNS IP changed" your@mail.tld
          fi
        else
          echo "[DNS v6] Response for $DYN_NAME seems invalid or no AAAA record present!"
        fi
       
      }
       
      if [ $# -eq 0 ]; then
        echo "Usage: $0 yourdyndns.name.tld"
        exit 1
      else
        # call ipcheck for every argument
        for arg in "$@"
        do
          ipcheck $arg
        done
      fi
  2. Hey Timo,

    ich bin erst jetzt nach knapp über einem Jahr über deine Antwort gestolpert…. 🙁
    Ich habe das Paket nachinstalliert und auf den ersten Blick funktioniert es.

    Ich bin gerade nur über das Format der IP verwundert (bin aber auch kein IPv6-Profix was die Syntax angeht).

    Meine Fritz weist folgendes Präfix aus: IPv6-Präfix: 2003:df:7f1d:8f00::/56, Gültigkeit: 82346/82346s
    In Proxmox steht jetzt drin: 2003:df:7fff:1e00:0:0:0:0/56

    Die Fritz Box selbst hat aber folgende IP: IPv6-Adresse: 2003:df:7fff:3077:2e91:abff:fecc:f1b7/64
    (/64er Netz aus dem /56er Netz geschnitten; die Fritz verteilt ja auch noch ein /64er-Netz aus dem /56er an das Gastnetz)
    Die IPv6 des /64er-Netzes steht im DNS

    Zugehöriges Log:
    [DNS v6] Response seems valid ip6: 2003:df:7fff:1e96:2e91:abff:fecc:f1b7
    [DNS v6] xxxx.xxxx not found in cluster.fw, adding 2003:df:7fff:1e00:0:0:0:0/56 to managment

    Ist das so richtig? Oder wird da falsch abgeschnitten?

    Muss man diese „Spezialität“ der Fritz gesondert betrachten?

    Eine weitere Frage:
    Ich habe bei mir die Spezialität das ich dezentral unterschiedliche Subnetzgrößen habe (/48 und /56 mixed).
    Ist es möglich das Script dahingehend anzupassen, dass man die Subnetzgröße für IPv6 im Crontab-Aufruf für die einzelnen dezentrale Standorte mitgibt, anstatt statisch in der Config-Datei um das ganze etwas variabler zu gestalten?
    Und muss man den Spezial-Fall von oben ggf. auch parametrisierbar pro Standort betrachten (64er Netz aus 56er-Netz)?

    Schon mal vielen vielen Dank für dein bisheriges Scripting und deine Unterstützung für meine Anfrage!

    Gruß
    Florian

  3. Kann man die Mail ggf. auch parametrisierbar machen pro Host(s)?
    Ich würde dann die Cron ggf. mit zwei Aufrufparameter einstellen wie:

    /root/update-dyndns.sh xxxx.xxxx.de yyyy.yyyy.de -mail=test@test.de > /dev/null
    Im Script wäre die Mail dann immer einkommentiert und würde aber nur eine Mail versenden, wenn -mail im Aufrufparameter mitgegeben wird?

    Oder, wenn es das einfacher macht:
    Wenn ich mehrere Hosts habe, dann wären auch mehrere Crons und nicht mehrere Hosts in einem Cron denkbar.

    /root/update-dyndns.sh xxxx.xxxx.de -mail=test@test.de > /dev/null
    /root/update-dyndns.sh yyyy.yyyy.de -mail=test@test.de > /dev/null

  4. Ich habe nochmal geschaut.

    Nach dieser Logik weist die Fritz die Netze zu:

    IPv6-Adresse: 2003:df:7fff:313a:2e91:abff:fecc:f1b7/64, Gültigkeit: 14189/1589s
    IPv6-Präfix: 2003:df:7f2f:3e00::/56, Gültigkeit: 81453/81453s
    Heimnetz2003:df:7f2f:3e00::/64
    Gastnetz2003:df:7f2f:3e01::/64
    WAN2003:df:7fff:313a::/64

Schreibe einen Kommentar zu Florian Antworten abbrechen

Pflichtfelder sind mit * markiert.