1 ## @defgroup on-openvpn Nutzer-Tunnel
2 ## @brief Alles rund um die Nutzertunnel-Verbindung: Tests, Auswahl, Aufbau, Abbau, Portweiterleitungen und Logs.
3 # Beginn der Doku-Gruppe
6 MIG_OPENVPN_DIR=/etc/openvpn/opennet_user
7 MIG_OPENVPN_CONFIG_TEMPLATE_FILE=/usr/share/opennet/openvpn-mig.template
9 # Pakete mit dieser TOS-Markierung duerfen nicht in den Tunnel
11 ## Quelldatei für Standardwerte des Nutzer-VPN-Pakets
12 ON_OPENVPN_DEFAULTS_FILE=/usr/share/opennet/openvpn.defaults
13 MIG_PREFERRED_SERVERS_FILE=/var/run/mig-tunnel-
servers.list
18 ## @fn get_on_openvpn_default()
19 ## @brief Liefere einen der default-Werte der aktuellen Firmware zurück (Paket on-openvpn).
20 ## @param key Name des Schlüssels
21 ## @sa get_on_core_default
28 ## @fn has_mig_openvpn_credentials()
29 ## @brief Prüft, ob der Nutzer bereits einen Schlüssel und ein Zertifikat angelegt hat.
30 ## @returns Liefert "wahr", falls Schlüssel und Zertifikat vorhanden sind oder
31 ## falls in irgendeiner Form Unklarheit besteht.
34 trap
"" $GUARD_TRAPS &&
return 1
38 ## @fn verify_mig_gateways()
39 ## @brief Durchlaufe die Liste aller Internet-Gateway-Dienste und aktualisieren deren Status.
40 ## @see run_cyclic_service_tests
42 local max_fail_attempts
43 local test_period_minutes
50 ## @fn select_mig_connection()
51 ## @brief Aktiviere den angegebenen VPN-Gateway
52 ## @param wanted Name eines Diensts
53 ## @attention Seiteneffekt: Beräumung aller herumliegenden Konfigurationen von alten Verbindungen.
55 trap
"error_trap select_mig_connection '$*'" $GUARD_TRAPS
59 # loesche Flags fuer die Vorselektion
60 set_service_value
"$one_service" "switch_candidate_timestamp" ""
61 # erst nach der Abschaltung der alten Dienste wollen wir den/die neuen Dienste anschalten (also nur Ausgabe)
62 [
"$one_service" =
"$wanted" ] && echo
"$one_service" &&
continue
64 done |
while read one_service;
do
70 ## @fn find_and_select_best_gateway()
71 ## @brief Ermittle den besten Gateway und prüfe, ob ein Wechsel sinnvoll ist.
72 ## @param force_switch_now [optional] erzwinge den Wechsel auf den besten Gateway unabhängig von Wartezeiten (true/false)
75 trap
"error_trap find_and_select_best_gateway '$*'" $GUARD_TRAPS
76 local force_switch_now=${1:-
false}
80 local current_gateway=
82 local current_priority
84 local switch_candidate_timestamp
86 local bettergateway_timeout
87 now=$(get_uptime_minutes)
89 msg_debug "Trying to find a better gateway"
90 # suche nach dem besten und dem bisher verwendeten Gateway
91 # Ignoriere dabei alle nicht-verwendbaren Gateways.
96 |
while read service_name;
do
97 # Ist der beste und der aktive Gateway bereits gefunden? Dann einfach weiterspringen ...
98 # (kein Abbruch der Schleife - siehe weiter unten - Stichwort SIGPIPE)
99 [ -n
"$current_gateway" ] &&
continue
101 uci_is_false
"$(get_service_value "$service_name
" "status
" "false")" && \
102 msg_debug "$host did not pass the last test" && \
104 # der Gateway ist ein valider Kandidat
105 # Achtung: Variablen innerhalb einer
"while"-Sub-Shell wirken sich nicht auf den Elternprozess aus
106 # Daher wollen wir nur ein bis zwei Zeilen:
108 # [den aktiven] (falls vorhanden)
109 # Wir brechen die Ausgabe jedoch nicht nach den ersten beiden Zeilen ab. Andernfalls muessten wir
110 # uns um das SIGPIPE-Signal kuemmern (vor allem in cron-Jobs).
111 [ -z
"$best_gateway" ] && best_gateway=
"$service_name" && echo
"$best_gateway"
112 [ -n
"$(get_openvpn_service_state "$service_name
")" ] && current_gateway=
"$service_name" && echo
"$service_name" ||
true
114 best_gateway=$(echo
"$result" | sed -n 1p)
115 current_gateway=$(echo
"$result" | sed -n 2p)
116 if [
"$current_gateway" =
"$best_gateway" ]; then
117 if [ -z
"$current_gateway" ]; then
118 msg_debug "There is still no usable gateway around"
120 msg_debug "Current gateway ($current_gateway) is still the best choice"
121 # Wechselzaehler zuruecksetzen (falls er hochgezaehlt wurde)
122 set_service_value
"$current_gateway" "switch_candidate_timestamp" ""
126 msg_debug "Current ($current_gateway) / best ($best_gateway)"
127 # eventuell wollen wir den aktuellen Host beibehalten (sofern er funktioniert und wir nicht zwangsweise wechseln)
128 if [ -n
"$current_gateway" ] \
129 && uci_is_false
"$force_switch_now" \
130 && uci_is_true
"$(get_service_value "$current_gateway
" "status
" "false")"; then
131 # falls der beste und der aktive Gateway gleich weit entfernt sind, bleiben wir beim bisher aktiven
132 current_priority=$(get_service_priority
"$current_gateway")
133 best_priority=$(get_service_priority
"$best_gateway")
134 [
"$current_priority" -eq
"$best_priority" ] \
135 &&
msg_debug "Keeping current gateway since the best gateway has the same priority" \
137 # falls der beste und der aktive Gateway gleich weit entfernt sind, bleiben wir beim bisher aktiven
138 # Haben wir einen besseren Kandidaten? Muessen wir den Wechselzaehler aktivieren?
139 # Zaehle hoch bis der switch_candidate_timestamp alt genug ist
140 switch_candidate_timestamp=$(
get_service_value "$current_gateway" "switch_candidate_timestamp")
141 if [ -z
"$switch_candidate_timestamp" ]; then
142 # wir bleiben beim aktuellen Gateway - wir merken uns allerdings den Switch-Zeitstempel
143 set_service_value
"$current_gateway" "switch_candidate_timestamp" "$now"
144 msg_debug "Starting to count down until the switching timer reaches $bettergateway_timeout minutes"
147 # noch nicht alt genug fuer den Wechsel?
148 is_timestamp_older_minutes
"$switch_candidate_timestamp" "$bettergateway_timeout" \
149 || {
msg_debug "Counting down further until we reach $bettergateway_timeout minutes";
return 0; }
152 # eventuell kann hier auch ein leerer String uebergeben werden - dann wird kein Gateway aktiviert (korrekt)
153 [ -n
"$best_gateway" ] \
154 &&
msg_debug "Switching gateway from $current_gateway to $best_gateway" \
155 ||
msg_debug "Disabling $current_gateway without a viable alternative"
160 ## @fn get_active_mig_connections()
161 ## @brief Liefere die aktiven VPN-Verbindungen (mit Mesh-Internet-Gateways) zurück.
162 ## @returns Liste der Namen aller Dienste, die aktuell eine aktive VPN-Verbindung halten.
163 ## @attention Diese Funktion braucht recht viel Zeit.
165 trap
"error_trap get_active_mig_connections '$*'" $GUARD_TRAPS
168 [
"$(get_openvpn_service_state "$service_name
")" =
"active" ] && echo
"$service_name" ||
true
173 ## @fn get_starting_mig_connections()
174 ## @brief Liefere die im Aufbau befindlichen VPN-Verbindungen (mit Mesh-Internet-Gateways) zurück.
175 ## @returns Liste der Namen aller Dienste, die aktuell beim Verbindungsaufbau sind.
176 ## @attention Diese Funktion braucht recht viel Zeit.
178 trap
"error_trap get_starting_mig_connections '$*'" $GUARD_TRAPS
181 [
"$(get_openvpn_service_state "$service_name
")" =
"connecting" ] && echo
"$service_name" ||
true
186 ## @fn reset_mig_connection_test_timestamp()
187 ## @brief Löse eine erneute Prüfung dieses Gateways beim nächsten Prüflauf aus.
188 ## @param service_name Name eines Diensts
189 ## @details Das Löschen des *status_timestamp* Werts führt zu einer
190 ## erneuten Prüfung zum nächstmöglichen Zeitpunkt.
192 local service_name=
"$1"
193 set_service_value
"$service_name" "status_timestamp" ""
197 ## @fn reset_all_mig_connection_test_timestamps()
198 ## @brief Löse eine erneute Prüfung aller Gateways zum nächstmöglichen Zeitpunkt aus.
199 ## @sa reset_mig_connection_test_timestamp
208 ## @fn get_mig_connection_test_age()
209 ## @brief Ermittle das Test des letzten Verbindungstests in Minuten.
210 ## @returns Das Alter des letzten Verbindungstests in Minuten oder nichts (falls noch kein Test durchgeführt wurde).
211 ## @details Anhand des Test-Alters lässt sich der Zeitpunkt der nächsten Prüfung abschätzen.
213 local service_name=
"$1"
216 # noch keine Tests durchgefuehrt?
217 [ -z
"$timestamp" ] &&
return 0
219 now=$(get_uptime_minutes)
220 echo
"$timestamp" "$now" | awk
'{ print $2 - $1 }'
224 ## @fn get_client_cn()
225 ## @brief Ermittle den Common-Name des Nutzer-Zertifikats.
226 ## @details Liefere eine leere Zeichenkette zurück, falls kein Zertifikat vorhanden ist.
228 [ -e
"$MIG_OPENVPN_DIR/on_aps.crt" ] ||
return 0
229 openssl x509 -in
"$MIG_OPENVPN_DIR/on_aps.crt" \
230 -subject -nameopt multiline -noout 2>/dev/
null | awk
'/commonName/ {print $3}'
234 ## @fn get_mig_port_forward_range()
235 ## @brief Liefere den ersten und letzten Port der Nutzertunnel-Portweiterleitung zurück.
236 ## @param client_cn [optional] common name des Nutzer-Zertifikats
237 ## @returns zwei Zahlen durch Tabulatoren getrennt / keine Ausgabe, falls keine Main-ID gefunden wurde
238 ## @details Jeder AP bekommt einen Bereich von zehn Ports fuer die Port-Weiterleitung zugeteilt.
240 trap
"error_trap get_mig_port_forward_range '$*'" $GUARD_TRAPS
241 local client_cn=${1:-}
249 [ -z
"$client_cn" ] &&
msg_debug "get_mig_port_forward_range: failed to get Common Name - maybe there is no certificate?" &&
return 0
251 if echo
"$client_cn" | grep -q
'^\(\(1\.\)\?[0-9][0-9]\?[0-9]\?\.aps\.on\)$'; then
253 cn_address=${client_cn%.aps.on}
254 cn_address=${cn_address#*.}
255 elif echo
"$client_cn" | grep -q
'^\([0-9][0-9]\?[0-9]\?\.mobile\.on\)$'; then
257 cn_address=${client_cn%.mobile.on}
258 elif echo
"$client_cn" | grep -q
'^\(2[\._-][0-9][0-9]\?[0-9]\?\.aps\.on\)$'; then
260 cn_address=${client_cn%.aps.on}
261 cn_address=${cn_address#*.}
262 elif echo
"$client_cn" | grep -q
'^\(3[\._-][0-9][0-9]\?[0-9]\?\.aps\.on\)$'; then
264 cn_address=${client_cn%.aps.on}
265 cn_address=${cn_address#*.}
268 if [ -z
"$cn_address" ] || [
"$cn_address" -lt 1 ] || [
"$cn_address" -gt 255 ]; then
269 msg_info "$(basename "$0
"): invalidate certificate Common Name ($client_cn)"
271 first_port=$((portbase + (cn_address-1) * port_count))
272 last_port=$((first_port + port_count - 1))
273 echo -e
"$first_port\t$last_port"
279 ## @brief Je nach Status des Moduls: prüfe die VPN-Verbindungen bis mindestens eine Verbindung
280 ## aufgebaut wurde bzw. trenne alle Verbindungen.
281 ## @details Diese Funktion sollte regelmäßig als cronjob ausgeführt werden.
284 # die Gateway-Tests sind nur moeglich, falls ein Test-Schluessel vorhanden ist
295 ## @fn disable_on_openvpn()
296 ## @brief Trenne alle laufenden oder im Aufbau befindlichen Verbindungen.
299 # möglicherweise vorhandene Verbindungen trennen und bei Bedarf openvpn neustarten
303 done | grep -q . && apply_changes openvpn
308 ## @fn get_mig_tunnel_servers()
309 ## @brief Ermittle die Server für den gewünschen Dienst, die via Tunnel erreichbar sind.
310 ## @params stype Dienst-Typ (z.B. "DNS" oder "NTP") - entspricht den DHCP-Options, die vom OpenVPN-Server gepusht werden.
311 ## @details Die Ausgabe ist leer, falls kein Tunnel aufgebaut ist.
313 trap
"error_trap get_mig_tunnel_server '$*'" $GUARD_TRAPS
315 [ -z
"$(get_active_mig_connections)" ] &&
return 0
316 [ -f
"$MIG_PREFERRED_SERVERS_FILE" ] ||
return 0
317 awk <
"$MIG_PREFERRED_SERVERS_FILE" '{ if ($1 == "'"$stype"'") print $2 }'
320 # Ende der Doku-Gruppe
has_mig_openvpn_credentials()
Prüft, ob der Nutzer bereits einen Schlüssel und ein Zertifikat angelegt hat.
get_services(service_type)
Liefere alle Dienste zurueck, die dem angegebenen Typ zugeordnet sind. Falls kein Typ angegben wird...
get_mig_port_forward_range(client_cn)
Liefere den ersten und letzten Port der Nutzertunnel-Portweiterleitung zurück.
get_starting_mig_connections()
Liefere die im Aufbau befindlichen VPN-Verbindungen (mit Mesh-Internet-Gateways) zurück.
disable_on_openvpn()
Trenne alle laufenden oder im Aufbau befindlichen Verbindungen.
find_and_select_best_gateway(force_switch_now)
Ermittle den besten Gateway und prüfe, ob ein Wechsel sinnvoll ist.
filter_reachable_services()
Filtere aus einer Reihe eingehender Dienste diejenigen heraus, die erreichbar sind.
filter_enabled_services()
Filtere aus einer Reihe eingehender Dienste diejenigen heraus, die nicht manuell ausgeblendet wurden...
set eu uci q show dhcp grep dhcp dnsmasq dns uci for fname in etc resolv conf tmp resolv conf auto var etc dnsmasq conf var run dnsmasq servers
_get_file_dict_value(key)
Auslesen eines Werts aus einem Schlüssel/Wert-Eingabestrom.
select_mig_connection(wanted)
Aktiviere den angegebenen VPN-Gateway.
run_cyclic_service_tests(test_function)
Durchlaufe alle via STDIN angegebenen Dienste bis mindestens ein Test erfolgreich ist...
get_mig_tunnel_servers()
Ermittle die Server für den gewünschen Dienst, die via Tunnel erreichbar sind. stype Dienst-Typ (z...
sort_services_by_priority()
Sortiere den eingegebenen Strom von Dienstnamen und gib eine nach der Priorität sortierte Liste...
msg_info(message)
Informationen und Fehlermeldungen ins syslog schreiben.
verify_mig_gateways()
Durchlaufe die Liste aller Internet-Gateway-Dienste und aktualisieren deren Status.
reset_all_mig_connection_test_timestamps()
Löse eine erneute Prüfung aller Gateways zum nächstmöglichen Zeitpunkt aus.
get_client_cn()
Ermittle den Common-Name des Nutzer-Zertifikats.
enable_openvpn_service()
Erzeuge eine funktionierende openvpn-Konfiguration (Datei + UCI, service_name).
reset_mig_connection_test_timestamp(service_name)
Löse eine erneute Prüfung dieses Gateways beim nächsten Prüflauf aus.
msg_debug(message)
Debug-Meldungen ins syslog schreiben.
get_service_value(key, default)
Auslesen eines Werts aus der Service-Datenbank.
get_active_mig_connections()
Liefere die aktiven VPN-Verbindungen (mit Mesh-Internet-Gateways) zurück.
set eu grep root::etc shadow exit if which chpasswd dev null
get_mig_connection_test_age()
Ermittle das Test des letzten Verbindungstests in Minuten.
get_on_openvpn_default()
Liefere einen der default-Werte der aktuellen Firmware zurück (Paket on-openvpn, key).
has_openvpn_credentials_by_template(template_file)
Prüft, ob der Nutzer bereits einen Schlüssel und ein Zertifikat angelegt hat.
disable_openvpn_service(service_name)
Löschung einer openvpn-Verbindung.
is_on_module_installed_and_enabled(module)
Pruefe ob ein Modul sowohl installiert, als auch aktiv ist.
update_mig_connection_status()
Je nach Status des Moduls: prüfe die VPN-Verbindungen bis mindestens eine Verbindung aufgebaut wurde ...