ip rule でポリシールールを追加すると、リンクが消えるまでは機能しますが、その瞬間にルールは単に存在しなくなります。大きな音を立てて失敗したものは何もなく、変わったのはカーネルの状態で、そのルールがあるべきだと知る設定の所有者は誰もいませんでした。これが、生き残るルールとそうでないルールの違いのすべてです。systemd-networkd のホストでは、再接続のたびにコマンドを再実行するのではなく、ルールに所有者を与えることが解決策になります。

ルールは、誰かが所有しているときにだけ存在する

カーネルのルーティングポリシーは設定ではなく状態です。実行中のシステムの中にあり、依存するインターフェースを壊すものは何でも、それを一緒に持っていきます。シェルのコマンドはその状態を変えるだけで、意図の記録を残しません。

ネットワークマネージャーは逆に動き、設定を読み、適用し、リンクイベントが前の状態を無効にしたときに再度適用します。設定で宣言されたルールは作り直されますが、その横で追加されたルールは所有者のない外来のオブジェクトです。

この区別には networkd で名前がついており、何かを有効にする前に読む価値があります。最近のバージョンでは外来のルールが既定で削除されるため、手動で入れた項目が残ると考えていた人は驚きます。

設定を信じる前にバージョンを読む

所有の扱いは networkd.conf の設定で制御され、その設定は古い systemd のリリースには存在しません。古いホストは読んでいる文書とは違う振る舞いをするため、それを前提に設計する前にインストールされているバージョンを確認してください。

重要な値は二つあります。一つは networkd の設定で宣言されていないルールを削除するかどうかを決め、もう一つは経路について同じことをします。どちらもポリシーの中身ではなく所有に関するもので、どちらも現在のリリースでは既定で厳しい側の振る舞いになります。これらを全体で無効にすると、すべての外来ルールが生き残りますが、それは意図した一つの VPN ポリシーを宣言するよりずっと広い判断です。

systemctl --version | head -1
networkctl --version | head -1
grep -R "ManageForeign" /etc/systemd/ /usr/lib/systemd/ 2>/dev/null

その grep は、ローカルで何が設定されているかと、ベンダーの既定値が何かを教えてくれます。存在しないことも情報で、コンパイル時の既定値が効いているという意味だからです。既定値をメモに書いておけば、次の人がもう一度突き止める必要はありません。

ルールと、それが指すテーブルを宣言する

.network ファイルのルールはテーブルに名前を付けるだけで、名前ができるのはそこまでです。そのテーブルの中の経路を作るわけでも、それが妥当か確かめるわけでもありません。経路なしでルールを宣言すると、通信に一致させてから落とすポリシーになります。

同じファイルに経路を宣言し、到達できる次のホップと正しいテーブルを指定します。宣言したルールに優先度を残し、順序付きの一覧で期待した位置に収まるようにします。意図より早く誤ったテーブルに届くルールは、直そうとしたサービスより多くの通信を変えてしまいます。インターフェースの所有と接続の残りの部分が依然として必要なので、下の断片は完全なファイルではなく形として読んでください。

[RoutingPolicyRule]
Family=ipv4
IPProtocol=tcp
SourcePort=443
Table=100
Priority=1000

[Route]
Destination=10.80.0.0/24
Gateway=10.80.0.1
Table=100

そのうえで再読み込みし、ファイルではなくカーネルの状態を読み戻します。きれいに解釈された設定でも、意図しないルールができることがあり、二つの見え方が一致するのは宣言が完全だったときだけです。

インターフェースの所有者を一人に決める

一つのトンネルに二人のマネージャーがいると、どちらにも不具合のように見える争いが起きます。既存の wg-quick 設定を networkd に取り込むのは追加ではなく移行であり、両方を動かすとインターフェースとそのルールが二人の所有者の下に残ります。

移行は一度に済ませ、古いユニットを止めて無効にし、インターフェースと経路を networkd で宣言してから再読み込みします。古いユニットが起動時に戻ってこないことを確認してください。再起動後に再び有効になる残存ユニットは、いま宣言した状態を上書きします。新しい設定が再接続を一度越えるまで古い設定ファイルを残し、その後で引退させます。

マークの所有にも同じ注意が必要で、ルールがファイアウォールのマークに一致するなら、誰かがそのマークを設定しなければなりません。誰も設定しないマークを参照するルールは、意図的に見える死んだ設定です。

ライフサイクルのイベントを練習する

設定は、イベントをまたいだ振る舞いの分だけの価値しかありません。リンクを落とし、上げ、サービスを再起動し、アドレスを更新します。それぞれの後に、ルールの一覧とテーブルの中身を読みます。

sudo networkctl reload
ip rule show
ip route show table 100
sudo systemctl restart systemd-networkd

意図した結果は、同じ所有者つきのポリシーが作り直され、古い状態が引退することであり、再接続のたびにシェルのルールを足し直すより長持ちします。変更記録に、ルールを所有するファイルと対応するバージョンを書いた一行を残してください。そうすれば次のエンジニアはシェルの履歴を探さずにポリシーを見つけられます。所有者のいないルールは、また失われるルールです。

関連記事