Wstawiasz polecenie route do /etc/ppp/ip-up, ponownie łączysz tunel biurowy, a plik nigdy się nie uruchamia. Ze skryptem wszystko w porządku: to połączenie w ogóle nie przechodzi przez PPP. Wbudowani klienci Cisco IPSec i IKEv2 nigdy nie uruchamiają pppd, więc katalog hooków nigdy nie jest czytany, podczas gdy PPTP i L2TP po IPSec nadal przez niego przechodzą. Ustal, który przypadek u Ciebie zachodzi, a następnie wyzwól ten nie-PPP z zadania nadzoru launchd.

Sprawdź interfejs, zanim zmodyfikujesz jakikolwiek hook

Dwie komendy rozstrzygają, który mechanizm może działać. Wypisz skonfigurowane usługi, a następnie wypisz interfejsy, które istnieją, gdy tunel jest podniesiony. Nazwa interfejsu jest dowodem; etykieta w Ustawieniach Systemowych nie jest.

scutil --nc list
ifconfig -l | tr ' ' '\n' | grep -E '^(utun|ppp)[0-9]+$'

Usługa, która tworzy ppp0, uruchamia hooki PPP i wykona Twój plik. Usługa Cisco IPSec lub IKEv2 tworzy zamiast tego urządzenie utun, interfejs tunelu bez warstwy PPP, więc nic wewnątrz /etc/ppp nie może do niego dotrzeć. Gdy pojawiają się oba rodzaje, sprawdź, z którą usługą się połączyłeś. Ludzie tracą popołudnie na niewłaściwym hooku, czytając nazwę klienta.

Na ścieżce PPP plik musi należeć do roota i być wykonywalny, ponieważ pppd sprawdza jedno i drugie, zanim cokolwiek uruchomi. install ustawia właściciela i tryb w jednym kroku.

sudo install -o root -g wheel -m 0755 /dev/null /etc/ppp/ip-up
sudo ls -l /etc/ppp/ip-up

Plik utworzony przez Finder lub edytor graficzny często należy do Twojego konta albo nie ma bitu wykonania, a potem zawodzi tak cicho jak brakujący hook. Umieść teraz obok niego ip-down. root i wheel w kolumnie właściciela oraz rwxr-xr-x w kolumnie trybu to jest to, czego chcesz.

Pozwól launchd uruchomić skrypt, gdy pojawi się tunel

Dla klientów, które nigdy nie dotykają PPP, wyzwalacz musi przyjść skądinąd, a magazyn konfiguracji systemu jest uczciwym źródłem: macOS przepisuje go przy każdym przejściu tunelu. Zadanie launchd obserwujące ten katalog jest wywoływane, gdy tunel się pojawia lub znika.

<?xml version="1.0" encoding="UTF-8"?>
<plist version="1.0">
<dict>
  <key>WatchPaths</key>
  <array><string>/Library/Preferences/SystemConfiguration</string></array>
  <key>Label</key><string>net.example.tunnel-watch</string>
  <key>ProgramArguments</key>
  <array><string>/bin/sh</string><string>/usr/local/sbin/tunnel-hook</string></array>
  <key>RunAtLoad</key><true/>
  <key>ThrottleInterval</key><integer>20</integer>
</dict>
</plist>

Zainstaluj go jako launch daemon, a nie jako agenta dla użytkownika, ponieważ naprawa tras należy do maszyny. bootstrap zastępuje starszą formę load -w w bieżących wydaniach. RunAtLoad czyni pierwsze uruchomienie obserwowalnym, a ThrottleInterval nie pozwala serii zmian połączeń Wi-Fi wielokrotnie uruchamiać skryptu.

sudo install -o root -g wheel -m 0644 tunnel-watch.plist \
  /Library/LaunchDaemons/net.example.tunnel-watch.plist
sudo launchctl bootstrap system \
  /Library/LaunchDaemons/net.example.tunnel-watch.plist

Pułapka polega na tym, że obserwowany katalog zmienia się także dla zdarzeń bez związku z Twoim VPN: połączenia Wi-Fi, odnowienia DHCP, włożonego kabla. Poniższy strażnik zmienia hałaśliwy wyzwalacz w użyteczny hook, a linia logu wyjaśnia trasę o dziwnej godzinie.

