これを決めるのは三つの問いだけで、しかも三つしかない。クライアントは再起動後も残る接続エントリをOS上に作るか。引数リスト以外から秘密を受け取れるコマンドラインを持つか。その身元要件は人間にブラウザのプロンプトへ答えさせないか。この順に答えれば、選択は好みの問題ではなくなる。定期実行のジョブは、三つの答えのうち最も弱いものが許す範囲でしかヘッドレスになれないからだ。

クライアントを三つのスクリプト化可能な層に当てはめる

OSネイティブのプロファイル、ベンダーのCLI、GUIだけのクライアント、ブラウザSSOのクライアントには四つの異なる進め方が要る。最初の二つは無人再起動に耐え、無人再起動こそが本題だ。残り二つはスクリプトより先に設計判断を要する。

What the client exposesWhat to scriptWhat still bites
OS-native connection entryrasdial, scutil --nc, nmcliTunnel is up before DNS and routes settle
Vendor CLI reading stdinCLI wrapped by the scheduler, secret from the credential storePrompt text changes between releases
GUI onlyPinned version plus a state assertionLocale and DPI break the click targets

経路一:プロファイルをOSがすでに所有している

これが最も安い経路で、ほとんどの人が見落とすものだ。Windowsでは rasdial "Office" が接続し、rasdial "Office" /disconnect が切断する。rasphone -d "Office" は同じエントリをダイヤルアップ画面から操作する。macOSは scutil --nc list で保存済みサービスを一覧し、scutil --nc start で一つを開始する。Linuxはこの仕事をNetworkManagerに渡す。

rasdial "Office" user *
scutil --nc start "Office"
nmcli connection up id Office
nmcli -t -f GENERAL.STATE connection show Office

これらのインターフェースはOSが所有するプロファイルしか知らないため、任意の第三者クライアントは動かせない。プロファイルが無い場合は一度だけ作り、毎回作り直すのではなくスクリプトに存在確認をさせる。スクリプトは冪等にしよう。現在の状態を読み、切断時だけ接続し、その後にジョブが本当に必要とするプライベート名を検証する。

経路二:ベンダーがコマンドラインを提供している

コマンドラインを十分に公開しているクライアントは包み込める。Cisco AnyConnect には長年接続専用のヘルパーがあり、OpenConnect は一つのバイナリで AnyConnect、GlobalProtect、Pulse の各プロトコルを話す。 重要な要件はフラグの一覧ではなく、秘密がどこから来るかだ。引数リストのパスワードは、プロセスを一覧できる者なら誰でも読める。

printf '%s\n' "$VPN_PASS" | openconnect --protocol=anyconnect \
  --user "$VPN_USER" --passwd-on-stdin vpn.example.com

FortiClient と一部の GlobalProtect ビルドにはサポートされたCLIがまったく無く、コミュニティクライアントやベンダーライセンスの代替品は調達の判断であって回避策ではない。CIではサービスアカウントにシェルではなくそのバイナリそのものへの権限を与え、注入された引数がrootコマンドにならないようにする。

経路三:GUI自動化と、それが最後である理由

公式クライアントにインターフェースが無い場合、スクリプトはウィンドウメッセージやSendKeysでボタンを押す。これはバージョンと言語に脆く、中途半端に接続されたセッションを残し、ダイアログが誤った言語で出ると黙って失敗する。この道を選ぶならクライアントのバージョンを固定し、後で接続状態を検証し、手作業の代替手順をスクリプトの隣に文書化しておく。

プロセスを殺して強制切断する場合も同じ注意が要る。トンネルはプロセスより長く生きることも、監視役が再起動することもあるので、殺して再起動は確実なリセットではない。

トンネルが上がった後に壊れるもの

接続の成功が証明するのは認証だけで、それ以上ではない。ジョブが存在する理由の資源を、独自のプロトコルと短いタイムアウトで試し、失敗メッセージに段階名を入れる。デスクトップで見えるプロファイル、証明書、保存済み資格情報はサービスセッションからは見えないことがあるので、信頼する前にスケジューラ自身のアカウントで一度実行する。

再試行は制限する。拒否された資格情報を繰り返すループはアカウントをロックし、MFAのプロンプトは登録設計なしにヘッドレスジョブでは答えられない。ブラウザSSOの流れに対するスクリプト再試行は試行を消費するだけだ。多数の端末にはMDM配布のVPNペイロードや構成プロファイルを使い、資格情報のライフサイクルをプラットフォームに持たせる。そう管理できないクライアントにだけスクリプトを残す。

終了コードと最初に失敗した資源を記録する。スクリプトが動いた事実ではない。その一行が、スケジューラの警告を五分の診断に変える。

関連記事