Opennet Firmware
on-openvpn.sh
gehe zur Dokumentation dieser Datei
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
4 ## @{
5 
6 MIG_OPENVPN_DIR=/etc/openvpn/opennet_user
7 MIG_OPENVPN_CONFIG_TEMPLATE_FILE=/usr/share/opennet/openvpn-mig.template
8 # shellcheck disable=SC2034
9 DEFAULT_MIG_PORT=1600
10 # Pakete mit dieser TOS-Markierung duerfen nicht in den Tunnel
11 # shellcheck disable=SC2034
12 TOS_NON_TUNNEL=8
13 ## Quelldatei für Standardwerte des Nutzer-VPN-Pakets
14 ON_OPENVPN_DEFAULTS_FILE=/usr/share/opennet/openvpn.defaults
15 MIG_PREFERRED_SERVERS_FILE=/var/run/mig-tunnel-servers.list
16 # shellcheck disable=SC2034
17 ZONE_TUNNEL=on_vpn
18 # shellcheck disable=SC2034
19 NETWORK_TUNNEL=on_vpn
20 TRACEROUTE_FILENAME="traceroute_gw_cache"
21 
22 
23 ## @fn get_on_openvpn_default()
24 ## @brief Liefere einen der default-Werte der aktuellen Firmware zurück (Paket on-openvpn).
25 ## @param key Name des Schlüssels
26 ## @sa get_on_core_default
28  local key="$1"
29  _get_file_dict_value "$key" "$ON_OPENVPN_DEFAULTS_FILE"
30 }
31 
32 
33 ## @fn has_mig_openvpn_credentials()
34 ## @brief Prüft, ob der Nutzer bereits einen Schlüssel und ein Zertifikat angelegt hat.
35 ## @returns Liefert "wahr", falls Schlüssel und Zertifikat vorhanden sind oder
36 ## falls in irgendeiner Form Unklarheit besteht.
38  has_openvpn_credentials_by_template "$MIG_OPENVPN_CONFIG_TEMPLATE_FILE" && return 0
39  trap "" EXIT && return 1
40 }
41 
42 
43 ## @fn verify_mig_gateways()
44 ## @brief Durchlaufe die Liste aller Internet-Gateway-Dienste und aktualisieren deren Status.
45 ## @see run_cyclic_service_tests
47  local max_fail_attempts
48  local test_period_minutes
49  max_fail_attempts=$(get_on_openvpn_default "test_max_fail_attempts")
50  test_period_minutes=$(get_on_openvpn_default "test_period_minutes")
51  get_services "gw" | run_cyclic_service_tests "verify_vpn_connection" "$test_period_minutes" "$max_fail_attempts"
52 }
53 
54 
55 ## @fn select_mig_connection()
56 ## @brief Aktiviere den angegebenen VPN-Gateway
57 ## @param wanted Name eines Diensts
58 ## @attention Seiteneffekt: Beräumung aller herumliegenden Konfigurationen von alten Verbindungen.
60  trap 'error_trap select_mig_connection "$*"' EXIT
61  local wanted="$1"
62  local found_service=0
63  for one_service in $(get_services "gw"); do
64  # loesche Flags fuer die Vorselektion
65  set_service_value "$one_service" "switch_candidate_timestamp" ""
66  # erst nach der Abschaltung der alten Dienste wollen wir den neuen Dienst anschalten
67  [ "$one_service" = "$wanted" ] && found_service=1 && continue
68  # alle unerwuenschten Dienste abschalten
69  disable_openvpn_service "$one_service" || true
70  done
71  [ "$found_service" = "0" ] || enable_openvpn_service "$wanted" "host"
72 }
73 
74 
75 ## @fn find_and_select_best_gateway()
76 ## @brief Ermittle den besten Gateway und prüfe, ob ein Wechsel sinnvoll ist.
77 ## @param force_switch_now [optional] erzwinge den Wechsel auf den besten Gateway unabhängig von Wartezeiten (true/false)
78 ## @ref mig-switch
79 # shellcheck disable=SC2120
81  trap 'error_trap find_and_select_best_gateway "$*"' EXIT
82  local force_switch_now="${1:-false}"
83  local service_name
84  local host
85  local best_gateway=
86  local current_gateway=
87  local current_priority
88  local best_priority
89  local switch_candidate_timestamp
90  local now
91  local bettergateway_timeout
92  now=$(get_uptime_minutes)
93  bettergateway_timeout=$(get_on_openvpn_default vpn_bettergateway_timeout)
94  msg_debug "Trying to find a better gateway"
95  # suche nach dem besten und dem bisher verwendeten Gateway
96  # Ignoriere dabei alle nicht-verwendbaren Gateways.
97  for service_name in $(get_services "gw" \
101  host=$(get_service_value "$service_name" "host")
102  uci_is_false "$(get_service_value "$service_name" "status" "false")" && \
103  msg_debug "$host did not pass the last test" && \
104  continue
105  # dieser Gateway ist ein valider Kandidat
106  [ -z "$best_gateway" ] && best_gateway="$service_name" && continue
107  [ -n "$(get_openvpn_service_state "$service_name")" ] && current_gateway="$service_name" && break
108  done
109  if [ "$current_gateway" = "$best_gateway" ]; then
110  if [ -z "$current_gateway" ]; then
111  msg_debug "There is still no usable gateway around"
112  else
113  # gibt es eine gueltige default-Route?
114  # Auf einem AP mit der v0.5.2 trat einmal eine Situation auf, in der zwei
115  # OpenVPN-Prozesse gleichzeitig gestartet wurden und somit um den
116  # Device-Namensraum (tun0/tun1) konkurrierten.
117  # Am Ende ueberlebte der Prozess mit tun0 - allerdings hatte der tun1-Prozess
118  # zuvor die default-Route ueberschrieben. Dieser Zustand ohne Internetzugang
119  # war als Fehlerzustand nicht zu erkennen.
120  if [ -z "$(get_target_route_interface 1.1.1.1)" ]; then
121  # Durch aussergewoehnliche Umstaende (siehe oben) gibt es keine
122  # default-Route. Um sicherzugehen, dass wir uns nicht gerade im
123  # Verbindungsaufbau befinden, warten wir noch ein paar Sekunden und
124  # starten anschliessend openvpn neu.
125  sleep 20
126  [ -n "$(get_target_route_interface 1.1.1.1)" ] || {
127  # immer noch keine default-Route
128  msg_info "Missing default route detected - restarting openvpn"
129  /etc/init.d/openvpn restart || true
130  return
131  }
132  fi
133  msg_debug "Current gateway ($current_gateway) is still the best choice"
134  # Wechselzaehler zuruecksetzen (falls er hochgezaehlt wurde)
135  set_service_value "$current_gateway" "switch_candidate_timestamp" ""
136  fi
137  return 0
138  fi
139  msg_debug "Current ($current_gateway) / best ($best_gateway)"
140  # eventuell wollen wir den aktuellen Host beibehalten (sofern er funktioniert und wir nicht zwangsweise wechseln)
141  if [ -n "$current_gateway" ] \
142  && uci_is_false "$force_switch_now" \
143  && uci_is_true "$(get_service_value "$current_gateway" "status" "false")"; then
144  # falls der beste und der aktive Gateway gleich weit entfernt sind, bleiben wir beim bisher aktiven
145  current_priority=$(get_service_priority "$current_gateway")
146  best_priority=$(get_service_priority "$best_gateway")
147  [ "$current_priority" -eq "$best_priority" ] \
148  && msg_debug "Keeping current gateway since the best gateway has the same priority" \
149  && return 0
150  # falls der beste und der aktive Gateway gleich weit entfernt sind, bleiben wir beim bisher aktiven
151  # Haben wir einen besseren Kandidaten? Muessen wir den Wechselzaehler aktivieren?
152  # Zaehle hoch bis der switch_candidate_timestamp alt genug ist
153  switch_candidate_timestamp=$(get_service_value "$current_gateway" "switch_candidate_timestamp")
154  if [ -z "$switch_candidate_timestamp" ]; then
155  # wir bleiben beim aktuellen Gateway - wir merken uns allerdings den Switch-Zeitstempel
156  set_service_value "$current_gateway" "switch_candidate_timestamp" "$now"
157  msg_debug "Starting to count down until the switching timer reaches $bettergateway_timeout minutes"
158  return 0
159  else
160  # noch nicht alt genug fuer den Wechsel?
161  if ! is_timestamp_older_minutes "$switch_candidate_timestamp" "$bettergateway_timeout"; then
162  msg_debug "Counting down further until we reach $bettergateway_timeout minutes"
163  return 0
164  fi
165  fi
166  fi
167  # eventuell kann hier auch ein leerer String uebergeben werden - dann wird kein Gateway aktiviert (korrekt)
168  if [ -n "$best_gateway" ]; then
169  msg_debug "Switching gateway from $current_gateway to $best_gateway"
170  else
171  msg_debug "Disabling $current_gateway without a viable alternative"
172  fi
173  select_mig_connection "$best_gateway"
174 }
175 
176 
177 ## @fn get_active_mig_connections()
178 ## @brief Liefere die aktiven VPN-Verbindungen (mit Mesh-Internet-Gateways) zurück.
179 ## @returns Liste der Namen aller Dienste, die aktuell eine aktive VPN-Verbindung halten.
180 ## @attention Diese Funktion braucht recht viel Zeit.
182  trap 'error_trap get_active_mig_connections "$*"' EXIT
183  local service_name
184  for service_name in $(get_services "gw"); do
185  [ "$(get_openvpn_service_state "$service_name")" != "active" ] || echo "$service_name"
186  done
187 }
188 
189 
190 ## @fn get_starting_mig_connections()
191 ## @brief Liefere die im Aufbau befindlichen VPN-Verbindungen (mit Mesh-Internet-Gateways) zurück.
192 ## @returns Liste der Namen aller Dienste, die aktuell beim Verbindungsaufbau sind.
193 ## @attention Diese Funktion braucht recht viel Zeit.
195  trap 'error_trap get_starting_mig_connections "$*"' EXIT
196  local service_name
197  for service_name in $(get_services "gw"); do
198  [ "$(get_openvpn_service_state "$service_name")" != "connecting" ] || echo "$service_name"
199  done
200 }
201 
202 
203 ## @fn reset_mig_connection_test_timestamp()
204 ## @brief Löse eine erneute Prüfung dieses Gateways beim nächsten Prüflauf aus.
205 ## @param service_name Name eines Diensts
206 ## @details Das Löschen des *status_timestamp* Werts führt zu einer
207 ## erneuten Prüfung zum nächstmöglichen Zeitpunkt.
209  local service_name="$1"
210  set_service_value "$service_name" "status_timestamp" ""
211 }
212 
213 
214 ## @fn reset_all_mig_connection_test_timestamps()
215 ## @brief Löse eine erneute Prüfung aller Gateways zum nächstmöglichen Zeitpunkt aus.
216 ## @sa reset_mig_connection_test_timestamp
218  local service_name
219  for service_name in $(get_services "gw"); do
221  done
222 }
223 
224 
225 ## @fn get_mig_connection_test_age()
226 ## @brief Ermittle das Test des letzten Verbindungstests in Minuten.
227 ## @returns Das Alter des letzten Verbindungstests in Minuten oder nichts (falls noch kein Test durchgeführt wurde).
228 ## @details Anhand des Test-Alters lässt sich der Zeitpunkt der nächsten Prüfung abschätzen.
230  local service_name="$1"
231  local timestamp
232  timestamp=$(get_service_value "$service_name" "status_timestamp")
233  # noch keine Tests durchgefuehrt?
234  [ -z "$timestamp" ] && return 0
235  echo "$timestamp" "$(get_uptime_minutes)" | awk '{ print $2 - $1 }'
236 }
237 
238 
239 ## @fn get_client_cn()
240 ## @brief Ermittle den Common-Name des Nutzer-Zertifikats.
241 ## @details Liefere eine leere Zeichenkette zurück, falls kein Zertifikat vorhanden ist.
242 get_client_cn() {
243  [ -e "$MIG_OPENVPN_DIR/on_aps.crt" ] || return 0
244  get_ssl_certificate_cn "$MIG_OPENVPN_DIR/on_aps.crt"
245 }
246 
247 
248 ## @fn get_mig_port_forward_range()
249 ## @brief Liefere den ersten und letzten Port der Nutzertunnel-Portweiterleitung zurück.
250 ## @param client_cn [optional] common name des Nutzer-Zertifikats
251 ## @returns zwei Zahlen durch Tabulatoren getrennt / keine Ausgabe, falls keine Main-ID gefunden wurde
252 ## @details Jeder AP bekommt einen Bereich von zehn Ports fuer die Port-Weiterleitung zugeteilt.
254  trap 'error_trap get_mig_port_forward_range "$*"' EXIT
255  local client_cn="${1:-}"
256  [ -z "$client_cn" ] && client_cn=$(get_client_cn)
257  local port_count=10
258  local cn_address=
259  local portbase
260  local first_port
261 
262  [ -z "$client_cn" ] && msg_debug "get_mig_port_forward_range: failed to get Common Name - maybe there is no certificate?" && return 0
263 
264  if echo "$client_cn" | grep -q '^\(\(1\.\)\?[0-9][0-9]\?[0-9]\?\.aps\.on\)$'; then
265  portbase=10000
266  cn_address=${client_cn%.aps.on}
267  cn_address=${cn_address#*.}
268  elif echo "$client_cn" | grep -q '^\([0-9][0-9]\?[0-9]\?\.mobile\.on\)$'; then
269  portbase=12550
270  cn_address=${client_cn%.mobile.on}
271  elif echo "$client_cn" | grep -q '^\(2[\._-][0-9][0-9]\?[0-9]\?\.aps\.on\)$'; then
272  portbase=15100
273  cn_address=${client_cn%.aps.on}
274  cn_address=${cn_address#*.}
275  elif echo "$client_cn" | grep -q '^\(3[\._-][0-9][0-9]\?[0-9]\?\.aps\.on\)$'; then
276  portbase=20200
277  cn_address=${client_cn%.aps.on}
278  cn_address=${cn_address#*.}
279  fi
280 
281  if [ -z "$cn_address" ] || [ "$cn_address" -lt 1 ] || [ "$cn_address" -gt 255 ]; then
282  msg_info "$(basename "$0"): invalidate certificate Common Name ($client_cn)"
283  else
284  first_port=$((portbase + (cn_address-1) * port_count))
285  # output first port and last port
286  printf '%s\t%s\n' "$first_port" "$((first_port + port_count - 1))"
287  fi
288 }
289 
290 
292 ## @brief Je nach Status des Moduls: prüfe die VPN-Verbindungen bis mindestens eine Verbindung
293 ## aufgebaut wurde bzw. trenne alle Verbindungen.
294 ## @details Diese Funktion sollte regelmäßig als cronjob ausgeführt werden.
296  if is_on_module_installed_and_enabled "on-openvpn"; then
297  # die Gateway-Tests sind nur moeglich, falls ein Test-Schluessel vorhanden ist
300  # shellcheck disable=SC2119
302  fi
303  else
305  fi
306 }
307 
308 
309 ## @fn disable_on_openvpn()
310 ## @brief Trenne alle laufenden oder im Aufbau befindlichen Verbindungen.
312  local service_name
313  local changed=0
314  # möglicherweise vorhandene Verbindungen trennen und bei Bedarf openvpn neustarten
315  for service_name in $(get_active_mig_connections; get_starting_mig_connections); do
316  disable_openvpn_service "$service_name"
317  changed=1
318  done
319  [ "$changed" = "0" ] || apply_changes "openvpn"
320 }
321 
322 
323 ## @fn get_mig_tunnel_servers()
324 ## @brief Ermittle die Server für den gewünschen Dienst, die via Tunnel erreichbar sind.
325 ## @params stype Dienst-Typ (z.B. "DNS" oder "NTP") - entspricht den DHCP-Options, die vom OpenVPN-Server gepusht werden.
326 ## @details Die Ausgabe ist leer, falls kein Tunnel aufgebaut ist.
328  trap 'error_trap get_mig_tunnel_server "$*"' EXIT
329  local stype="$1"
330  [ -z "$(get_active_mig_connections)" ] && return 0
331  [ -f "$MIG_PREFERRED_SERVERS_FILE" ] || return 0
332  awk <"$MIG_PREFERRED_SERVERS_FILE" '{ if ($1 == "'"$stype"'") print $2 }'
333 }
334 
335 
336 ## @fn get_traceroute_csv()
337 ## @brief Liefere den gecachten Traceroute zum Service zurück
338 ## @param Service Name
339 ## @returns CSV Liste von Hops
341  local service_name="$1"
342  local traceroute
343  local host
344 
345  host=$(get_service_value "$service_name" "host")
346  traceroute=$(get_service_value "$TRACEROUTE_FILENAME" "$host")
347 
348  # noch keine Tests durchgefuehrt?
349  [ -z "$traceroute" ] && return 0
350  echo "$traceroute"
351 }
352 
353 
354 ## @fn update_traceroute_gw_cache()
355 ## @brief Aktualisiere den traceroute zu allen Gateway Servern.
357  trap 'error_trap update_traceroute_gw_cache "$*"' EXIT
358  local host
359  local traceroute
360 
361  for host in $(get_services "gw" | pipe_service_attribute "host" | cut -f 2- | sort | uniq); do
362  # do traceroute and get result as csv back
363  traceroute=$(get_traceroute "$host")
364  # update cache file
365  set_service_value "$TRACEROUTE_FILENAME" "$host" "$traceroute"
366  done
367  # es gab eine Aenderung
368  msg_info "updating traceroute to gateway servers"
369 }
370 
371 
372 # Ende der Doku-Gruppe
373 ## @}
has_mig_openvpn_credentials()
Prüft, ob der Nutzer bereits einen Schlüssel und ein Zertifikat angelegt hat.
Definition: on-openvpn.sh:14
get_services(service_type)
Liefere alle Dienste zurueck, die dem angegebenen Typ zugeordnet sind. Falls kein Typ angegben wird...
Definition: services.sh:68
get_mig_port_forward_range(client_cn)
Liefere den ersten und letzten Port der Nutzertunnel-Portweiterleitung zurück.
Definition: on-openvpn.sh:64
get_starting_mig_connections()
Liefere die im Aufbau befindlichen VPN-Verbindungen (mit Mesh-Internet-Gateways) zurück.
Definition: on-openvpn.sh:39
disable_on_openvpn()
Trenne alle laufenden oder im Aufbau befindlichen Verbindungen.
Definition: on-openvpn.sh:72
find_and_select_best_gateway(force_switch_now)
Ermittle den besten Gateway und prüfe, ob ein Wechsel sinnvoll ist.
Definition: on-openvpn.sh:29
update_traceroute_gw_cache()
Aktualisiere den traceroute zu allen Gateway Servern.
Definition: on-openvpn.sh:85
filter_reachable_services()
Filtere aus einer Reihe eingehender Dienste diejenigen heraus, die erreichbar sind.
Definition: services.sh:44
set_service_value()
Setzen eines oder mehrerer Werte fuer einen Dienst. Je nach Schluesselname wird der Inhalt in die per...
Definition: services.sh:79
filter_enabled_services()
Filtere aus einer Reihe eingehender Dienste diejenigen heraus, die nicht manuell ausgeblendet wurden...
Definition: services.sh:49
pipe_service_attribute(key, default)
Liefere zu einer Reihe von Diensten ein gewähltes Attribut dieser Dienste zurück. ...
Definition: services.sh:63
local key
Definition: core.sh:85
_get_file_dict_value(key)
Auslesen eines Werts aus einem Schlüssel/Wert-Eingabestrom.
Definition: core.sh:85
select_mig_connection(wanted)
Aktiviere den angegebenen VPN-Gateway.
Definition: on-openvpn.sh:23
run_cyclic_service_tests(test_function)
Durchlaufe alle via STDIN angegebenen Dienste bis mindestens ein Test erfolgreich ist...
Definition: services.sh:171
get_mig_tunnel_servers()
Ermittle die Server für den gewünschen Dienst, die via Tunnel erreichbar sind. stype Dienst-Typ (z...
Definition: on-openvpn.sh:77
get_traceroute_csv(Service)
Liefere den gecachten Traceroute zum Service zurück.
Definition: on-openvpn.sh:82
sort_services_by_priority()
Sortiere den eingegebenen Strom von Dienstnamen und gib eine nach der Priorität sortierte Liste...
Definition: services.sh:37
msg_info(message)
Informationen und Fehlermeldungen ins syslog schreiben.
Definition: core.sh:15
verify_mig_gateways()
Durchlaufe die Liste aller Internet-Gateway-Dienste und aktualisieren deren Status.
Definition: on-openvpn.sh:18
get_traceroute(host)
Liefere einen traceroute zu einem Host zurueck.
Definition: routing.sh:67
reset_all_mig_connection_test_timestamps()
Löse eine erneute Prüfung aller Gateways zum nächstmöglichen Zeitpunkt aus.
Definition: on-openvpn.sh:49
get_client_cn()
Ermittle den Common-Name des Nutzer-Zertifikats.
Definition: on-openvpn.sh:58
enable_openvpn_service()
Erzeuge eine funktionierende openvpn-Konfiguration (Datei + UCI, service_name).
Definition: openvpn.sh:9
reset_mig_connection_test_timestamp(service_name)
Löse eine erneute Prüfung dieses Gateways beim nächsten Prüflauf aus.
Definition: on-openvpn.sh:45
msg_debug(message)
Debug-Meldungen ins syslog schreiben.
Definition: core.sh:9
get_service_value(key, default)
Auslesen eines Werts aus der Service-Datenbank.
Definition: services.sh:86
get_active_mig_connections()
Liefere die aktiven VPN-Verbindungen (mit Mesh-Internet-Gateways) zurück.
Definition: on-openvpn.sh:34
set eu uci q show dhcp grep dhcp dnsmasq dns uci true for fname in etc resolv conf tmp resolv conf auto var etc dnsmasq conf var run dnsmasq servers
Definition: dns:14
get_mig_connection_test_age()
Ermittle das Test des letzten Verbindungstests in Minuten.
Definition: on-openvpn.sh:54
get_on_openvpn_default()
Liefere einen der default-Werte der aktuellen Firmware zurück (Paket on-openvpn, key).
Definition: on-openvpn.sh:9
done
Definition: core.sh:85
has_openvpn_credentials_by_template(template_file)
Prüft, ob der Nutzer bereits einen Schlüssel und ein Zertifikat angelegt hat.
Definition: openvpn.sh:54
disable_openvpn_service(service_name)
Löschung einer openvpn-Verbindung.
Definition: openvpn.sh:19
is_on_module_installed_and_enabled(module)
Pruefe ob ein Modul sowohl installiert, als auch aktiv ist.
Definition: modules.sh:9
update_mig_connection_status()
Definition: on-openvpn.sh:69