Własność obejmuje plist tak samo jak skrypt. Demon, który wykonuje plik w katalogu, do którego Twoje konto może pisać, oddaje temu kontu roota.

Zabezpiecz skrypt, aby drugie uruchomienie niczego nie zmieniło

Skrypt najpierw decyduje, czy tunel istnieje, i po cichu wychodzi, gdy nie istnieje. Wszystko po tym musi być bezpieczne do powtórzenia, bo wyzwalacz odpala znacznie częściej niż jedno połączenie. Rozwiąż interfejs w czasie działania, zamiast zakodować na stałe indeks, który zmieni się przy następnym połączeniu.

#!/bin/sh
set -u
TUN=$(ifconfig -l | tr ' ' '\n' | grep -m1 '^utun[0-9]' || true)
[ -n "$TUN" ] || exit 0
netstat -rn -f inet | grep -q '^10\.42' && exit 0
route -n add -net 10.42.0.0/16 -interface "$TUN"

route add kończy się niezerowym statusem, gdy trasa już istnieje, więc skrypt, który każdy niezerowy status traktuje jako błąd, za każdym razem, gdy robi właściwą rzecz, zgłasza zepsuty hook. Powyższy test z netstat zamienia ten przypadek w jawny no-op.

Obecność interfejsu jest słabszym dowodem, niż wygląda, bo inne klienty VPN i niektóre środowiska kontenerowe także tworzą urządzenia utun. Zapytaj samą usługę przez scutil --nc status "Office VPN" i wymagaj stanu połączenia, zanim dotkniesz routingu. Zachowaj odpowiedź w linii logu, bo tydzień później ten zapis będzie jedynym wyjaśnieniem, jakie będziesz mieć.

Przywróć bramę bez psucia DNS

Gdy tunel przejmie trasę domyślną, zwykłe przeglądanie wychodzi przez biuro, a lokalna ścieżka do internetu przestaje działać. Najpierw przechwyć router interfejsu fizycznego, a potem skieruj trasę domyślną z powrotem na niego. DHCP bije parsowanie tabeli, która już zawiera własną domyślną trasę tunelu.

PHYS=en0
GW=$(ipconfig getoption "$PHYS" router)
[ -n "$GW" ] && sudo route -n change default "$GW"

Resolverery to osobna warstwa z osobnym narzędziem, a naprawa trasy ich nie naprawia. Zobacz przez scutil --dns, co obowiązuje, a potem napraw to, co to tworzy: kolejność usług i wpisy DNS w usłudze fizycznej. Wyczyszczenie pamięci podręcznej diagnozuje zawieszone wyszukiwanie po zmianie sieci; to nie jest coś do planowania.

Tunel, który przetrwa zmianę Wi-Fi, gdy DNS się zacina, to trzecia usterka: sudo killall -HUP mDNSResponder każe resolverowi ponownie odczytać konfigurację. Użyj go raz, ręcznie.

Udowodnij, że zadanie odpala samo

Wymuś jedno uruchomienie przez kickstart -k, zanim zaufasz ścieżce nadzoru, a potem odczytaj status wyjścia zapisany przez zadanie. Czyste wyjście dowodzi, że skrypt działa; nie dowodzi, że działa wyzwalacz. Potem rozłącz się i połącz ponownie z załadowanym zadaniem, i wypatruj uruchomienia, o które nie prosiłeś.

sudo launchctl kickstart -k system/net.example.tunnel-watch
sudo launchctl print system/net.example.tunnel-watch | grep -E 'last exit code|state ='

Sprawdź scutil --nc status przed i po każdej próbie. Skrypt, który działał, gdy tunel był wyłączony, niczego nie dowodzi o ścieżce połączenia.

Gałąź demontażu zasługuje na takie samo traktowanie jak gałąź połączenia: usuń trasę ręcznie, pozwól zadaniu działać i potwierdź, że wraca. Hook, który dodaje trasę i nigdy jej nie usuwa, zostawia maszynę, która tydzień później zachowuje się źle, bez niczego, co łączyłoby dwa zdarzenia. Gdy oba zachowania się utrzymają, sudo launchctl bootout system/net.example.tunnel-watch czysto wycofuje zadanie. Pozostawiony załadowany demon nadzoru to sposób, w jaki jednorazowa naprawa staje się niewyjaśnioną trasą miesiące później.

Powiązane artykuły