blog

ネットワークアドレス変換メッセージのトレース

このセクションでは、メッセージトレースを使用して NAT 関連の接続問題を特定する方法を示します。...

Oct 29, 2025 · 9 min. read
シェア

はじめに

ネットワークアドレス変換は、コンテナや仮想マシンをインターネットに公開する方法です。着信接続は、宛先アドレスを別のアドレスに書き換えるよう要求し、それがコンテナや仮想マシンにルーティングされます。同じ技術は、着信接続を異なるサーバーに分散させるロードバランシングにも使用できます。

ネットワークアドレス変換が期待通りに動作しない場合、接続要求が失敗したり、 間違ったサービスが公開されたり、接続が間違ったコンテナに入ってしまったり、 リクエストがタイムアウトしたりします。このような問題をデバッグする1つの方法は、送られてくるリクエストが期待され た、あるいは設定された変換に合っているかどうかをチェックすることです。

接続トラッキング

NATは単にIPアドレスやポート番号を変更するだけではありません。例えば、アドレスXをYにマッピングするときに、逆変換を実行するための新しいルールを追加する必要はありません。conntrack "と呼ばれるネットフィルターシステムは、既存の接続からのリプライメッセージを認識します。各接続は、conntrackシステム内で独自のNAT状態を持っています。逆変換は自動的に行われます。

ルール・マッチング・トラッキング

nftables ユーティリティを使用すると、メッセージがどのように処理され、ル ールセットのどのルールに一致するかを確認できます。この特別な機能を使用するには、適切な場所に「追跡ルール」を挿入します。これらのルールは、追跡するメッセージを選択します。IPアドレスCのホストがIPアドレスSとポートPのサービスにアクセスしているとします。メッセージがどのNAT変換ルールにマッチするか、システムがどのルールをチェックするか、メッセージがどこかでドロップされるかどうかを知りたいのです。

処理されるのは着信コネクションなので、ルールをpreloutingフックに追加します。 preroutingとは、カーネルがまだメッセージの送信先を決定していないことを意味します。宛先アドレスを変更すると、通常、メッセージはホスト自身によって処理されるのではなく、システムによって転送されるようになります。

初期構成

  1. # nft 'add table inet trace_debug'
  2. # nft 'add chain inet trace_debug trace_pre { type filter hook prerouting priority -; }'
  3. # nft "insert rule inet trace_debug trace_pre ip saddr $C ip daddr $S tcp dport $P tcp flags syn limit rate 1/second meta nftrace set 1"

最初のルールは、ルールの新しいテーブルを追加し、将来的にルールの削除やデバッグを容易にします。コマンド1つで、 nft delete table inet trace_debug 中に一時的にテーブルに追加されたすべてのルールとチェーンが削除されます。

2つ目のルールは、ルーティングのための基本的なフックをシステムに作成し、その優先度を負数に設定することで、接続追跡プロセスとNATルールマッチングで強制されるようにします。

このルールは、このルールに一致するメッセー ジに関連するすべてのイベントをシステムに記録させます。トレース情報を可能な限り効率的に表示するには、トレースされるイベントにレート制限を追加して、その数が管理可能な範囲になるようにすることを検討してください。秒あたり最大1メッセージ、または1分あたり最大1メッセージに制限するのがよいでしょう。上記の例では、端末 $C からポート $P に到達し、端末 $S に到達するすべての SYN メッセージと SYN/ACK メッセージが記録されます。レート制限設定文は、多すぎるイベントによるフラッディングの危険性から保護します。実際、ほとんどの場合、ログに記録するメッセージは1つだけで十分です。

iptablesを使用する場合も設定方法は同様です。同等の設定ルールも同様です:

  1. # iptables -t raw -I PREROUTING -s $C -d $S -p tcp --tcp-flags SYN SYN  --dport $P  -m limit --limit 1/s -j TRACE

トラッキングイベントの取得

ネイティブ nft ユーティリティのユーザーは、nft を直接 nft トレースモードで実行できます:

  1. # nft monitor trace

このコマンドは受信したメッセージと、そのメッセージにマッチするすべてのルールを表示します:

  1. trace id f0f627 ip raw prerouting  packet: iif "veth0" ether saddr ..

結果は次の章で詳しく分析します。iptablesを使用している場合は、まず iptables -バージョン コマンドでインストールされているバージョンを確認してください。例

  1. # iptables --version
  2. iptables v1.8.5 (legacy)

