colinardo
Goto Top

Mikrotik: mDNS Repeater als Docker-Container auf dem Router (ARM,ARM64,X86)

article-picture

back-to-topEinleitung


mDNS Repeating (Multicast DNS) auf Mikrotik Routern, ein hier oft nachgefragtes Feature, ist zur Zeit der Anleitung noch kein Bestandteil von RouterOS (Mikrotik arbeitet aber offensichtlich bereits daran). Da aber seit einiger Zeit für einige CPU-Architekturen ein Docker-Paket für RouterOS 7 existiert, habe ich ein Docker Image basierend auf der Vorlage dieses Github Repos für den Betrieb direkt auf dem Router gebaut welches mDNS Multicast-Pakete zwischen zwei oder mehr VLANs vermittelt. Damit wird das VLAN übergreifende Bekanntmachen vieler Gerätschaften ermöglicht welche sich normalerweise per mDNS über die Link Local Multicast-Adresse 224.0.0.251 via UDP Port 5353 nur in ihrer eigenen Layer-2 Broadcast-Domain zu erkennen geben. Der Container basiert auf einem sehr Ressourcen sparenden Alpine Linux und das Repeating übernimmt ein mit C programmierter Daemon von Darell Tan.
Ich habe das ganze dann noch in dem Sinne erweitert, dass sich die VLANs über eine Umgebungsvariable des Mikrotik leichter ändern lassen. Zudem stelle ich ein Mikrotik-Script bereit welches die komplette Interface-Einrichtung und das Anlegen des Containers für den Laien automatisiert.

In der Anleitung und im weiter unten bereitgestellten Mikrotik-Skript für die Einrichtung gehe ich davon aus, dass man ein aktuelles VLAN-Setup per "VLAN-Filtering" in einer VLAN-Bridge betreibt, so wie es auch @aqui in seiner Anleitung Mikrotik VLAN Konfiguration ab RouterOS Version 6.41 hier im Forum zeigt.

back-to-topKompatibilität/Verfügbarkeit von Docker-Containern auf Mikrotik Routern


Zur Zeit kommen für den Betrieb von Docker-Containern auf RouterOS nur drei Architekturen in Frage
  • ARM
  • ARM64
  • x86 (z.B. CHR)

Das wären zum Zeitpunkt der Anleitung folgende Modelle (Auszug, ohne Anspruch auf Vollständigkeit)

  • RB3011UiAS-RM
  • RB4011iGS+RM
  • RB5009UG+S+IN /RB5009UPr+S+IN
  • RB1100AHx4
  • CCR2004-16G-2S+PC / CCR2004-16G-2S+ / CCR2004-1G-12S+2XS / CCR2116-12G-4S+ / CCR2216-1G-12XS-2XQ / CCR2004-1G-2XS-PCIe
  • CRS305-1G-4S+IN / CRS310-1G-5S-4S+IN / CRS326-24G-2S+IN / CRS309-1G-8S+IN / CRS328-4C-20S-4S+RM / CRS317-1G-16S+RM
  • netPower 15FR / netPower 16P / FiberBox Plus / netFiber 9
  • und sämtliche Hardware auf die sich die x86-Variante von RouterOS installieren lässt.

back-to-topBenötigte Hardware-Ressourcen


RAM: Zum vernünftigen Betrieb empfiehlt sich ein Router mit mindestens 128MB RAM
Festplatten-Speicher: Der Container selbst begnügt sich je nach Architektur entpackt mit ca. 10 - 15MB.

Sollte das eigene Gerät bspw. nur über 16MB Flash verfügen aber einen USB Port lässt sich das Rootverzeichnis des Containers auch drauf ablegen. Des weiteren hat man ab RouterOS 7.7 auch die Möglichkeit eine RAM-Disk zu erstellen mit der sich ein Teil des Hauptspeichers als Datenablage nutzen lässt.
https://help.mikrotik.com/docs/display/ROS/Disks#Disks-AllocateRAMtofold ...
/disk add type=tmpfs tmpfs-max-size=50M slot=myRamDisk
Hierbei muss man aber im Kopf behalten das der Speicher nach einem Reboot oder Stromausfall geleert wird, man also den Container jedes mal neu anlegen muss.

