Ви кладете команду 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 daemon, а не як агент для кожного користувача, бо виправлення маршрутів належить машині. 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 чисто знімає завдання. Залишений завантаженим демон спостереження — це шлях, яким разове виправлення через місяці стає нез’ясованим маршрутом.

Схожі статті