(legacy)は、追跡されたイベントがカーネルのリングバッファに記録されることを意味します。これらのイベントは dmesg や journalctl コマンドで見ることができます。デバッグ出力にはいくつかの情報が欠けていますが、概念的には新しいツールによって提供される出力と似ています。まず、ルールがログに記録された行番号を見て、アクティブな iptables ルールセットと手動で関連付ける必要があります。出力が (nf_tables) を示す場合、xtables-monitor ツールを使用できます:

  1. # xtables-monitor --trace

xtables-monitor ユーティリティは、 nft モニタおよびトレース ユーティリティと同じカーネル インタフェースを使用します。両者の唯一の違いは、xtables-monitorツールがiptables構文でイベントを表示することであり、iptables-nftとnftの両方を使用している場合、マップ/セットを使用するルールやnftablesのみがサポートするその他の機能は表示されません。

代表例

あるVM/コンテナへのポートが機能しない問題をデバッグする必要があるとします。ssh -p .2.3コマンドでそのサーバー上のコンテナの1つにリモート接続できるはずですが、接続要求がタイムアウトしてしまいます。

あなたは、そのコンテナを実行しているホストへのログイン権限を持っています。そのマシンにログインし、トレースルールを追加します。一時的なデバッグ・ルール・テーブルを追加する方法は、前の例で説明しました。トレースルールは以下のようになります:

  1. nft "insert rule inet trace_debug trace_pre ip daddr .3 tcp dport 1222 tcp flags syn limit rate 6/minute meta nftrace set 1"
  1. trace id 9c01f8 inet trace_debug trace_pre packet: iif "enp0" ether saddr .. ip saddr .2 ip daddr .3 ip protocol tcp tcp dport 1222 tcp flags == syn
  2. trace id 9c01f8 inet trace_debug trace_pre rule ip daddr .2 tcp dport 1222 tcp flags syn limit rate 6/minute meta nftrace set 1 (verdict continue)
  3. trace id 9c01f8 inet trace_debug trace_pre verdict continue
  4. trace id 9c01f8 inet trace_debug trace_pre policy accept
  5. trace id 9c01f8 inet nat prerouting packet: iif "enp0" ether saddr .. ip saddr .2 ip daddr .3 ip protocol tcp  tcp dport 1222 tcp flags == syn
  6. trace id 9c01f8 inet nat prerouting rule ip daddr .3  tcp dport 1222 dnat ip to . (verdict accept)
  7. trace id 9c01f8 inet filter forward packet: iif "enp0" oif "veth21" ether saddr .. ip daddr .70.10 .. tcp dport 22 tcp flags == syn tcp window 29200
  8. trace id 9c01f8 inet filter forward rule ct status dnat jump allowed_dnats (verdict jump allowed_dnats)
  9. trace id 9c01f8 inet filter allowed_dnats rule drop (verdict drop)
  10. trace id 20a4ef inet trace_debug trace_pre packet: iif "enp0" ether saddr .. ip saddr .2 ip daddr .3 ip protocol tcp tcp dport 1222 tcp flags == syn

トラッキング結果の行ごとの分析

出力結果の最初の行は、後続の出力のトリガーとなったメッセージの番号です。この行には、nft ルールの構文と同じ構文があり、受信メッセージの最初のフィー ルドに関する情報も含まれています。この行には、メッセージを受信したインタフェースの名前、メッセージの送信元および宛先 MAC アドレス、メッセージの送信元 IP アドレス、TCP の送信元および宛先ポートも表示されます。この行の先頭には「トレース番号」も表示されます。この番号は、トレースルールにマッチする特定のメッセージを識別します。2行目には、メッセージがマッチした最初のトレースルールが含まれています:

  1. trace id 9c01f8 inet trace_debug trace_pre rule ip daddr .2 tcp dport 1222 tcp flags syn limit rate 6/minute meta nftrace set 1 (verdict continue)

これは追加されたばかりのトレースルールです。ここに表示される最初のルールは、常にメッセージ追跡を有効にするものです。ここに他のルールがある場合は、ここには表示されません。トレース出力がない場合は、このトレースルールが到着しなかったか、マッチングに成功しなかったことを意味します。次の2行は、後続のマッチングルールがなく、trace_pre フックがメッセージの継続を許可していることを示します。

次のマッチングルールは

  1. trace id 9c01f8 inet nat prerouting rule ip daddr .3  tcp dport 1222 dnat ip to . (verdict accept)

