NetworkManager/Dispatcher

Dieser Artikel wurde für die folgenden Ubuntu-Versionen getestet:

Zum Verständnis dieses Artikels sind folgende Seiten hilfreich:

  1. Einen Editor öffnen

  2. Rechte von Ordnern und Dateien ändern

Inhaltsverzeichnis
  1. Was ist ein Dispatcher-Skript
    1. Aktionen
    2. Environment
  2. Was kann man damit machen?
    1. Beispiele
      1. NM-Ereignisse im Systemlog
      2. Netzwerk Freigaben einbinden
      3. VPN: Datenlecks verhindern
      4. Wifi deaktivieren wenn Ethernet (LAN) ...
  3. Links

Der NetworkManager kann mehr als nur einfach eine Verbindung auf- und wieder abbauen. Er ist in der Lage, zusätzliche Skripte beim Verbindungsauf- und -abbau ausführen. Dies erfolgt über Dispatcher-Skripte, die ausgeführt werden, wenn der NetworkManager eine Aktion durchführt, z.B. wenn er sich zu einem WLAN verbindet oder davon trennt. Diese Funktionen sind jedoch nicht von der grafischen Oberfläche aus konfigurierbar.

Achtung!

Das Skript /etc/NetworkManager/dispatcher.d/01-ifupdown sollte man niemals löschen oder modifizieren. Es ist ein grundlegender Bestandteil der Funktionalität des NetworkManagers und wird in unveränderter Form für die Funktion des NetworkManagers benötigt.

Was ist ein Dispatcher-Skript

Grundsätzlich ist ein Dispatcher-Skript ein ganz normales Shellskript, das im Ordner /etc/NetworkManager/dispatcher.d/ liegt. Diese Skripte werden dann vom NetworkManager bei einer Änderung an den Netzwerken mit zwei Parametern aufgerufen: Netzwerkschnittstelle und Aktion. Wichtig dabei ist, dass root der Besitzer der Skripte ist, da sie sonst nicht ausgeführt werden können.

In dem Ordner können mehrere Skripte liegen, diese werden in alphabetischer Reihenfolge abgearbeitet. Ein Skript liegt bereits in dem Ordner, 01-ifupdown, dieses sorgt dafür, dass Skripte für das Konfigurationssystem ifupdown in den Verzeichnissen unter /etc/network/ mit den richtigen Informationen in ihrer Programmumgebung aufgerufen werden. Auch bei inaktivem ifupdown legen viele Programme hier ihre Startskripte ab.

Die Skripte sollten den Statuswert 0 (= Erfolg) an den Dispatcher zurück geben. Bei einigen Versionen des NetworkManager bricht der Dispatcher den Aufruf weiterer Skripte ab, sobald eines einen Fehler meldet.

Aktionen

Als zweiter Parameter wird dem Script vom NetworkManager der Name der Aktion übergeben, welche die Dispatcherausführung ausgelöst hat.

Dispatcher Aktionen
AktionBedeutungAnwendungsbeispiel
pre-up Die Aktivierung einer Netzwerk-Schnittstelle steht bevor.
up Eine reelle Netzwerk-Schnittstelle wurde aktiviert. Mountet man Netzwerkfreigaben über ein Netzwerk, so kann man dieses an dieser Stelle ausführen lassen.
pre-down Die De-Aktivierung einer Netzwerk-Schnittstelle steht bevor. Hat man Netzwerkfreigaben eingebunden, so sollte man diese vor dem Verbindungsabbau aushängen. Erst dann kann man sicher gehen, dass alle Daten korrekt geschrieben wurden. Beachte: Dieses Ereignis wird nicht immer ausgelöst! (S.u.)
down Eine reelle Netzwerk-Schnittstelle wurde deaktiviert. Für eine Synchronisation über das Netzwerk ist es jetzt zu spät. Man kann aber den Müll wegräumen.
pre-vpn-up Die Aktivierung einer VPN-Schnittstelle steht bevor.
vpn-up Eine virtuelle (VPN) Netzwerk-Schnittstelle wurde aktiviert. Dies ist der richtige Zeitpunkt zur Anpassung der DNS-Namensserver, sofern NetworkManager es nicht richtig macht.
vpn-pre-down Die De-Aktivierung einer VPN-Schnittstelle steht bevor.
vpn-down Eine virtuelle (VPN) Netzwerk-Schnittstelle wurde deaktiviert.
hostname Der Hostname wurde geändert
dhcp4-change Der DHCP lease hat sich geändert (IPv4 Verbidungen)
dhcp6-change Der DHCP lease hat sich geändert (IPv6 Verbidungen)
connectivity-change Die Verbindungsqualität des Netzwerks hat sich geändert. Es gibt die Zustände UNKNOWN, NONE, PORTAL, LIMITED und FULL.

