スリープフックに再接続コマンドを書けば、問題は解決したように見えます。解決するのは、ネットワークが存在した後にフックが動き、クライアントが認識する ID で、クライアントが実際に実装している経路を通るときだけです。復帰後の失敗のほとんどはこの三つのどれかで、どれもフック自身にはエラーとして現れません。フックはどちらにせよ成功を返します。

ゼロで終わるスクリプトは、トンネルが戻ったかどうかを何も教えてくれません。

フックは動く、それからどうなる?

#!/bin/sh
case "$1" in
  pre)
    vpn-client disconnect
    ;;
  post)
    vpn-client connect
    ;;
esac

各イベントは遷移の前後でフックを二回呼び出し、フェーズとイベント名を渡します。最小限の環境で root として動くため、絶対パスが必要で、デスクトップセッションの変数は期待できません。両方の呼び出しがすぐ返ることは、スクリプトが動いたことしか示しません。その後プロファイルが上がったかは別の観察であり、ログに独自の証拠が必要です。

フェーズ引数を設計の一部として扱ってください。pre フェーズでの再接続は、マシンがすべてを失おうとしている最中に行われ、成功しません。

root、最小環境、間違ったユーザー

クライアントがユーザーごとにログイントークンを保持するなら、フックを root で動かすと提示できるものが何も残りません。フックが実際に使う ID で状態を確認してください。デスクトップ端末から試した ID ではありません。ビルドによっては切断後にデーモンがインターフェースを保持したままになり、再接続が古い状態に落ちます。先にプロセスを片付ければ戻りますが、その代わりにクリーンでない終了になります。正常系だけ試すと、どちらの挙動も見えません。

フックは絶対パスで書き、root 所有で実行可能にし、何をしたかをログに残してください。そのログがなければ次の調査はゼロから始まります。

自動接続は VPN の経路ではない

NetworkManager には二つの有効化機構があります。プロファイルの自動接続フラグと、プライマリプロファイルの有効化で始まるセカンダリ接続です。ネイティブのデバイス種別は前者を実装し、VPN プラグインは歴史的に後者だけを実装してきたため、OpenVPN プロファイルで自動接続に印を付けてもまったく効果がないことがあります。両方のプロパティを確認し、代わりに VPN を Wi-Fi または Ethernet プロファイルのセカンダリとして接続してください。

[Unit]
PartOf=NetworkManager.service
Requires=NetworkManager-wait-online.service
After=NetworkManager-wait-online.service

systemd ユニットで管理する WireGuard インターフェースは、フックではなくそのドロップインが必要で、ネットワークがオンラインになった後に並べます。

ネットワークを待つ、上限付きで

timer=1
while ! ping -c1 -n -w1 203.0.113.1 >/dev/null; do
  sleep 1; timer=$((timer+1))
  [ $timer -ge 300 ] && exit 0
done
nmcli connection up "work-vpn"

復帰時、Wi-Fi がまだ再接続している間にトンネルを要求されることがあり、固定のスリープは当て推量になります。到達可能なアドレスを待ってからプロファイルを上げ、待ち時間に上限を設けて、フックが復帰経路を無期限に開いたままにできないようにします。数値インデックスよりプロファイル名を選び、正しいプロファイルがネットワークごとに変わるなら、健全なトンネルに事前に選択をファイルへ記録させてください。

再起動の嵐にも備え、試行回数を数えて止めてください。失敗した検査のたびに反応するウォッチドッグは、一つの障害をループに変えます。

一度サスペンドし、ジャーナルでフック自身の出力を読んでください。順序を証明する唯一のテストです。

関連記事