このDNATルールは、他のアドレスとポートへのマッピングを設定します。ルールのパラメータ.70.10は、パケットを受信する必要があるVMのアドレスで、今のところ問題はありません。正しいVMアドレスでない場合は、アドレスの入力が間違っているか、間違ったNATルールがマッチしています。

IP 転送

下の出力でわかるように、IPルーティングエンジンは、メッセージを別のホストに転送する必要があることをIPスタックに伝えます:

  1. trace id 9c01f8 inet filter forward packet: iif "enp0" oif "veth21" ether saddr .. ip daddr .70.10 .. tcp dport 22 tcp flags == syn tcp window 29200

これは受信したメッセージの別のプレゼンテーションですが、以前と比べるといくつか興味深い違いがあります。結果は出力インターフェースのコレクションを持っています。これはルーティングの決定にルールがあるため、存在しません。トレース番号は同じなので、まだ同じメッセージですが、宛先アドレスとポートが変更されています。tcp dport 2212にマッチするルールがまだあると仮定すると、それらはもうこの段階ではメッセージに影響を与えません。

その行に出力インターフェイスが含まれていなければ、ルーティングの決定はメッセージをローカルマシンにルーティングしたことになります。ルーティングプロセスのデバッグは別のトピックなので、この記事では取り上げません。

  1. trace id 9c01f8 inet filter forward rule ct status dnat jump allowed_dnats (verdict jump allowed_dnats)

この出力は、メッセージが trace id 9c01f8 inet filter allowed_dnats rule drop (verdict drop) チェーンにジャンプしたルールにマッチしたことを示しています。次の行には、接続失敗の根本原因が書かれています:

  1. trace id 9c01f8 inet filter allowed_dnats rule drop (verdict drop)

このルールはメッセージを無条件に破棄するので、そのメッセージのログは出力されません。次の行は別のメッセージの出力を示しています:

  1. trace id 20a4ef inet trace_debug trace_pre packet: iif "enp0" ether saddr .. ip saddr .2 ip daddr .3 ip protocol tcp tcp dport 1222 tcp flags == syn

トレース番号はすでに異なっており、メッセージの内容は同じです。最初のメッセージが破棄されたので、TCP は再送を試みました。最初のメッセージは破棄されたので、TCPは再送を試みました。さて、次はチェーンをチェックする番です。

ルールセット分析

前のセクションでは、メッセージがinetフィルターテーブルの trace id 9c01f8 inet trace_debug trace_pre rule ip daddr .2 tcp dport 1222 tcp flags syn limit rate 6/minute meta nftrace set 1 (verdict continue) 名前のチェーンにドロップされていることを発見しました。では、確認してみましょう:

  1. # nft list chain inet filter allowed_dnats
  2. table inet filter {
  3.  chain allowed_dnats {
  4.   meta nfproto ipv4 ip daddr . tcp dport @allow_in accept
  5.   drop
  6.    }

allow_inセット内のパケットを受け入れるルールはトレースログには表示されません。上記のメッセージの宛先アドレスが @allow_in セットにあるか、要素を列挙して再確認してください:

  1. # nft "get element inet filter allow_in { . }"
  2. Error: Could not process rule: No such file or directory

当然のことながら、アドレス・サービスのペアはコレクションにはありません。コレクションに追加します。

  1. # nft "add element inet filter allow_in { . }"

ここでクエリーコマンドを実行すると、新しく追加された要素が返されます。

  1. # nft "get element inet filter allow_in { . }"
  2. table inet filter {
  3. set allow_in {
  4. type ipv4_addr . inet_service
  5. elements = { . }

これで ssh コマンドが動作し、トレースに変更が反映されるはずです:

  1. trace id 497abf58 inet filter forward rule ct status dnat jump allowed_dnats (verdict jump allowed_dnats)
  2. trace id 497abf58 inet filter allowed_dnats rule meta nfproto ipv4 ip daddr . tcp dport @allow_in accept (verdict accept)
  3. trace id 497abf58 ip postrouting packet: iif "enp0" oif "veth21" ether .. trace id 497abf58 ip postrouting policy accept

これは、メッセージが転送パスの最後のフック postroutingを通過したことを示します。

それでも接続できない場合は、メッセージフローの後半に問題があり、nftables ルールセットでカバーできない可能性があります。

要約

経由:

Read next