Die Aktionen pre-… funktionieren unter 16.04 gar nicht oder nur unzuverlässig. Ab Ubuntu 18.04 kann man diese benutzen, jedoch werden diese Aktionen nur an Skripte in den entsprechenden Unterverzeichnissen /etc/NetworkManager/dispatcher.d/pre-up.d/ bzw. /etc/NetworkManager/dispatcher.d/pre-down.d/ weitergegeben. Es reicht aus, in diesen Verzeichnissen einen Symlink zum Skript in /etc/NetworkManager/dispatcher.d/ anzulegen.

Hinweis:

Die Aktion pre-down wird nur aufgerufen, wenn NetworkManager selbst die Deaktivierung der Schnittstelle verursacht. Eine von anderer Seite verursachte Abschaltung, wie beispielsweise über rfkill, betätigen eines Hardware-Schalters oder gar Störungen im Netzwerk kann NetworkManager nicht vorher erkennen.

Bei den Aktionen hostname, dhcp* und connectivity-change ist der erste übergebene Parameter entweder none oder möglicherweise leer, aber immer vorhanden. Programmieren ohne ausreichendes Quoting kann zu falschem Verhalten führen.

Environment

Die Skripte werden mit einer spezifischen Programmumgebung gestartet, deren Variablen Informationen zur momentanen Situation der Netzwerk-Konfiguration enthalten, beispielsweise CONNECTION_ID (= Name der Verbindung) oder IP4_ADDRESS_0 (= erste IPv4-Adresse). Die tatsächlich verfügbaren Variablen variieren mit der Aktion (zweiter Parameter); man kann sie in der Praxis ermitteln, indem man sie von einem Skript mit diesem Befehl ins Systemlog schreiben lässt:

1
printenv >&2

Was kann man damit machen?

Eigentlich alles was ein Skript so kann... man sollte aber nur sinnvolle Dinge wie z.B. automatischer Uhrzeitabgleich, Starten eines VPN-Clients oder Umschalten der iptables-Konfiguration machen.

Grundsätzlich kann man die Skripte in jeder Programmiersprache erstellen. Es ist jedoch üblich, für solche Systemskripte die System-Shell zu verwenden, also bei Ubuntu die dash.

Beispiele

NM-Ereignisse im Systemlog

Bei Netzwerkproblemen möchte man oft durch Analyse des Systemlogs Fehler finden, weiß aber nicht, nach welchen Begriffen man suchen soll. In dieser Situation hilft das Skript 00-logger, indem es einfach Meldungen mit bekannten Texten ins Systemlog schreibt.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
#! /bin/dash -e
# Script to dispatch NetworkManager events
#
# /etc/NetworkManager/dispatcher.d/00-logger
# logs calls to syslog.
# See NetworkManager(8) for further documentation of the dispatcher events.

# SPDX-License-Identifier: GPL-2.0-or-later
# (c) 2020 kB @ ubuntuusers.de

