VPN do Mac ligada mas ip-up nunca arrancou: execute uma vigilância launchd
Os túneis Cisco IPSec e IKEv2 nunca iniciam o pppd, por isso /etc/ppp/ip-up fica em silêncio. Vigie a pasta de configuração e faça o script provar o túnel.
Coloca um comando route em /etc/ppp/ip-up, volta a ligar o túnel do escritório, e o ficheiro nunca é executado. Não há nada de errado com o script: essa ligação não passa por PPP de todo. Os clientes integrados Cisco IPSec e IKEv2 nunca iniciam o pppd, pelo que a pasta de hooks nunca é lida, enquanto o PPTP e o L2TP sobre IPSec continuam a passar por ela. Determine qual é o seu caso e depois dispare o caso não-PPP a partir de uma tarefa de vigilância launchd.
Verifique a interface antes de editar qualquer hook
Dois comandos decidem que mecanismo pode funcionar. Liste os serviços configurados e depois liste as interfaces que existem enquanto o túnel está ativo. O nome da interface é a prova; a etiqueta nas Definições do Sistema não é.
scutil --nc list
ifconfig -l | tr ' ' '\n' | grep -E '^(utun|ppp)[0-9]+$'
Um serviço que produz ppp0 executa os hooks PPP e executará o seu ficheiro. Um serviço Cisco IPSec ou IKEv2 produz, em vez disso, um dispositivo utun, uma interface de túnel sem camada PPP, pelo que nada dentro de /etc/ppp o consegue alcançar. Quando aparecem os dois tipos, verifique a que serviço se ligou. As pessoas perdem uma tarde no hook errado por lerem o nome do cliente.
No caminho PPP o ficheiro tem de pertencer ao root e ser executável, porque o pppd verifica ambos antes de executar seja o que for. O install define proprietário e modo num só passo.
sudo install -o root -g wheel -m 0755 /dev/null /etc/ppp/ip-up
sudo ls -l /etc/ppp/ip-up
Um ficheiro criado pelo Finder ou por um editor gráfico acaba muitas vezes na sua conta, ou sem o bit de execução, e depois falha tão silenciosamente como o hook em falta. Coloque já ip-down ao lado. root e wheel na coluna do proprietário mais rwxr-xr-x na coluna do modo é o que quer.
Deixe o launchd disparar o script quando aparecer um túnel
Para clientes que nunca tocam no PPP, o gatilho tem de vir de outro sítio, e o armazém de configuração do sistema é a fonte honesta: o macOS reescreve-o em cada transição de túnel. Uma tarefa launchd que vigia essa pasta é chamada quando um túnel aparece ou desaparece.
<?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>
Instale-o como daemon launch, não como agente por utilizador, porque a reparação de rotas pertence à máquina. O bootstrap substitui a forma antiga load -w nas versões atuais. O RunAtLoad torna a primeira execução observável, e o ThrottleInterval impede que uma rajada de alterações de ligação Wi-Fi lance o script repetidamente.
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
A armadilha é que a pasta vigiada muda por eventos sem nada a ver com a sua VPN: uma ligação Wi-Fi, uma renovação de DHCP, um cabo ligado. O guarda abaixo transforma um gatilho ruidoso num hook utilizável, e uma linha de registo explica uma rota a uma hora estranha.
A propriedade abrange o plist tal como o script. Um daemon que executa um ficheiro numa pasta onde a sua própria conta pode escrever entrega root a essa conta.
Proteja o script para que uma segunda execução não mude nada
O script decide primeiro se o túnel existe e sai em silêncio quando não existe. Tudo o resto tem de ser seguro de repetir, porque o gatilho dispara muito mais vezes do que uma única ligação. Resolva a interface em tempo de execução em vez de codificar um índice que muda na ligação seguinte.
#!/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 sai com estado diferente de zero quando a rota já existe, por isso um script que trata cada estado diferente de zero como falha relata um hook quebrado sempre que faz o correto. O teste com netstat acima transforma esse caso num no-op explícito.
A presença da interface é uma prova mais fraca do que parece, porque outros clientes VPN e alguns ambientes de contentores também criam dispositivos utun. Pergunte ao próprio serviço com scutil --nc status "Office VPN" e exija o estado ligado antes de tocar no encaminhamento. Guarde a resposta numa linha de registo, porque uma semana depois esse registo será a única explicação que terá.
Recupere a gateway sem quebrar o DNS
Quando o túnel assume a rota predefinida, a navegação normal sai pelo escritório e o caminho local para a internet deixa de funcionar. Capture primeiro o router da interface física e depois aponte a predefinida de volta para ele. O DHCP bate a análise de uma tabela que já contém a predefinida do próprio túnel.
PHYS=en0
GW=$(ipconfig getoption "$PHYS" router)
[ -n "$GW" ] && sudo route -n change default "$GW"
Os resolvers são uma camada separada com uma ferramenta separada, e reparar a rota não os repara. Veja o que está em vigor com scutil --dns e depois corrija o que o produz: a ordem dos serviços e as entradas DNS no serviço físico. Limpar a cache diagnostica uma consulta presa após uma mudança de rede; não é algo para agendar.
Um túnel que sobrevive a uma mudança de Wi-Fi enquanto o DNS encrava é uma terceira falha: sudo killall -HUP mDNSResponder faz o resolver reler a sua configuração. Use-o uma vez, à mão.
Prove que a tarefa dispara sozinha
Force uma execução com kickstart -k antes de confiar no caminho de vigilância e depois leia o estado de saída registado pela tarefa. Uma saída limpa prova que o script funciona; não prova que o gatilho funciona. Depois desligue e volte a ligar com a tarefa carregada, e observe uma execução que não pediu.
sudo launchctl kickstart -k system/net.example.tunnel-watch
sudo launchctl print system/net.example.tunnel-watch | grep -E 'last exit code|state ='
Verifique scutil --nc status antes e depois de cada tentativa. Um script que correu com o túnel em baixo não prova nada sobre o caminho de ligação.
O ramo de desmontagem merece o mesmo tratamento que o ramo de ligação: remova a rota à mão, deixe a tarefa correr e confirme que volta. Um hook que adiciona uma rota e nunca a remove deixa uma máquina que se comporta mal uma semana depois, sem nada que ligue os dois acontecimentos. Quando ambos os comportamentos se mantêm, o sudo launchctl bootout system/net.example.tunnel-watch retira a tarefa de forma limpa. Um daemon de vigilância deixado carregado é a forma de uma reparação pontual se tornar uma rota inexplicada meses depois.