二つのクライアントがどちらもジャンプホストを開けても、互いを開く用事はないはずです。サービスへの到達はフォワーディングの決定であり、ピアへの到達はポリシーの決定であり、ほとんどのVPNスタックは両者を別々のオブジェクトに保ちます。前者のために設定したものは後者を強制しません。したがって作業は、ルールを追加することではなく、このサーバーでピア間トラフィックを決めるオブジェクトを見つけ、そこに拒否を書き、クライアントから証明することです。

共有サービスへのルートはピア間の許可ではありません

クライアントサブネットを広告すると、別のクライアントのアドレスへ向けてパケットをどうワイヤに載せるかをピアに伝えます。そのパケットが届くかどうかは後から、それをフォワードするものによって決まります。LinuxゲートウェイではFORWARDチェーン、ユーザー空間のデーモン内ではトンネル自身のセッションテーブルです。ルートは到達可能性に答え、許可には触れません。

だからルートだけを読む分離チェックは何も証明しません。/24を全クライアントに配っても、ピア間のパケットはすべてフォワードフックで破棄できます。危険なのは逆の場合です。明示的なルールがなく、既定が許可的で、誰も試していないから分離が存在しているように見える状態です。

ターミナルを開く前に要件を一行で書きます。サービスブロックとジャンプホストには到達し、クライアントブロック内の他の何にも到達しないことです。

ピアのパケットを実際にフォワードするのは誰かを見極める

サーバーの二つの挙動はクライアント側からは同じに見え、必要なルールは異なります。カーネルがパケットをトンネルインターフェースから再びフォワードするか、デーモンがセッション間でコピーしてカーネルがそれを見ないかです。

WireGuardは前者です。ピア間スイッチがないため、10.8.0.4から10.8.0.5へのパケットはwg0に届き、フォワードフックに当たり、AllowedIPsが両方のアドレスを覆っていれば再びwg0からルーティングされます。OpenVPNはサーバー設定にclient-to-clientが設定されると後者になります。そのディレクティブがユーザー空間でセッション間にパケットを渡すからです。何かを書く前に、読み込まれている設定でそのディレクティブを確認してください。トラフィックを決して見ないカーネルルールは、機能するルールと区別できず、そうやって配備は分離なしで終わります。

読み込まれていると想定するファイルではなく、実効設定を読んでください。ディストリのパッケージは独自のインクルードを持ち込むからです。デーモンがフォワードするなら、拒否はデーモン自身のクライアント別制御に入り、ホストのファイアウォールはホスト宛てのトラフィックだけを絞り込みます。カーネルがフォワードするなら、フォワードフックだけが重要です。

ルーティングや変換を行う配備には、もう一つ確認が要ります。遠隔クライアントが見る送信元は、期待したものではないかもしれません。その場合、クライアントサブネットに一致するルールは何にも一致しません。一致を書く前に変換後も残るアドレスを記録し、NATアドレスを推測するよりインターフェースの組で一致させることを選んでください。

各アドレスに単独で失効できる識別子を与える

二台の端末が一つの資格情報を共有した時点で分離は崩れます。その識別子に紐づいたルールでは両者を分けられず、紛失したノートPCを失効させるとグループ全体が失効します。端末ごとの証明書やピアごとの鍵なら失効を局所化し、ポリシーも読みやすく保てます。各アドレスがちょうど一人または一台に対応するからです。設計上それができない場合は、ルールを細粒度だと説明するのではなく、手順書にそう書いてください。

アドレス割り当てはポリシーと一致させる必要があります。サービス用のブロック、クライアント用のブロックを確保し、クライアントアドレスは先着順の範囲ではなく、管理下のプールから配ってください。サーバーが変換やブリッジを行うなら、対応表を設定の隣に置いてください。次の読み手がルールを再導出するのに必要なのはその表です。

拒否はパケットがトンネルを横切る場所に置く

WireGuardのトラフィックをフォワードするゲートウェイでは、二つのルールで足ります。サービスブロックを許可し、クライアントブロックの残りを破棄します。インターフェースの組はアドレスと同じくらい重要で、宛先だけで一致させるとLANから来るトラフィックも捕まえるため、許可が先に評価されるようその順で読み込んでください。

nftablesでは、この組を既存のinet filterテーブルに入れ、許可を先、破棄を後にします。どちらの一致もアドレスと同様にインターフェースの組を固定します。

nft add rule inet filter forward iifname "wg0" oifname "wg0" ip saddr 10.8.0.0/24 ip daddr 10.8.2.0/24 accept
nft add rule inet filter forward iifname "wg0" oifname "wg0" ip saddr 10.8.0.0/24 ip daddr 10.8.0.0/24 drop

読み込み後は終了ステータスを信じずにチェーンを読み返し、両方のルールが意図した順にあることを確認してください。IPv6プールには対応するip6ルールを追加してください。さもないとデュアルスタックのトンネルでv4の組は飾りです。サーバーに既に許可的なフォワードポリシーがあるなら、チェーンごと置き換えるのではなく破棄を追加してください。

OpenVPNでclient-to-clientが有効な場合、これらのフォワードルールはピア間トラフィックに決して一致せず、拒否はデーモン自身の制御に置く必要があります。ホスト宛てトラフィック用のカーネルルールは残しますが、分離制御として数えないでください。

拒否はサーバーではなくクライアントから証明する

ゲートウェイからの試験は何も証明しません。ローカルで生成されたトラフィックは出力経路を取り、フォワードフックを決して通らないからです。実際のクライアントを二台使い、先に許可のケースを実行して、壊れたトンネルを機能するポリシーと誤認しないようにしてください。

許可されたクライアントからサービスがそのポートで応答することを確認し、同じクライアントからピアのアドレスを試して、拒否ではなく静かな破棄を期待します。

ping -c 3 -W 2 10.8.0.5
nc -z -w 2 10.8.0.5 22
nft list chain inet filter forward
iptables -vnL FORWARD
ping -6 -c 3 -W 2 fd00:8::5

破棄されたパケットとオフラインのピアはどちらもタイムアウトに見えるので、結論を出す前にカウンターを確認してください。ピアが明らかに稼働している間に破棄ルールのカウンターが増えることを確認します。IPv6でも繰り返してください。ip6ルールがないとv4の拒否が有効に見えてしまいます。受け入れと拒否の結果を日付とともに手順書に記録してください。次の移行でルールを再導出するからです。

関連記事