Слагате команда route в /etc/ppp/ip-up, свързвате отново офисния тунел и файлът така и не се изпълнява. Със скрипта нищо не е наред: тази връзка изобщо не минава през PPP. Вградените клиенти Cisco IPSec и IKEv2 никога не стартират pppd, затова папката с куките не се чете, докато PPTP и L2TP през IPSec все още минават през нея. Определете кой случай е вашият и след това задействайте не-PPP случая от задача за наблюдение на launchd.

Проверете интерфейса, преди да редактирате каквато и да е кука

Две команди решават кой механизъм може да работи. Изброете конфигурираните услуги, след това изброете интерфейсите, които съществуват, докато тунелът е активен. Името на интерфейса е доказателството; етикетът в Системни настройки не е.

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

Услуга, която създава ppp0, изпълнява PPP куките и ще изпълни вашия файл. Услуга Cisco IPSec или IKEv2 създава вместо това устройство utun, тунелен интерфейс без PPP слой, така че нищо в /etc/ppp не може да го достигне. Когато се появят и двата вида, проверете към коя услуга сте се свързали. Хората губят следобед на грешната кука, защото четат името на клиента.

По PPP пътя файлът трябва да е собственост на root и да е изпълним, защото pppd проверява и двете, преди да пусне каквото и да е. install задава собственик и режим в една стъпка.

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

Файл, създаден през Finder или графичен редактор, често се оказва собственост на вашия акаунт или без бит за изпълнение и после се проваля толкова тихо, колкото липсващата кука. Сложете ip-down до него сега. root и wheel в колоната за собственик плюс rwxr-xr-x в колоната за режим е това, което искате.

Оставете launchd да задейства скрипта, когато се появи тунел

За клиенти, които никога не докосват PPP, задействащият сигнал трябва да дойде от друго място, а хранилището на системната конфигурация е честният източник: macOS го пренаписва при всеки преход на тунела. Задача на launchd, която наблюдава тази папка, се извиква, когато тунел се появи или изчезне.

<?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>

Инсталирайте го като launch демон, а не като агент за отделен потребител, защото поправката на маршрути принадлежи на машината. bootstrap заменя по-старата форма load -w в текущите издания. RunAtLoad прави първото изпълнение наблюдаемо, а ThrottleInterval спира серия от промени при свързване с Wi-Fi да стартира скрипта многократно.

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

Клопката е, че наблюдаваната папка се променя и при събития без нищо общо с вашия VPN: свързване с Wi-Fi, подновяване на DHCP, включен кабел. Пазачът по-долу превръща шумния сигнал в използваема кука, а ред в журнала обяснява маршрут в странен час.

Собствеността покрива plist, както и скрипта. Демон, който изпълнява файл в папка, в която вашият акаунт може да пише, дава на този акаунт root.

Защитете скрипта, така че второ изпълнение да не променя нищо

Скриптът първо решава дали тунелът съществува и излиза тихо, когато не. Всичко след това трябва да е безопасно за повторение, защото сигналът се задейства много по-често от една връзка. Откривайте интерфейса по време на изпълнение, вместо да твърдо кодирате индекс, който се променя при следващото свързване.

#!/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 излиза с ненулев статус, когато маршрутът вече съществува, така че скрипт, който приема всеки ненулев статус за грешка, съобщава счупена кука всеки път, когато прави правилното. Тестът с netstat по-горе превръща този случай в явна no-op.

Наличието на интерфейса е по-слабо доказателство, отколкото изглежда, защото други VPN клиенти и някои среди за контейнери също създават устройства utun. Питайте самата услуга с scutil --nc status "Office VPN" и изисквайте свързано състояние, преди да пипате маршрутизацията. Пазете отговора в ред от журнала, защото след седмица този запис ще е единственото обяснение, което имате.

Върнете шлюза, без да счупите DNS

Когато тунелът поеме маршрута по подразбиране, обикновеното сърфиране излиза през офиса и локалният интернет път спира да работи. Първо уловете рутера на физическия интерфейс, после насочете маршрута по подразбиране обратно към него. DHCP е по-добър от разбор на таблица, която вече държи собствения маршрут по подразбиране на тунела.

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

Резолверите са отделен слой с отделен инструмент и поправката на маршрута не ги поправя. Вижте какво е в сила с scutil --dns, после поправете това, което го създава: реда на услугите и DNS записите на физическата услуга. Изчистването на кеша диагностицира заявка, заседнала след смяна на мрежата; това не е нещо за планиране.

Тунел, който оцелява при смяна на Wi-Fi, докато DNS засича, е трета повреда: sudo killall -HUP mDNSResponder кара резолвера да препрочете конфигурацията си. Използвайте го веднъж, на ръка.

Докажете, че задачата се задейства сама

Принудете едно изпълнение с kickstart -k, преди да се доверите на пътя за наблюдение, после прочетете изходния статус, който задачата е записала. Чистият изход доказва, че скриптът работи; той не доказва, че сигналът работи. После прекъснете и се свържете отново със заредена задача и наблюдавайте за изпълнение, което не сте поискали.

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

Проверявайте scutil --nc status преди и след всеки опит. Скрипт, който е работил, докато тунелът е бил спрян, не доказва нищо за пътя на свързване.

Клонът за разглобяване заслужава същото отношение като клона за свързване: премахнете маршрута на ръка, оставете задачата да работи и потвърдете, че се връща. Кука, която добавя маршрут и никога не го премахва, оставя машина, която след седмица се държи зле, без нищо да свързва двете събития. Когато и двете поведения се държат, sudo launchctl bootout system/net.example.tunnel-watch оттегля задачата чисто. Оставен зареден демон за наблюдение е начинът, по който еднократна поправка става необяснен маршрут месеци по-късно.

Подобни статии