ノートPCがすでにそのネットワーク上にあるなら、自宅ネットワークの VPN には何もすることがありません。魅力的な近道は、非ゼロで終了する準備コマンドです。それはユニットを失敗させ、どのダッシュボードも壊れて見えるようにします。systemd にはまさにこの種の判断のためのディレクティブがあり、意図的なスキップと実際の障害を切り分けます。正しく行うことは、主に各終了コードが何を意味するかを書き留めることです。

失敗する前提条件が間違った形である理由

ExecStartPre は非ゼロの終了を起動の失敗として扱います。これはサービスを準備するコマンドとしては正しい動作です。それを条件として使うと、ポリシー上の判断が壊れた依存関係と区別できなくなります。ExecCondition は、ユニットが失敗と記録されずに起動を辞退できるようにするために存在します。

この区別は午前三時に効いてきます。設計どおりに動いていた赤いユニットを誰もトリアージしたくないからです。失敗したユニットは、意図的な失敗の中に本当の障害を隠してしまいます。

まず終了コードの契約を書く

スクリプトより先に契約を書きます。終了コードがゼロなら続行、いちなら残りの起動コマンドをスキップ、255 なら条件そのものが実行できなかったことを意味します。二から 254 まで のコードも通常はスキップを意味し、ヘルパーがランダムなエラーコードを返すと忘れがちです。以下の drop-in はヘルパーを実際のユニット名に紐づけます。

[Service]
ExecCondition=/usr/local/libexec/should-start-home-vpn

既定の意味論を前提にする前に、実効ユニットに継承された SuccessExitStatus や stop-post の設定がないか確認します。そのうえで三つの結果をそれぞれ意図的にテストします。

ネットワークの手がかりは位置の証明ではない

到達可能なルーターのアドレスや既知の MAC は正しいネットワークを示唆できますが、どちらもそれを証明しません。別のネットワークが同じアドレスを再利用でき、ARP のデータは古くなっている可能性があり、識別子は模倣できます。プローブはポリシーと整合する最適化として扱い、認証の結果とはみなしません。

不確実性が何を意味するかをあらかじめ決めます。不確かなときに VPN を起動するのは、単に接続に失敗するだけのクライアントにとっては安全です。不確かなときにスキップするのは安全ではありません。プローブは時間制限つきにし、一覧の先頭を前提にせず実際に有効なインターフェースを読みます。応答しないプローブは「いいえ」と答えるプローブより悪いものです。ユニットがその完了を待つからです。

その前提をユニットファイルの隣に記録し、次の担当者がヘルパーの想定を知れるようにします。スクリプト冒頭のコメントが、それを残す最も安価な場所です。

ネットワーク間の移動を扱う

条件はサービスの起動時に実行され、後から再評価されることはありません。スキップされたユニットは、ノートPCが喫茶店に移動した後もスキップされたままです。稼働中のトンネルは、ノートPCが自宅に戻った後も上がったままです。ネットワーク変更のライフサイクルを、別の仕組みで起動と停止の判断に結びつけます。

systemctl cat home-vpn.service

四つのケースを試します。自宅での起動、外出先での起動、実行できないプローブ、ユニットが稼働中のネットワーク間移動です。スキップの理由をジャーナルに残し、意図的な判断が認証の失敗に見えないようにします。サービスのリファレンスには終了の意味論と最小バージョンが記載されています。

関連記事