myName=${0##*/}
: ${PATH:=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin}

#LOG()	logger --tag="nm-dispatcher-script" --id=$PPID "$@"
#LOG()	logger --tag="nm-dispatcher-script [$PPID]" --id=$$ "$@"
LOG()	logger --tag="$myName [$$]" --id=$PPID "$@"
ERROR()	LOG -p user.err		-- "$@"			# 3
WARN()	LOG -p user.warning	-- "$@"			# 4
NOTE()	LOG -p user.notice	-- "$@"			# 5*
INFO()	LOG -p user.info	-- "$@"			# 6
DEBUG()	LOG -p user.debug	-- "$@"			# 7
DIE()	{ ERROR $0 $* ; false ;}

put_symlink_in()	\
	for dir 
	do	dir=/etc/NetworkManager/dispatcher.d/${dir}.d
		test -e $dir/$myName || ln -s $0 $dir/
	done

test $myName = 00-logger	&&
{ DEBUG $0 $* start.
  case $2 in (vpn*)	NOTE "$2 $DEVICE_IP_IFACE/$DEVICE_IFACE/$1/$CONNECTION_ID "
			ip -4 route | NOTE
  esac
  case $2 in ('')	DIE  no action found.	# Should never happen.
    ;; (hostname)	NOTE Hostname: $(hostname)
    ;; (connectivity*)	NOTE Netzanbindung: $CONNECTIVITY_STATE
			{ ip -6 route ; ip -4 route ;} | INFO
    ;; (dhcp4*)		NOTE DHCP4 $1
			#printenv | grep DHCP4 | DEBUG
    ;; (dhcp6*)		NOTE DHCP6 $1
			#printenv | grep DHCP6 | DEBUG
    ;; (*up|*down)	{ echo -n "$1/$CONNECTION_ID "
			  nmcli --get-values general.state device show $1 |
				sed 's/^[^(]*(//; s/)$//'
			} | NOTE
    ;; (*)		WARN 1=$1, 2=$2.
  esac
  put_symlink_in pre-up pre-down
  DEBUG $0 $* endet.
}			|| DEBUG 00-logger sourced.

Die Meldungen erfolgen mit unterschiedlicher Priorität und Ausführlichkeit, nach denen man filtern kann. Beispielsweise sieht man mit dem Befehl

journalctl -f  -p 0..5 | grep nm-dispatcher 

alle Meldungen von nm-dispatcher mit den Prioritäten 0..5 (= notice) nach Absenden dieses Befehls fortlaufend aktualisiert, darunter auch die Meldungen dieses Skripts. Alle Meldungen bekommt man, indem man 0..7 (= debug) verwendet oder die Option -p ganz weg lässt.

Alternativ kann man bei neueren Versionen des NetworkManagers auch diesen Befehl verwenden:

journalctl -b 0 -p 5 -u NetworkManager-dispatcher.service 

Damit werden alle dem Dispatcher zugeordneten Meldungen ab dem aktuellen Rechnerstart bis Priorität 5 seitenweise angezeigt.

Den Stil der Meldungen kann man durch Wahl zweckmäßiger Optionen für den Systemlogger anpassen, siehe auskommentierte Vorschläge im Skript.

Netzwerk Freigaben einbinden

Die automatische Einbindung von im Netzwerk freigegebener Dateisystemen soll normalerweise über das Init-System (z.B. systemd) erfolgen, jedoch funktioniert das nicht immer. In diesen Fällen kann man alternativ die Einbindung über ein Dispatcher-Skript versuchen. Das hier vorgestellte Skript bindet aus der Datei /etc/fstab alle Dateisysteme ein, welche mit den beiden Optionen _netdev und noauto markiert sind, wenn eine dem Skript bekannte Verbindung aktiviert wird. Beim Deaktivieren dieser Verbindung werden alle eingebundenen Netzwerk-Dateisysteme entfernt.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
#! /bin/dash -e
# Script to dispatch NetworkManager events

# /etc/NetworkManager/dispatcher.d/50-net-shares
# mounts network shares

# See NetworkManager(8) for further documentation of the dispatcher events.

# SPDX-License-Identifier: GPL-2.0-or-later
# (c) 2020 kB @ ubuntuusers.de

# Use "nmcli connection" to find your UUID.
myUUID=b1317c20-4258-4e32-b947-2614c69fee41		# Change this!
#-----------------------------------------------------------------------

case $2
in (vpn-up|up|*pre-down)	test $CONNECTION_UUID = ${myUUID:=none}
;; (*)				false
esac			|| exit 0

. /etc/NetworkManager/dispatcher.d/00-logger
put_symlink_in pre-down

case $2 in (*up)
  awk '/_netdev/ && /noauto/ && ! /^#/' /etc/fstab	|
	while	read source target more
	do	NOTE mounting $source on $target
		mount -v $target 2>&1 | INFO
	done
esac

{ findmnt --output FSTYPE,SOURCE --types nfs,nfs4,cifs |
	grep ''	|| WARN Keine Netzlaufwerke eingebunden.
} | NOTE

case $2 in (*pre-down)
  umount --verbose --lazy --all --types nfs,nfs4,cifs | grep success | NOTE
esac

DEBUG "$0 $* ends."

Dieses Skript benötigt das Skript 00-logger für Meldungen und zur Realisierung der pre-down-Funktionalität. Da diese pre-down-Funktionalität prinzipbedingt jedoch nicht immer funktioniert, ist dieses Skript nicht geeignet zur Verbesserung der Datensicherheit, sondern lediglich eine Komfort-Funktion.

VPN: Datenlecks verhindern

Der Auf- und Abbau von VPN-Verbindungen gehört zur Grundfunktionalität von NetworkManager und bedarf selbst keiner Unterstützung durch Skripte. Jedoch möchte man in der Regel bei VPN-Nutzung verhindern, dass über die immer noch bestehenden Basis-IP-Verbindungen für IPv6 und IPv4 am Tunnel vorbei Informationen für Außenstehende sichtbar bleiben. Ein weiteres Problem stellt der DNS-Namensdienst dar, wenn Abfragen am Tunnel vorbei zu den DNS-Servern gelangen.

Bei einem korrekt konfigurierten VPN stellt NetworkManager bereits eine ziemlich sichere Mitgliedschaft im VPN her. Das hier vorgestellte Skript 30-VPN-only baut darauf auf und realisiert zusätzlich diese Sicherheitsaspekte:

  • Beschränkung der (entweder IPv4- oder IPv6-) Basis-Verbindung auf die Erreichbarkeit des eigenen Routers und das VPN-Gateway. Alle anderen, vor dem Aufbau des VPN möglichen IPv4- und IPv6-Verbindungen werden entweder blockiert oder in den Tunnel geleitet. Dies wird realisiert durch eine zusätzliche, der üblichen Routing-Tabelle main vorgeschaltete Routing-Tabelle. In dieser zusätzlichen Routing-Tabelle werden IP-Ziele entweder direkt verboten (prohibit-Route) oder zur Bearbeitung in weiteren Routing-Tabellen zugelassen (throw-Route).

  • Deaktivierung der für die Basis-Verbindung definierten DNS-Server. Es werden nur noch die durch die VPN-Verbindung vorgegebenen DNS-Server kontaktiert. Diese Funktionalität setzt eine Zusammenarbeit des NetworkManager mit systemd-resolve voraus und funktioniert daher nur ab Ubuntu 18.04.

Dieses Skript beschränkt die Konnektivität des Rechnersystems und ist daher nur für jene geeignet, die Wert auf die durch diese Einschränkung verbesserte Vermeidung von Datenlecks legen! Man kann nicht beides gleichzeitig haben. Mit diesem Skript kann der Rechner Ziele im LAN außer dem Router nicht mehr erreichen und kann auch von Rechnern im LAN nicht mehr erreicht werden.

Man kann im Skript über die Variable myUUID festlegen, für welche VPN-Verbindung das Skript arbeiten soll. Wenn es für jede VPN-Verbindung wirksam sein soll, lässt man die Variable einfach undefiniert.

Es liegt in der Verantwortung jedes VPN-Betreibers, ob sein VPN einen Zugang zum freien Internet ermöglicht oder nicht. In VPN ohne freien Zugang zum Internet muss bei Benutzung dieses Skripts der Konnektivitäts-Check des NetworkManagers ausgeschaltet oder speziel für das VPN umkonfiguriert werden. Anderenfalls wird NetworkManager nach wenigen Minuten die VPN-Verbindung beenden, weil der Konnektivitäts-Status FULL nicht erreicht wurde. Bei VPN mit Zugang zum freien Internet sollte der Konnektivitäts-Check ohne Änderung funktionieren.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
#! /bin/dash -e
# Script to dispatch NetworkManager events

# /etc/NetworkManager/dispatcher.d/30-VPN-only
# disconnects non-VPN networks

# See NetworkManager(8) for further documentation of the dispatcher events.

# SPDX-License-Identifier: GPL-2.0-or-later
# (c) 2020 kB @ ubuntuusers.de

# Use "nmcli connection" to find your UUID.
#myUUID=df719bbe-5a2f-4fa0-aa14-035bb577f040		# Change this!
#-----------------------------------------------------------------------

case $2
in (vpn-*)	test $CONNECTION_UUID = ${myUUID:-$CONNECTION_UUID}
;; (*)		false
esac		|| exit 0

. ${0%/*}/00-logger
put_symlink_in pre-up

IP()	{ ip -${IPv:-4} "$@"	|| true ;}
ROUTE() case $* in (*table*) IP route "$@" ;; (*) ROUTE "$@" table 666 ; esac
RULE()	{ IP rule "$@" pref 1	;}

filter()
{ while RULE show | grep -q '' ; do RULE del ; done
  ROUTE flush
  case $* in (OFF|'') return ; esac

  ROUTE add throw default
  ROUTE show table main | grep -v "dev $1" |
	while read prefix more
	do	case $prefix	in (default)	continue
				;; (*/128)	test $IPv = 6
				;; (*/32)	test $IPv = 4
				;; (*/*)	false
				;; (*)		true
		esac	&& destiny=throw	|| destiny=prohibit
		DEBUG $destiny $prefix
		ROUTE add $destiny $prefix
	done
  RULE add lookup 666
}

X="Erlaube $1, beschränke $DEVICE_IP_IFACE, blockiere andere"
#X="Allow $1, restrict $DEVICE_IP_IFACE, block other"
for IPv in 4 6
do	case $2
	in (vpn-pre-up)	filter  $1 ; NOTE "IPv$IPv-Filter: $X"
	;; (vpn-up)		resolvectl -$IPv revert $DEVICE_IP_IFACE
	;; (vpn-down)		filter OFF ; NOTE IPv$IPv-Filter entfernt.
	esac
done
{ echo "DNS-Servers ($2)" ; resolvectl dns ;} | INFO
DEBUG $* ends.

Dieses Skript benötigt das Skript 00-logger für Meldungen und zur Realisierung der pre-up-Funktionalität.

Wifi deaktivieren wenn Ethernet (LAN) verfügbar ist, und umgekehrt

Dieses Skript ist ab Ubuntu 18.04 nicht erforderlich, da deren NetworkManager die Funktionalität selbst beherrscht.

Am einfachsten legt man das Skript im Terminal an:

sudo nano /etc/NetworkManager/dispatcher.d/10-wifi-off 

Das eigentlich Skript mit Logger nach /var/log/syslog:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
#!/bin/bash
logger "Running $0"
case "$1" in
    eth*)  # Name der Ethernet-Schnittstelle. Bitte mit <ip link> oder <ifconfig -a> ermitteln
           # und hier ggf. ändern!
        if [ "$2" == up ]; then
		logger "$1 $2 wifi off"
		nmcli radio wifi off
        elif [ "$2" == down ]; then
		logger "$1 $2 wifi on"
		nmcli radio wifi on	
	fi
esac

Danach die Rechte richtig setzten:

sudo chmod 755 /etc/NetworkManager/dispatcher.d/10-wifi-off 

Wenn die der Name der Ethernet-Schnittstelle richtig eingetragen wurde, funktioniert das Skript sofort:

LAN-Kabel rein → WLAN wird deaktiviert

LAN-Kabel raus → WLAN wird aktiviert und verbindet sich neu