OpenVPN の pull-filter は意図したルートより多くに一致する
pull-filter はテキストを前方一致でプッシュされたオプションと比較するため、route は route-gateway や route-ipv6 にも当たります。まず動詞を、次にルートを確認してください。
ローカルのデフォルトルートを残すために一行を追加したところ、それ以降トンネルは内部 DNS サーバーに届かなくなりました。その行は pull-filter ignore "route" でしたが、これは「このルートを無視する」という意味ではありません。「テキストがその五文字で始まるプッシュされたオプションをすべて捨てる」という意味で、通常のサーバーではルート本体に加えて route-gateway、route-metric、route-ipv6 も含まれます。以下のチェックリストは、他の人が使うプロファイルに触れる前に私が実行するものです。
フィルターはプッシュされたオプションにだけ作用する
pull-filter は、トンネル確立後にサーバーが送るオプション行にだけ作用し、それ以外には作用しません。ローカルファイルに書いたオプションは影響を受けず、アプリケーションの通信も同様です。プロセスにトンネルを使わせないようにしたいのであれば、これはまったくの間違った道具で、どのようなフィルターの組み合わせでも実現できません。
この範囲は有用です。サーバーの設定をそのままにして、その一部をローカルで拒否できるからです。これはチケットに書く価値のある制限でもあります。フィルターが変えるのはこのクライアントが受け入れるものであり、サーバーが提供するものではありません。同じプロファイルの他の利用者には影響しません。また、このオプションを実装していない別のクライアントに切り替えると失われます。その上にワークフローを築く前に知っておく価値があります。
パターンを書く前に、サーバーが実際に何を送るかを確認してください。冗長度を高くすると、クライアントは受け取ったプッシュ済みオプション文字列を出力します。照合する正確なテキストについて、これが唯一の正直な情報源です。
sudo openvpn --config /etc/openvpn/corp.ovpn --verb 4
sudo grep -n 'pull-filter' /etc/openvpn/corp.ovpn
プッシュされた行は、カンマで区切られたオプションの一覧として読んでください。オプション名とその引数はスペースで区切られている点に注意します。フィルターのテキストはそのストリームと比較されるため、次の手順では空白が非常に重要になります。
一致は前方一致なので、空白がすべてを決める
比較は文字列の前方一致であり、解析済みのオプション名ではありません。したがって "route" は route-gateway 10.8.0.1、route-ipv6 2001:db8::/32、route-metric 100 に一致します。末尾に空白を一つ足すと、おそらく意図した一族に絞られ、完全な前方一致にすると特定の宛先まで絞られます。
| Filter text | What it actually matches | Typical outcome |
|---|---|---|
"route" | Every option beginning with those letters | Routes, gateway, metric and IPv6 routes all dropped |
"route " | Route entries only, not the hyphenated siblings | The routes go, the rest survives |
"route 10.60." | A single destination range | The narrowest rule, and the easiest to audit |
"dhcp-option DNS" | Pushed DNS settings | Resolver stays local, internal names stop resolving |
最後の行は、スプリットトンネルの作業中に人を捕まえる行です。独自のリゾルバーを保つのは正当な選択で、それはすべての内部名がプライベートゾーンを見られないリゾルバーに依存することを意味します。これは本番で発見する副作用ではなく、意図して受け入れる結果です。
前方一致は、ルールが後から静かに広がりうることも意味します。サーバーがあなたのテキストで始まる新しいオプションを追加すると、誰もプロファイルを編集しないままフィルターがそれにも適用され始めます。これが設計で対処すべき故障モードで、緩和策はオプションのできるだけ多くを一致させることであり、できるだけ少なくすることではありません。
三つの動詞、三つの異なる結果
accept、ignore、reject は同じ考えの三つの綴りではありません。一致したときにクライアントが何をするか、そして誰が気づくかが異なります。
ignore は一致したオプションを静かに捨てます。意図的に置き換えた設定にはこれが適切です。reject はそれを捨ててクライアントに文句を言わせます。その不在が後で発見されるのではなくログに見えるべき場合に適切です。accept は肯定的な形で、個々のオプションを外すのではなく許可リストを組むときに使います。
どの動詞を使っても、フィルターは現れる順に適用されるため、重なる二つのルールは順序に依存した結果を生み、レビューが非常に難しくなります。意図ごとに一つのルールを保ち、それぞれに最も具体的なテキストを入れます。許可リストが必要なら、同じことを足し合わせる ignore のルールの山よりも、少数の accept ルールを選んでください。そうすればレビュー担当者はファイルを上から下まで読んで結果を予測でき、それだけが規模に耐える唯一のテストです。
適用されたルートとリゾルバーを確認し、プロファイルは見ない
ファイル上で正しく見えるフィルターでも、何も有用にルーティングしないトンネルを残すことがあります。プッシュされたオプションがすでに適用または破棄された稼働中のシステムで効果を確認してください。
ip route show default
ip route show | grep -c 'proto static'
resolvectl status | grep -A4 'Link.*tun0'
curl -sS -o /dev/null -w '%{http_code}\n' --max-time 5 https://intranet.example.internal/
最初の二行は、プッシュされたデフォルトルートが届いたかと、静的な項目がいくつ残ったかを示します。リゾルバーへの問い合わせは、トンネルインターフェースが実際にどのサーバーを使っているかを示し、通常そこが DNS の驚きが現れる場所です。最後の行は、利用者にとって重要な唯一の問いに答えます。
そこにいる間に両方向を試してください。ローカルのインターネットを動かしたまま内部の名前解決を静かに壊すルールは、表面的な確認を通り抜けて最初の実際の作業で失敗します。同じテストに内部リソースと公開サイトを一つずつ入れてください。
例外とその所有者を記録する
フィルターは運用者の設計に対する例外なので、理由とレビュー時点を添えて書き留めてください。「ローカルのデフォルトを保つためにプッシュされたルートを無視する」はレビュー可能です。プロファイルの裸の一行はそうではなく、次の人がそれを消すか、推測で広げてしまいます。
バージョンもここでは重要です。一致の挙動と正確に受け入れられる構文は OpenVPN 2.4、2.5、2.6 で洗練されてきたので、出荷するビルドのマニュアルでパターンを確認してください。古いリリースについてのフォーラム投稿で確認しないでください。OpenVPN 2.6 のマニュアルがこのオプションとその一致規則を文書化しています。
最後に、サーバーを運用している人と例外について合意してください。意図的なサーバーポリシーに反するクライアント側のフィルターは、次の監査を待つサポートインシデントです。プロファイルが出荷された後に誰かがルートの欠落に気づいてから解決するより、出荷前に決着させるほうがはるかに安く済みます。