Anda menaruh perintah route di /etc/ppp/ip-up, menyambung ulang tunnel kantor, dan berkas itu tidak pernah berjalan. Tidak ada yang salah dengan skripnya: koneksi itu sama sekali tidak melewati PPP. Klien bawaan Cisco IPSec dan IKEv2 tidak pernah memulai pppd, jadi direktori hook tidak pernah dibaca, sementara PPTP dan L2TP di atas IPSec masih melewatinya. Tentukan kasus mana yang Anda hadapi, lalu picu kasus non-PPP dari pekerjaan pengawasan launchd.

Periksa antarmuka sebelum Anda menyunting hook apa pun

Dua perintah menentukan mekanisme mana yang bisa bekerja. Daftar layanan yang dikonfigurasi, lalu daftar antarmuka yang ada selama tunnel naik. Nama antarmuka adalah buktinya; label di Pengaturan Sistem bukan.

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

Layanan yang menghasilkan ppp0 menjalankan hook PPP dan akan mengeksekusi berkas Anda. Layanan Cisco IPSec atau IKEv2 justru menghasilkan perangkat utun, antarmuka tunnel tanpa lapisan PPP, jadi tidak ada apa pun di dalam /etc/ppp yang bisa mencapainya. Ketika kedua jenis muncul, periksa layanan mana yang Anda sambungkan. Orang menghabiskan satu sore pada hook yang salah karena membaca nama klien.

Pada jalur PPP berkas itu harus dimiliki root dan dapat dieksekusi, karena pppd memeriksa keduanya sebelum menjalankan apa pun. install menetapkan pemilik dan mode dalam satu langkah.

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

Berkas yang dibuat lewat Finder atau editor grafis sering berakhir milik akun Anda, atau tanpa bit eksekusi, lalu gagal sesunyi hook yang hilang. Letakkan ip-down di sebelahnya sekarang. root dan wheel di kolom pemilik plus rwxr-xr-x di kolom mode adalah yang Anda inginkan.

Biarkan launchd memicu skrip saat tunnel muncul

Untuk klien yang tidak pernah menyentuh PPP, pemicunya harus datang dari tempat lain, dan penyimpanan konfigurasi sistem adalah sumber yang jujur: macOS menulisnya ulang pada setiap transisi tunnel. Pekerjaan launchd yang mengawasi direktori itu dipanggil saat tunnel muncul atau hilang.

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

Pasang sebagai launch daemon, bukan agen per pengguna, karena perbaikan rute milik mesin. bootstrap menggantikan bentuk lama load -w pada rilis saat ini. RunAtLoad membuat eksekusi pertama teramati, dan ThrottleInterval mencegah gelombang perubahan sambungan Wi-Fi meluncurkan skrip berulang kali.

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

Jebakannya adalah direktori yang diawasi berubah karena peristiwa yang tidak ada hubungannya dengan VPN Anda: sambungan Wi-Fi, pembaruan DHCP, kabel yang dicolokkan. Penjaga di bawah mengubah pemicu berisik menjadi hook yang berguna, dan satu baris log menjelaskan rute pada jam yang aneh.

Kepemilikan mencakup plist maupun skrip. Daemon yang mengeksekusi berkas di direktori yang dapat ditulis akun Anda sendiri menyerahkan root kepada akun itu.

Lindungi skrip agar eksekusi kedua tidak mengubah apa pun

Skrip lebih dulu memutuskan apakah tunnel ada, dan keluar dengan tenang bila tidak. Semua setelah itu harus aman diulang, karena pemicunya menyala jauh lebih sering daripada satu koneksi. Selesaikan antarmuka saat runtime daripada mengodekan indeks yang berubah pada koneksi berikutnya.

#!/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 keluar dengan status bukan nol ketika rute sudah ada, jadi skrip yang menganggap setiap status bukan nol sebagai kegagalan melaporkan hook rusak setiap kali ia melakukan hal yang benar. Uji netstat di atas mengubah kasus itu menjadi no-op eksplisit.

Kehadiran antarmuka adalah bukti yang lebih lemah daripada kelihatannya, karena klien VPN lain dan sebagian lingkungan kontainer juga membuat perangkat utun. Tanyakan kepada layanannya sendiri dengan scutil --nc status "Office VPN" dan tuntut status tersambung sebelum menyentuh perutean. Simpan jawabannya di baris log, karena seminggu kemudian catatan itu adalah satu-satunya penjelasan yang Anda punya.

Bawa gateway kembali tanpa merusak DNS

Ketika tunnel mengambil alih rute default, penjelajahan biasa keluar lewat kantor dan jalur internet lokal berhenti bekerja. Tangkap dulu router antarmuka fisik, lalu arahkan default kembali kepadanya. DHCP mengalahkan penguraian tabel yang sudah berisi default milik tunnel itu sendiri.

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

Resolover adalah lapisan terpisah dengan alat terpisah, dan memperbaiki rute tidak memperbaikinya. Lihat apa yang berlaku dengan scutil --dns, lalu perbaiki apa yang menghasilkannya: urutan layanan dan entri DNS pada layanan fisik. Mengosongkan cache mendiagnosis pencarian yang macet setelah pergantian jaringan; itu bukan sesuatu untuk dijadwalkan.

Tunnel yang bertahan melewati perubahan Wi-Fi sementara DNS tersendat adalah kesalahan ketiga: sudo killall -HUP mDNSResponder membuat resolover membaca ulang konfigurasinya. Gunakan sekali saja, dengan tangan.

Buktikan bahwa pekerjaan menyala sendiri

Paksa satu eksekusi dengan kickstart -k sebelum Anda memercayai jalur pengawasan, lalu baca status keluar yang dicatat pekerjaan. Keluar bersih membuktikan skrip bekerja; itu tidak membuktikan pemicunya bekerja. Lalu putuskan dan sambungkan lagi dengan pekerjaan termuat, dan awasi eksekusi yang tidak Anda minta.

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

Periksa scutil --nc status sebelum dan sesudah setiap percobaan. Skrip yang berjalan saat tunnel mati tidak membuktikan apa pun tentang jalur koneksi.

Cabang pembongkaran layak mendapat perlakuan yang sama seperti cabang koneksi: hapus rute dengan tangan, biarkan pekerjaan berjalan, dan pastikan rute itu kembali. Hook yang menambahkan rute dan tidak pernah menghapusnya meninggalkan mesin yang berperilaku buruk seminggu kemudian, tanpa ada yang menghubungkan kedua peristiwa. Setelah kedua perilaku bertahan, sudo launchctl bootout system/net.example.tunnel-watch memensiunkan pekerjaan itu dengan bersih. Daemon pengawasan yang dibiarkan termuat adalah cara perbaikan sekali jalan menjadi rute yang tak terjelaskan berbulan-bulan kemudian.

Artikel terkait