back-to-topContainer Package auf dem Router installieren


Verfügbar ist das Container-Package seit RouterOS v7.4beta4. Es wird in den Mikrotik-Downloads unter dem Topic Extra Package für eine der oben genannten Architekturen zum Download bereitgestellt.
Aus dem heruntergeladenen ZIP-File extrahiert man nun die Datei container-7.x.npk (das "x" steht für die aktuell verfügbare Version) und schiebt diese nun entweder per Drag n' Drop in die Winbox-Oberfläche oder überträgt die Datei per FTP/SFTP in das Hauptverzeichnis des Routers.
Zum Abschließen der Installation muss der Router zwingend einmal neu gestartet werden. Danach ist das Feature in Winbox unter dem Punkt Container in der linken Navigation verfügbar.

screenshot

back-to-topContainer installieren


Für all diejenigen die sich den Container nicht selbst bauen können/möchten habe ich drei fertige Docker-Images vorbereitet die ihr euch hier herunterladen könnt:


(Build-Datum 03-2023)

Wer sich die Container aus den Sourcen selbst bauen möchte findet weiter unten die Sourcen und Anweisungen zum Build-Vorgang.

Das für eure Architektur passende Image kopiert ihr euch nun in das Hauptverzeichnis auf den Router (wieder per Drag n' Drop in die Winbox oder per FTP/SFTP).

back-to-topVariante A : Automatische Einrichtung über ein Mikrotik-Skript


Für die Einrichtung inkl. der nötigen Interfaces und Zuweisung zur VLAN-Bridge habe ich ein Mikrotik-Skript geschrieben welches euch die Arbeit dafür abnimmt. Es ist einmalig nach dem Kopieren des Images auf dem Router in einem Terminal auszuführen. Wer lieber alle Einstellungen selbst vornehmen möchte, kann sich nach der Variante B im nächsten Punkt richten und diesen Schritt überspringen.

Im Skript anzupassen sind die nachfolgend genannten Variablen im Kopf des Skriptes:

VariableBeschreibung
BRIDGENAMEName der verwendeten VLAN-Bridge in der die VLANs Interfaces Member sind
VETHNAMEDiesen Namen bekommt das neue virtuelle Netzwerk-Interface zum Container
HOSTNAMEHostname des Containers
VLANSEin Array aus VLAN-IDs in die man die mDNS Paktete fluten möchte (Semikolon getrennt)
IMAGENAMEDateiname des Docker-Images mit dem man diesen auf dem Router kopiert hat
CONTAINERROOTOrdner in dem die Daten des Containers auf dem Router abgelegt werden

Für das virtuelle Netzwerk-Interface des Containers verwende ich im Skript ein auf dem Router nicht genutztes Subnetz (172.19.19.0/24), da dieses für den Container nicht benötigt wird, sollte das genutzte Subnetz bei euch schon zum Einsatz kommen ändert es und die GW Adresse auf ein bei euch nicht benutztes Subnetz in folgender Zeile des Skriptes ab:
/interface veth add address=172.19.19.2/24 gateway=172.19.19.1 name=mDNSTrunk
Hat man die Variablen an seine Umgebung angepasst, kopiere man sich das Skript in die Zwischenablage, öffne ein Terminal-Fenster auf dem Router und füge den Inhalt der Zwischenablage in das Terminalfenster ein (WICHTIG: Die abschließende Leerzeile bitte ebenfalls mitkopieren ansonsten muss man auf der Konsole noch einmal Enter drücken damit der Skriptblock{} auch ausgeführt wird.
{
  # name of bridge to add veth interface to
  :local BRIDGENAME "bridgeLocal"  
  # name the veth interface will get
  :local VETHNAME "mDNSTrunk"  
  # set hostname of container
  :local HOSTNAME "mDNS"  
  # set vlan-ids to reflect traffic between
  :local VLANS {100;200}
  # define image filename
  :local IMAGENAME "mdns_x86.tar"  
  # path to data root for the container
  :local CONTAINERROOT "docker/container/mdns_repeater"  

  # -------------------------------
  # variable holds all vlans seperated by spaces
  :local ALLVLANS ""  
  
  # check bridge existance
  :if ([:len [/interface bridge find name="$BRIDGENAME"]] = 0) do={  
    :put "Could not find a bridge with name '$BRIDGENAME' !"  
    :quit
  }
  # add veth interface
  :if ([:len [/interface veth find name="$VETHNAME"]] = 0) do={  
    # add veth interface
    /interface veth add address=172.19.19.2/24 gateway=172.19.19.1 name=$VETHNAME
  }
  # add veth interface to bridge
  :if ([:len [/interface bridge port find interface=$VETHNAME]] = 0) do={
    /interface bridge port add interface=$VETHNAME bridge=$BRIDGENAME
  }
  
  # for each vlan id
  :foreach id in=$VLANS do={
    # find existing vlan entry
    :local vlanentry [/interface bridge vlan find vlan-ids ~ "(^|;)$id(;|\$)" and bridge=$BRIDGENAME]  
    
    :if ([:len $vlanentry] = 0) do={
      # if entry does not exist add it to vlan list
      /interface bridge vlan add bridge=$BRIDGENAME tagged="$BRIDGENAME,$VETHNAME" vlan-ids=$id  
    } else={
      # if entry exists and veth interface is not a tagged member append veth interface to vlan as tagged interface
      :if ([:len [/interface bridge vlan find vlan-ids=$id and current-tagged ~ "$VETHNAME"]] = 0) do={  
        /interface bridge vlan set $vlanentry tagged=([get $vlanentry tagged],"$VETHNAME")  
      }
    }
    # build ALLVLAN variable
    :if ($ALLVLANS != "") do={  
      :set ALLVLANS ($ALLVLANS . " " . $id)  
    } else={
      :set ALLVLANS $id
    }
  }
  # add container environment variable wich defines the vlans used
  :if ([:len [/container envs find name=mdns key=VLANS]] = 0) do={
    /container envs add name=mdns key=VLANS value=$ALLVLANS
  }
  # finally add container
  :if ([:len [/container find root-dir="$CONTAINERROOT"]] = 0) do={  
    /container add file=$IMAGENAME envlist=mdns logging=yes start-on-boot=yes root-dir="$CONTAINERROOT" interface=$VETHNAME hostname=$HOSTNAME  
  } else={
    :put "Container already exists, please delete it beforehand and run again!"  
  }
}
Wenn das Skript nun hoffentlich fehlerfrei durchgelaufen ist🤞, könnt ihr euch unter dem Menüpunkt Container in der Navigation der Winbox davon überzeugen das der Container korrekt angelegt wurde:

Über Winbox:

screenshot

Oder über die Konsole:

/container print

Des weiteren sollte nun eine Docker-Umgebungsvariable mit dem Namen VLANS angelegt sein in welcher die oben angegebenen VLAN-IDs mit Leerzeichen von einander getrennt stehen:

screenshot

Diese Variable bestimmt am Ende die VLAN-IDs welche nach dem Start des Containers per mDNS zusammengeschaltet werden, möchte man also nachträglich die beteiligten VLANs ändern tut man dies hier (Ändert man hier die Interfaces muss das VETH Interface natürlich manuell unter /interface bridge vlan tagged in dem jeweiligen VLAN hinzugefügt werden. Der Container muss danach ebenfalls gestoppt und neu gestartet werden).

back-to-topVariante B : Manuelle Einrichtung


Die nötigen Schritte für eine manuelle Einrichtung des Containers ohne die Nutzung des Skriptes, zeige ich hier anhand von Screenshots in der Winbox.

Als erstes benötigen wir eine neues virtuelles Netzwerkinterface für dem Container (VETH)

screenshot

Das hier angegebene Subnetz (172.19.19.0/24) muss ein nicht genutzter Adressbereich auf dem Router sein. Effektiv wird dieses Netz im Container nicht genutzt da die Kommunikation ausschließlich über die VLAN getaggten Verbindungen stattfindet, es kann also frei gewählt werden.

Nun fügen wir dieses VETH-Interface unter Bridge > Ports der genutzten Bridge hinzu

screenshot

Anschließend unter Bridge >VLANs in den VLAN-IDs das VETH-Interface als tagged eintragen

screenshot

Nun gehen wir in der Winbox in den Abschnitt Container > Envs und fügen eine Container-Umgebungsvariable für die VLANs hinzu. Die VLANs sind dabei mit Leerzeichen voneinander zu trennen.

screenshot

Abschließend erstellen wir den Container selbst, mit Angabe des vorher auf den Router kopierten Images, des VETH-Interfaces, der angelegten Umgebungsvariablen und einem Ordner in den das Image des Containers entpackt wird.

screenshot

back-to-topContainer starten


Nun sind wir bereits soweit den Containers zu starten. Dazu den Container in der Winbox auswählen und auf den Start Button klicken:

screenshot

Über die Konsole (die 0 durch die angezeigte Nummer vor dem Eintrag ersetzen)
/container print
start 0

Am besten man öffnet sich das Log-Fenster und beobachtet dabei die Debug Ausgabe des Containers. Das sollte bei Erfolg dann so aussehen. Im Beispiel werden die VLANs 100 und 200 an den Container angebunden.

screenshot

Man erkennt das für jede VLAN-ID im Container ein VLAN-Interface angelegt wird über die der getaggte Traffic an den Container übertragen werden. uDHCP holt sich dann aus jedem VLAN per DHCP eine IP und am Ende wird der Repeater-Prozess gestartet.

back-to-topShell auf dem Container öffnen


Zum Debugging lässt sich über ein Terminal eine Shell innerhalb des Containers starten. Dazu öffnen wir einen Terminal, lassen uns die Container auflisten, und starten anschließend die Shell
/container print
shell 0
Auch hier die "0" durch die Nummer ersetzen welche dem Container vorangestellt ist.

screenshot

Hier sieht man auch die aktiven Prozesse (Repeater-Prozess und DHCP-Daemons) und die mDNS Listener auf den VLAN-Interfaces innerhalb des Containers

screenshot

Um die Container-Shell wieder zu verlassen drücke man STRG+D.

back-to-topFirewall

Wurde das Setup oben erfolgreich durchlaufen und geprüft das sich die Geräte aus den VLANs übergreifend bekannt machen, war das nur der erste Schritt, da ja erst einmal nur die mDNS-Pakete in die beteiligten VLANs geflutet werden. Da die meisten Benutzer für die Trennung der VLANs natürlich die Firewall nutzen, muss der Traffic zwischen den gewünschten Geräten erst zugelassen werden, sofern er per Default in eurer Firewall-Config blockiert wird.
Da das Firewall-Regelwerk bei jedem anders aufgebaut ist, kann ich hier nur Rezepte an die Hand geben, die ihr an eure Anforderungen anpassen könnt. Wenn Ihr, wovon ich ausgehe, eine Statefull Firewall Konfiguration mit "related/established" Regelwerk einsetzt muss die Regel auch immer nur in eine Richtung angelegt werden, die Rückroute für eine Verbindung ist dann automatisch durchgängig.

Beispiel: Zulassen von User mit IP 192.168.100.50 in VLAN100 auf Port 80 eines Gerätes mit der IP 192.168.200.60 in VLAN200
/ip firewall filter add chain=forward place-before=0 src-address=192.168.100.50 dst-address=192.168.200.60 in-interface=vlan100 out-interface=vlan200 protocol=tcp dst-port=80 action=accept
Beispiel: Zulassen von User mit IP 192.168.100.50 in VLAN100 auf sämtliche Geräte in VLAN200
/ip firewall filter add chain=forward place-before=0 src-address=192.168.100.50 dst-address=192.168.200.0/24 in-interface=vlan100 out-interface=vlan200 action=accept
Wie immer beachtet die Reihenfolge der Filter-Regeln! Die Regel muss vor den generellen DROP-Regeln in der forward Chain stehen damit sie auch greift!

back-to-topDocker Images selbst bauen


Wer sich den Container selbst bauen möchte, für den habe ich hier ein tar-Archiv vorbereitet.

Download mDNS Container Sourcen

Zum Bauen der Container ist ein installiertes Docker mit "dockerx" Erweiterung Voraussetzung.
Das Archiv enthält ein Skript mit welchem man die gewünschte Architektur bauen kann. Die Architektur wird dem Skript als erster Parameter mitgegeben:
sudo ./build_image.sh x86
Wenn man Images für eine Platform bauen möchte die nicht der eigenen entspricht auf der man gerade arbeitet, gibt es ein paar Möglichkeiten für die Emulation. Die einfachste realisiert sich über einen Docker-Container, den man einmalig vor dem Build-Vorgang startet und der sich von selbst wieder beendet:
sudo docker run --privileged --rm docker/binfmt:a7996909642ee92942dcd6cff44b9b95f08dad64
Man schaue dazu auch in folgenden Artikel Multi-Platform Docker Builds.

Nun viel Erfolg und Spaß beim Nachmachen.

Gruß @colinardo

Content-Key: 6478580736

Url: https://administrator.de/contentid/6478580736

Printed on: April 25, 2024 at 15:04 o'clock

Member: SeaStorm
SeaStorm Mar 24, 2023 at 18:04:50 (UTC)
Goto Top
Frage: Warum braucht man das (mDNS)?

Aus Security-Sicht ist das echt übel und sollte IMHO auf Clients und Servern ausgeschaltet werden. mDNS, LLMNR, Netbios etc.
Member: aqui
aqui Mar 24, 2023 updated at 20:04:43 (UTC)
Goto Top
Die meisten Netzdienste wie Drucker, NAS, Medienserver usw. announcen ihre Dienste fast ausnahmslos über mDNS damit Endgeräte diese automatisiert finden. mDNS nutzt eine Link local Multicast Adresse 224.0.0.251 mit UDP Port 5353 und einem festen TTL von 1 und ist damit unroutebar. Sprich es kann ohne Weiteres nicht in andere Netzwerk Segmente übertragen werden.
Das ist insofern fatal in segmentierten Netzen (VLANs) oder auch in einem VPN Umfeld, da Clients diese Dienste in anderen IP Netzen dann gar nicht oder nur schwer erreichen können. Professionelle Geräte haben für sowas oftmals einen mDNS Proxy an Bord. Nichts anderes ist die o.a. Lösung auch. SoHo Hardware hat solch ein Feature eher weniger.
Übel und unsicher ist das keineswegs, denn es sind ja nur UDP Multicast Frames die die IP Adressen dieser Dienste bekannt geben. Nicht mehr und nicht weniger. Microsoft ist da mit seinem SSDP Protokoll deutlich geschwätziger und nutzt ähnliche Mechanismen.
Ein immer wieder nachgefragtes Feature im Forum und Dank an @colinardo für diese elegante Lösung mit Mikrotik Hardware!
Member: colinardo
colinardo Mar 24, 2023 updated at 20:50:33 (UTC)
Goto Top
Frage: Warum braucht man das (mDNS)?
Den Ausführingen von @aqui kann ich mich auch nur anschließen 👍😉. Dank auch dir für die Erläuterungen.
Es ist halt hauptsächlich für das ganze Home-Entertainment und IOT Geraffel, da sich diese Gattung unter anderem darüber im Netz bekannt macht wenn sie nicht uPnP dafür benutzen.
Member: commodity
commodity Mar 25, 2023 at 11:33:35 (UTC)
Goto Top
Danke für diese vorzügliche Arbeit. Wird demnächst sicher umgesetzt.
Insbesondere die Apple-Jünger-Kunden werden sich darüber freuen! Wenn man kein mDNS hat, merkt man erst, wie "dumm" die doch sonst so smarten Apple-Geräte sind face-big-smile

Spart einen Raspberry Pi ein, dient also auch der Nachhaltigkeit face-smile

Viele Grüße, commodity

P.S.: @colinardo, packst Du das auch ins Mikrotik-Forum? Da würden sich sicher einige drüber freuen.
Member: colinardo
colinardo Mar 25, 2023 updated at 13:50:02 (UTC)
Goto Top
Zitat von @commodity:
Danke für die Blumen 🙂.
dient also auch der Nachhaltigkeit face-smile
So auch mein Gedanke 👍.
P.S.: @colinardo, packst Du das auch ins Mikrotik-Forum? Da würden sich sicher einige drüber freuen.
Wahrscheinlich sobald ich die englische Variante der Anleitung fertig habe.

Grüße Uwe
Member: colinardo
colinardo Mar 28, 2023 updated at 11:27:08 (UTC)
Goto Top
Zitat von @commodity:
P.S.: @colinardo, packst Du das auch ins Mikrotik-Forum? Da würden sich sicher einige drüber freuen.

Englische Version ist nun auch online
Mikrotik: mDNS Repeater as Docker-Container on the Router (ARM,ARM64,X86)
Post im Mikrotik Forum
mDNS repeater feature
Member: commodity
commodity Mar 28, 2023 at 13:31:15 (UTC)
Goto Top
Top! Und sehr lässig in den Feature-Request-Thread eingebaut. Das nenn' ich mal vornehme Zurückhaltung. Ich freue mich schon aufs Nachbauen face-smile

Viele Grüße, commodity
Member: colinardo
colinardo Mar 28, 2023 updated at 13:49:26 (UTC)
Goto Top
Und dann muss man lesen:
https://forum.mikrotik.com/viewtopic.php?p=992761#p992798

(normis) MikroTik Support : MikroTik is working on mDNS repeater, but that will come together with a global DNS overhaul and it will be an improvement in all areas, not just this one. This is also why it takes some time to make.

arghh ...Tutorial für'n A.... face-big-smile, aber natürlich schön zu hören das sich da was tut.
Member: commodity
commodity Mar 28, 2023 at 14:14:34 (UTC)
Goto Top
Das dauert noch Jahre... Selbst wenn da nicht dastünde: "it takes some time". Also keine Sorge! face-big-smile

Viele Grüße, commodity
Member: skyacer
skyacer Jul 03, 2023 updated at 14:31:12 (UTC)
Goto Top
Hi,

ich habe den Container einmal nach Variante B (manuell) angelegt. Habs bis auf die VLAN ID benannt.
Leider erhalte ich keine IP Adressen auf den Interfaces. Woran könnte das liegen?

Grüße


EDIT: Fehler selbst gefunden. Man sollte auch seine Hauptbridge auswählen und nicht eine eigene anlegen face-wink
screenshot 2023-07-03 162019
Member: colinardo
colinardo Jul 03, 2023 updated at 14:31:31 (UTC)
Goto Top
Servus.

Du hast offensichtlich keine DHCP-Server in den VLANs die antworten oder du hast das VETH in der Bridge nicht getagged eingetragen.

screenshot

Grüße Uwe
Member: skyacer
skyacer Jul 03, 2023 at 14:32:05 (UTC)
Goto Top
hab den fehler gefunden. Man sollte auch keine neue Bridge anlegen sondern seine normale Bridge nehmen. Dann klappt auch alles....
Member: colinardo
colinardo Jul 03, 2023 at 14:34:24 (UTC)
Goto Top
Kann im Eifer des Gefechts passieren face-smile.
Member: rapkin
rapkin Sep 12, 2023 at 09:54:49 (UTC)
Goto Top
genialer Artikel - hat mir sehr weitergeholfen.

Dummer Weise war das Problem aber nicht mDNS, sondern DLNA (also SSDP) - das repeatet der Container nicht. Ich habe bei Github aber einen anderen Container gefunden:

bonjour-reflector

Das klingt zwar erst mal nach Apple, aber dieser "Reflektor" kann wirklich auch SSDP - auch wenn in der Beschreibung ständig nur die Rede von mDNS ist.
Bitte schlagt mich jetzt nicht, von wegen "SSDP wäre geschwätzig" o.ä. - ich kann auch nix dafür, dass manche (viele) Hersteller das benutzen.
Member: aqui
aqui Sep 12, 2023 at 11:07:18 (UTC)
Goto Top
Das klingt zwar erst mal nach Apple
Ist Unsinn, denn Bonjour basiert auf dem bekannten mDNS Standard und ist wie SSDP Multicast basierend. Beide nutzen eine Link Local Multicast Adresse mit einem vorgeschriebenen TTL von 1 was sie dann unroutebar macht.
AVAHI Proxy oder solche Reflektoren sind dann das Mittel der Wahl.