VPN del Mac connessa ma ip-up mai eseguito: attiva una sorveglianza launchd
I tunnel Cisco IPSec e IKEv2 non avviano mai pppd, quindi /etc/ppp/ip-up resta muto. Sorveglia la cartella di configurazione e fai dimostrare il tunnel allo script.
Metti un comando route in /etc/ppp/ip-up, riconnetti il tunnel dell’ufficio e il file non viene mai eseguito. Con lo script non c’è nulla di sbagliato: quella connessione non passa affatto per PPP. I client integrati Cisco IPSec e IKEv2 non avviano mai pppd, quindi la cartella degli hook non viene mai letta, mentre PPTP e L2TP su IPSec ci passano ancora. Stabilisci quale caso hai e poi attiva il caso non-PPP da un job di sorveglianza launchd.
Controlla l’interfaccia prima di modificare qualsiasi hook
Due comandi decidono quale meccanismo può funzionare. Elenca i servizi configurati, poi elenca le interfacce che esistono mentre il tunnel è attivo. Il nome dell’interfaccia è la prova; l’etichetta in Impostazioni di Sistema non lo è.
scutil --nc list
ifconfig -l | tr ' ' '\n' | grep -E '^(utun|ppp)[0-9]+$'
Un servizio che produce ppp0 esegue gli hook PPP e eseguirà il tuo file. Un servizio Cisco IPSec o IKEv2 produce invece un dispositivo utun, un’interfaccia di tunnel senza livello PPP, quindi nulla dentro /etc/ppp può raggiungerlo. Quando compaiono entrambi i tipi, controlla a quale servizio ti sei connesso. Le persone perdono un pomeriggio sull’hook sbagliato leggendo il nome del client.
Sul percorso PPP il file deve appartenere a root ed essere eseguibile, perché pppd controlla entrambe le cose prima di eseguire qualsiasi cosa. install imposta proprietario e modo in un solo passaggio.
sudo install -o root -g wheel -m 0755 /dev/null /etc/ppp/ip-up
sudo ls -l /etc/ppp/ip-up
Un file creato tramite Finder o un editor grafico spesso finisce di proprietà del tuo account, o senza il bit di esecuzione, e poi fallisce in silenzio come l’hook mancante. Metti ip-down accanto adesso. root e wheel nella colonna del proprietario più rwxr-xr-x nella colonna del modo è quello che vuoi.
Lascia che launchd avvii lo script quando compare un tunnel
Per i client che non toccano mai PPP il trigger deve venire da altrove, e l’archivio di configurazione del sistema è la fonte onesta: macOS lo riscrive a ogni transizione del tunnel. Un job launchd che sorveglia quella cartella viene chiamato quando un tunnel compare o scompare.
<?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>
Installalo come launch daemon, non come agente per utente, perché la riparazione delle rotte appartiene alla macchina. bootstrap sostituisce la forma più vecchia load -w nelle versioni attuali. RunAtLoad rende osservabile la prima esecuzione, e ThrottleInterval impedisce a una raffica di cambi di connessione Wi-Fi di avviare lo script ripetutamente.
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
La trappola è che la cartella sorvegliata cambia anche per eventi senza alcun rapporto con la tua VPN: una connessione Wi-Fi, un rinnovo DHCP, un cavo inserito. La guardia qui sotto trasforma un trigger rumoroso in un hook utilizzabile, e una riga di log spiega una rotta a un’ora strana.
La proprietà copre il plist oltre allo script. Un demone che esegue un file in una cartella in cui il tuo account può scrivere consegna root a quell’account.
Proteggi lo script perché una seconda esecuzione non cambi nulla
Lo script decide prima se il tunnel esiste ed esce in silenzio quando non c’è. Tutto quello che segue deve essere sicuro da ripetere, perché il trigger scatta molto più spesso di una singola connessione. Risolvi l’interfaccia a runtime invece di codificare un indice che cambia alla connessione successiva.
#!/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 esce con stato diverso da zero quando la rotta è già presente, quindi uno script che tratta ogni stato diverso da zero come errore segnala un hook rotto ogni volta che fa la cosa giusta. Il test con netstat qui sopra trasforma quel caso in un no-op esplicito.
La presenza dell’interfaccia è una prova più debole di quanto sembri, perché altri client VPN e alcuni ambienti container creano anch’essi dispositivi utun. Chiedi al servizio stesso con scutil --nc status "Office VPN" ed esigi lo stato connesso prima di toccare il routing. Tieni la risposta in una riga di log, perché una settimana dopo quel record sarà l’unica spiegazione che avrai.
Recupera il gateway senza rompere il DNS
Quando il tunnel prende la rotta predefinita, la navigazione normale esce dall’ufficio e il percorso internet locale smette di funzionare. Cattura prima il router dell’interfaccia fisica, poi riporta la predefinita su di esso. DHCP batte l’analisi di una tabella che contiene già la predefinita del tunnel.
PHYS=en0
GW=$(ipconfig getoption "$PHYS" router)
[ -n "$GW" ] && sudo route -n change default "$GW"
I resolver sono uno strato separato con uno strumento separato, e riparare la rotta non li ripara. Guarda cosa è in vigore con scutil --dns, poi correggi ciò che lo produce: l’ordine dei servizi e le voci DNS del servizio fisico. Svuotare la cache diagnostica una risoluzione bloccata dopo un cambio di rete; non è qualcosa da pianificare.
Un tunnel che sopravvive a un cambio Wi-Fi mentre il DNS si blocca è un terzo guasto: sudo killall -HUP mDNSResponder fa rileggere al resolver la sua configurazione. Usalo una volta, a mano.
Dimostra che il job scatta da solo
Forza un’esecuzione con kickstart -k prima di fidarti del percorso di sorveglianza, poi leggi lo stato di uscita registrato dal job. Un’uscita pulita dimostra che lo script funziona; non dimostra che il trigger funzioni. Poi disconnetti e riconnetti con il job caricato, e osserva un’esecuzione che non hai chiesto.
sudo launchctl kickstart -k system/net.example.tunnel-watch
sudo launchctl print system/net.example.tunnel-watch | grep -E 'last exit code|state ='
Controlla scutil --nc status prima e dopo ogni tentativo. Uno script eseguito mentre il tunnel era abbassato non dimostra nulla sul percorso di connessione.
Il ramo di smontaggio merita lo stesso trattamento del ramo di connessione: rimuovi la rotta a mano, lascia eseguire il job e conferma che torni. Un hook che aggiunge una rotta e non la rimuove mai lascia una macchina che si comporta male una settimana dopo, senza nulla che colleghi i due eventi. Quando entrambi i comportamenti reggono, sudo launchctl bootout system/net.example.tunnel-watch ritira il job in modo pulito. Un demone di sorveglianza lasciato caricato è il modo in cui una riparazione una tantum diventa una rotta inspiegata mesi dopo.