Bạn đặt một lệnh route trong /etc/ppp/ip-up, kết nối lại đường hầm văn phòng, và tệp không bao giờ chạy. Script không có gì sai: kết nối đó hoàn toàn không đi qua PPP. Các ứng dụng khách tích hợp Cisco IPSec và IKEv2 không bao giờ khởi động pppd, nên thư mục hook không bao giờ được đọc, trong khi PPTP và L2TP trên IPSec vẫn đi qua đó. Xác định trường hợp của bạn, rồi kích hoạt trường hợp không PPP từ một tác vụ theo dõi launchd.

Kiểm tra giao diện trước khi sửa bất kỳ hook nào

Hai lệnh quyết định cơ chế nào có thể hoạt động. Liệt kê các dịch vụ đã cấu hình, rồi liệt kê các giao diện tồn tại khi đường hầm hoạt động. Tên giao diện là bằng chứng; nhãn trong Cài đặt Hệ thống thì không.

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

Một dịch vụ tạo ra ppp0 sẽ chạy các hook PPP và sẽ thực thi tệp của bạn. Dịch vụ Cisco IPSec hoặc IKEv2 thay vào đó tạo ra thiết bị utun, một giao diện đường hầm không có lớp PPP, nên không gì bên trong /etc/ppp có thể chạm tới nó. Khi xuất hiện cả hai loại, hãy kiểm tra bạn đã kết nối dịch vụ nào. Người ta mất cả buổi chiều với hook sai vì đọc tên ứng dụng khách.

Trên đường PPP, tệp phải thuộc sở hữu của root và có thể thực thi, vì pppd kiểm tra cả hai trước khi chạy bất cứ thứ gì. install đặt chủ sở hữu và chế độ trong một bước.

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

Tệp được tạo qua Finder hoặc trình soạn thảo đồ họa thường thuộc tài khoản của bạn, hoặc thiếu bit thực thi, rồi thất bại lặng lẽ như hook bị thiếu. Đặt ip-down bên cạnh nó ngay. root và wheel ở cột chủ sở hữu cùng rwxr-xr-x ở cột chế độ là thứ bạn muốn.

Để launchd kích hoạt script khi một đường hầm xuất hiện

Với những ứng dụng khách không bao giờ chạm vào PPP, trình kích hoạt phải đến từ nơi khác, và kho cấu hình hệ thống là nguồn trung thực: macOS ghi lại nó ở mỗi lần chuyển đường hầm. Một tác vụ launchd theo dõi thư mục đó được gọi khi đường hầm xuất hiện hoặc biến mất.

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

Cài nó như một launch daemon, không phải agent cho từng người dùng, vì việc sửa tuyến thuộc về máy. bootstrap thay thế dạng cũ load -w trên các bản phát hành hiện tại. RunAtLoad làm lần chạy đầu tiên quan sát được, và ThrottleInterval ngăn một loạt thay đổi kết nối Wi-Fi khởi chạy script lặp đi lặp lại.

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

Cái bẫy là thư mục được theo dõi thay đổi vì những sự kiện không liên quan đến VPN của bạn: kết nối Wi-Fi, gia hạn DHCP, cắm cáp. Bộ bảo vệ bên dưới biến một trình kích hoạt ồn ào thành hook dùng được, và một dòng nhật ký giải thích một tuyến xuất hiện vào giờ kỳ lạ.

Quyền sở hữu bao gồm cả plist lẫn script. Một daemon thực thi tệp trong thư mục mà tài khoản của bạn có thể ghi sẽ trao root cho tài khoản đó.

Bảo vệ script để lần chạy thứ hai không thay đổi gì

Script trước tiên quyết định đường hầm có tồn tại hay không, và thoát lặng lẽ khi không có. Mọi thứ sau đó phải an toàn để lặp lại, vì trình kích hoạt bắn thường xuyên hơn nhiều so với một kết nối. Hãy phân giải giao diện lúc chạy thay vì mã hóa cứng một chỉ mục sẽ thay đổi ở lần kết nối sau.

#!/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 thoát với trạng thái khác không khi tuyến đã tồn tại, nên một script coi mọi trạng thái khác không là lỗi sẽ báo hook hỏng mỗi lần nó làm đúng. Bài kiểm tra với netstat ở trên biến trường hợp đó thành một no-op rõ ràng.

Sự hiện diện của giao diện là bằng chứng yếu hơn vẻ ngoài của nó, vì các ứng dụng VPN khác và một số môi trường container cũng tạo ra thiết bị utun. Hỏi chính dịch vụ bằng scutil --nc status "Office VPN" và yêu cầu trạng thái đã kết nối trước khi chạm vào định tuyến. Giữ câu trả lời trong một dòng nhật ký, vì một tuần sau bản ghi đó sẽ là lời giải thích duy nhất bạn có.

Lấy lại cổng ra mà không phá DNS

Khi đường hầm chiếm tuyến mặc định, việc duyệt web thông thường đi ra qua văn phòng và đường internet cục bộ ngừng hoạt động. Trước tiên hãy bắt bộ định tuyến của giao diện vật lý, rồi trỏ tuyến mặc định trở lại nó. DHCP tốt hơn việc phân tích một bảng đã chứa tuyến mặc định của chính đường hầm.

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

Các bộ phân giải là một lớp riêng với công cụ riêng, và sửa tuyến không sửa chúng. Xem thứ đang có hiệu lực bằng scutil --dns, rồi sửa thứ tạo ra nó: thứ tự dịch vụ và các mục DNS trên dịch vụ vật lý. Xóa bộ nhớ đệm chẩn đoán một truy vấn bị kẹt sau khi đổi mạng; đó không phải việc để lên lịch.

Một đường hầm sống sót qua thay đổi Wi-Fi trong khi DNS khựng lại là lỗi thứ ba: sudo killall -HUP mDNSResponder khiến bộ phân giải đọc lại cấu hình. Dùng nó một lần, bằng tay.

Chứng minh rằng tác vụ tự kích hoạt

Buộc một lần chạy bằng kickstart -k trước khi tin vào đường theo dõi, rồi đọc trạng thái thoát mà tác vụ đã ghi. Một lần thoát sạch chứng minh script hoạt động; nó không chứng minh trình kích hoạt hoạt động. Sau đó ngắt và kết nối lại khi tác vụ đã tải, và quan sát một lần chạy mà bạn không yêu cầu.

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

Kiểm tra scutil --nc status trước và sau mỗi lần thử. Một script chạy khi đường hầm đang xuống không chứng minh gì về đường kết nối.

Nhánh tháo gỡ xứng đáng được đối xử như nhánh kết nối: gỡ tuyến bằng tay, để tác vụ chạy, và xác nhận nó quay lại. Một hook thêm tuyến mà không bao giờ gỡ nó để lại một máy hoạt động sai một tuần sau, mà không gì liên kết hai sự kiện. Khi cả hai hành vi đứng vững, sudo launchctl bootout system/net.example.tunnel-watch cho tác vụ nghỉ hưu gọn gàng. Một daemon theo dõi bị để tải là cách một lần sửa chữa trở thành một tuyến không giải thích được nhiều tháng sau.

Bài viết liên quan