Closed24

TCP/IPをハンズオンで学ぶ[NAT]

ハガユウキハガユウキ

NAT(NetworkAddressTranslation)

  • NATは特定のプロトコルを指した用語ではなく、「IPアドレスを変換する」という概念を指している。「変換」とは、パケットのヘッダにおいて、IPアドレスの入ったフィールドの値を書きかえる操作を指している。

  • このNATという技術は、DHCPと同じくらいIPv4で構築されたネットワークで普及している。インターネットを使っているとしたら、ほとんど確実にNATを利用している。

ハガユウキハガユウキ

なぜ多くのネットワークでNATが使われるようになったのかの背景

  • IPv4アドレスが32ビットの空間を持った正の整数である。
  • 32ビットで表せる数は2の32乗なので、IPv4アドレスの総数は約43億となる。この約43億という数は、IPv4のインターネットができた当初は十分に大きいと考えられていた。しかし、インターネットが普及するにつれて、IPv4アドレスが足りなくなった。そうした背景から普及したのがNATである。より厳密にはNATの一種であるNAPT(Network Address Port Translation)が使われる。
  • NAPTを使うと、少数のグローバルIPアドレスを、多数のプライベートIPアドレスで共有できる(家庭やオフィスにおける典型的なユースケースでは1つのIPv4グローバルアドレスを共有する)。つまり、インターネットを利用するホストの台数に比べて、必要なグローバルIPアドレスが少なくて済む。結果として、限りある資源であるIPv4のグローバルIPアドレスを節約できる。
ハガユウキハガユウキ

プライベートIPアドレス(プライベートアドレス)

プライベートアドレスというのは 、家庭やオフィスといった組織内のネットワークで自由に用いられるIPアドレスです。次のプレフィックスが、プライベートアドレスとして使えるように予約(Reserved)されています。つまり、グローバルアドレスとして使われることを心配することなく、自分たちで自由に採番して使えます。

  • 10.0.0.0/8
  • 172.16.0.0/12
  • 192.168.0.0/16

組織内のネットワークのことはLAN(LocalAreaNetwork)といいます。LANケーブルや無線LANといった言葉としてよく使われている、あのLANです。上記からサブネットを切り出して、そこからLANにつながるノードのIPアドレスを採番して使います。市販のブロードバンドルータでは、192.168.0.0/24や192.168.1.0/24といったセグメントがよくデフォルトで使われています。ただし、プライベートアドレスはLANの中でしか通用しません。なぜなら、ヘッダにプライベートアドレスが含まれるパケットは、インターネットではルーティングできないからです。そこで、プライベートアドレスを使ってインターネットを利用するためにNATを使います。近年は、NATを介さずに直接グローバルアドレスを扱うホストは、外部に公開されるサーバなどに限られています。しかし、グローバルアドレスが潤沢にあった時代は、外部に公開する必要のないホストにもグローバルアドレスが付与されていました。

ハガユウキハガユウキ
  • 10.0.0.0/8
  • 172.16.0.0/12
  • 192.168.0.0/16

上の範囲だったらプライベートIPアドレスとして使えるので、グローバルIPアドレスとして使われる心配をしなくて良い。グローバルIPアドレスをプライベートIPアドレスとして使うと、直接アクセスできてしまうので、ホストが外部から直接アクセス可能になる。これにより、攻撃者はネットワーク内のデバイスにアクセスして攻撃を仕掛けることができるため、セキュリティの脆弱性が高まる。

NATを使うことで、一つのグローバルIPアドレスで、ネットワークのプライベートIPアドレスを持つ複数のホストがインターネット上で通信ができる。

近年は、NATを介さずに直接グローバルアドレスを扱うホストは、外部に公開されるサーバなどに限られている。

ハガユウキハガユウキ

サブネット

サブネットは、より大きなネットワークの部分集合という意味である。たとえば、192.168.0.0/24は192.168.0.0/16に包含される。

ハガユウキハガユウキ

NATには種類があるが、その中でも家庭やオフィスで目にする機会が多い2種類のNAT(Source NAT、Destination NAT)について深ぼる

ハガユウキハガユウキ

Source NAT

  • 技術書でNATまたはNAPTとあった時、多くの場合にSource NATを指している(SNATと略すこともある)
  • SourceNATでは、文字どおりパケットの送信元IPアドレス(SourceIPAddress)を変換する。
  • Source NATではLANでしか通用しないプライベートIPアドレスをインターネットで通用するグローバルIPアドレスに変換する。
  • 一般的にNATはルータが実施する。書き換えるのは送信先がグローバルIPアドレスで、送信元がプライベートIPアドレスになっているパケットのヘッダである。ルータは送信元のプライベートIPアドレスを、自身のインターフェースに付与されているグローバルIPアドレスに書き換える。こうすることで、パケットの送信先も送信元もグローバルIPアドレスになるので、インターネット上で問題なくルーティングができる。
  • ルータまで戻ってきたパケットについても送信先のグローバルIPアドレスをプライベートIPアドレスに書き換える必要がある。
  • 実はIPアドレスを書き換えるだけでは、グローバルIPアドレスの節約にはならない。 送信元がプライベートIPアドレスのパケットを無条件にグローバルIPアドレスに書き換えてインターネットに転送することを考える。この場合、行きは問題ないが帰りに問題がある。LANにたくさんのホストがつながっているとルータはどのホストにパケットを渡せば良いのか分からなくなる。現段階だとプライベートIPアドレスとグローバルIPアドレスは一対一の関係なので、今のやり方だとLAN内のホストを特定するためにプライベートIPアドレスに紐づく複数のグローバルIPアドレスを用意する必要がある。
  • グローバルIPアドレスとプライベートIPアドレスを1:Nの関係で扱うには、IPアドレス以外のフィールドも書き換える必要がある。具体的にはトランスポート層のポート番号である。ポートまで含めて書き換えるため厳密にはNAPT(Network Address Port Translation)になる。
  • NAPTでは、インターネットに転送するパケットのIPアドレスとポート番号を必要に応じて書き換えるとともに、記憶しておく。書き換える前と書き換えた後の対応関係を記憶することで、インターネットからパケットが戻ってきた時に、LANのどのノードにパケットを転送すれば良いか判断できます。NAPTではこの書き換える前と書き換えた後の対応関係をセッションと呼んで管理する。
ハガユウキハガユウキ

NATやNAPTが実際に書きかえるヘッダのフィールドは、実はアドレスやポートだけではありません。なぜなら、IPやTCPといったプロトコルのヘッダにはチェックサムというフィールドがあるためです。チェックサムは、ビットに誤りが生じていないか検出するためのフィールドです。チェックサムの値は、他のフィールドなどから計算されます。そのため、アドレスやポートを書きかえるだけでは、チェックサムの値が一致しなくなるのです。チェックサムが一致しないパケットは、経路上のルータや受け取ったノードによって捨てられてしまう恐れがあります。そのため、アドレスやポートを書きかえた後に再計算した値を使ってチェックサムを書きかえます。その他にも、個別に考慮しなければならないプロトコルがいくつかあります。それらの多くは、ヘッダやペイロードとしてIPアドレスに由来する情報を含んでいます。NATするときは、それらについても修正しなければ、データの整合性がとれなくなります。

ハガユウキハガユウキ

ハガユウキハガユウキ

このネットワークでは、192.0.2.0/24のセグメントがLANで、203.0.113.0/24のセグメントを仮想的なインターネットに見立てている。つまり、routerのgw-veth1に付与された203.0.113.254というIPアドレスが、ルータのグローバルアドレスに相当する。今回の目標は、LANで使われる192.0.2.0/24のセグメントを、通信するときにSourceNATで203.0.113.254へと書きかえることである。

ハガユウキハガユウキ

LinuxでNATを設定するためには、iptablesというコマンドを使う

iptablesを使うと、Linuxのパケットフィルタリング(特定のパケットの転送を拒否または許可するための機能で、ファイアウォールを実現する方法のひとつ)やNATを設定できる。

NATはルータが行うので、NATの設定をしたいなら、router のname spaceでiptablesを実行する。

sudo ip netns exec router iptables -t nat -L
ハガユウキハガユウキ

iptablesの機能は、いくつかのテーブルという概念で分かれています。そのため、NATに関する設定をするときはtオプションでテーブルとしてnatを指定します。Lオプションは、現在の設定を表示するためのオプションです。

ハガユウキハガユウキ
# -A 処理を追加するチェインを指定しています。指定しているPOSTROUTINGは、ルーティングが終わってパケットがインターフェイスから出ていく直前を示しています。
# -s 処理の対象となる送信元IPアドレスの範囲です。
# -o 処理の対象とする出力先のネットワークインターフェイスです。
# -j 条件に一致したパケットをどのように処理するかのルールを指定しています。iptablesでは、処理方法のルールをターゲットと呼びます。指定しているMASQUERADEは、パケットに適用するターゲットがSourceNATであることを示します。

vagrant@ubuntu-focal:/var/tmp/http-home$ sudo ip netns exec router iptables -t nat \
> -A POSTROUTING \
> -s 192.0.2.0/24 \
> -o gw-veth1 \
> -j MASQUERADE
ハガユウキハガユウキ

MASQUERADE(マスカレード)

MASQUERADE(マスカレード)は、LinuxにおけるSource NATの実装につけられた名称である。一般にIPマスカレードと呼ぶ。

ハガユウキハガユウキ

NAPTが絡むと、送信先にパケットが届いた時に、そのパケットの送信元IPアドレスはルータがインターネットに接続するためのネットワークインターフェースに付与されたグローバルIPアドレスになるのか。本来はIPパケットのヘッダには送信元のIPアドレスが付与されるのでプライベートIPアドレスが指定されるはずだけど、それをNAPTがうまく変換してくれているのか。

vagrant@ubuntu-focal:/$ sudo ip netns exec wan tcpdump -tnl -i wan-veth0 icmp
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on wan-veth0, link-type EN10MB (Ethernet), capture size 262144 bytes
IP 203.0.113.254 > 203.0.113.1: ICMP echo request, id 26323, seq 166, length 64
IP 203.0.113.1 > 203.0.113.254: ICMP echo reply, id 26323, seq 166, length 64
IP 203.0.113.254 > 203.0.113.1: ICMP echo request, id 26323, seq 167, length 64
IP 203.0.113.1 > 203.0.113.254: ICMP echo reply, id 26323, seq 167, length 64
ハガユウキハガユウキ

NAPTが絡むと、送信先にパケットが届いた時に、そのパケットの送信元IPアドレスはルータがインターネットに接続するためのネットワークインターフェースに付与されたグローバルIPアドレスになるのか。本来はIPパケットのヘッダには送信元のIPアドレスが指定されるのでプライベートIPアドレスが指定されるはずだけど、それをNAPTがうまく変換してくれているのか。

vagrant@ubuntu-focal:/$ sudo ip netns exec wan tcpdump -tnl -i wan-veth0 icmp
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on wan-veth0, link-type EN10MB (Ethernet), capture size 262144 bytes
IP 203.0.113.254 > 203.0.113.1: ICMP echo request, id 26323, seq 166, length 64
IP 203.0.113.1 > 203.0.113.254: ICMP echo reply, id 26323, seq 166, length 64
IP 203.0.113.254 > 203.0.113.1: ICMP echo request, id 26323, seq 167, length 64
IP 203.0.113.1 > 203.0.113.254: ICMP echo reply, id 26323, seq 167, length 64
ハガユウキハガユウキ

NATをを使うことで、パケットのヘッダに含まれるプライベートアドレスを、ルータへ割り当てられたグローバルIPアドレスに書き換えることができる。

ハガユウキハガユウキ

Destination NAT

  • 「ポートを空ける」という行為は、Destination NATのことを指している
  • Source NATを使う環境では、一般的にインターネットからNATに向けた一方向的な通信はできない(さっきはLAN上のホストがNATで送信元パケットを変換して通信していた。つまりLAN上のホストがきっかけで通信が始まっている)。理由の一つはインターネットではプライベートIPアドレスを宛先にしたパケットをルーティングできないためです。
  • しかし、それではインターネットにサーバを公開したり、ノード同士でP2P(ピア・ツー・ピアとは、お互いにサーバやクライアントといった役割を定めず、同じ立場で通信をやり取りする形態のこと)な通信ができない。なぜなら、そのようなユースケースでは、LANにつながるノードが通信を待ち受ける立場になるからである。つまり、通信がはじまるきっかけとして、インターネットからパケットが到着するのを待つことになる。そこでこの問題を解決するのにDestination NATを使う
  • Destination NATを使うと、インターネットから到達したパケットをLANにつながっているノードに転送できる。つまり、送信先がルータのグローバルIPアドレスになっているパケットをプライベートIPアドレスに書き換えることができる。もちろん、何でもかんでも書きかえるわけではない。一般的には、送信先のポートが特定の値になっているTCPやUDPのパケットだけを選択的に書きかえてLANに転送する。つまり、指定したトランスポート層のプロトコルで、特定のポートに関してだけはインターネットを起点とした通信を許可するわけである。この振る舞いが「ポートを空ける」という表現につながっている。(インターネットから到来するパケットにはサイバー攻撃が含まれるため「ポートを空ける」ときは慎重にしましょう)
ハガユウキハガユウキ

iptablesコマンドを使って、Destination NATのルールを追加する。

# -A 処理を追加するチェインを指定しています。指定しているPREROUTINGは、処理のタイミングとしてインターフェイスからパケットが入ってきた直後を表しています。
# -p 処理の対象となるトランスポート層のプロトコルを指定しています。
# --dport 処理の対象となるポート番号です。
# -d 書きかえる前の送信先IPアドレスです。
# -j 条件に一致したパケットをどのように処理するかのルールを指定しています。指定しているDNATは、パケットに適用するターゲットがDestinationNATであることを示します。
# -to-destination 書きかえた後の送信先IPアドレスです。

vagrant@ubuntu-focal:/var/tmp/http-home$ sudo ip netns exec router iptables -t nat \
> -A PREROUTING \
> -p tcp \
> --dport 54321 \
> -d 203.0.113.254 \
> -j DNAT \
> --to-destination 192.0.2.1
ハガユウキハガユウキ

DestinationNATではパケットの宛先IPアドレスを書きかえます。結果として、インターネットから到来するパケットを起点としたLANのホストに対する通信が可能になる

vagrant@ubuntu-focal:~$ sudo ip netns exec wan tcpdump -tnl -i wan-veth0 "tcp and port 54321"
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on wan-veth0, link-type EN10MB (Ethernet), capture size 262144 bytes
IP 203.0.113.1.36416 > 203.0.113.254.54321: Flags [P.], seq 3055343448:3055343460, ack 2455350498, win 502, options [nop,nop,TS val 264814809 ecr 1674492590], length 12
IP 203.0.113.254.54321 > 203.0.113.1.36416: Flags [.], ack 12, win 509, options [nop,nop,TS val 1674657015 ecr 264814809], length 0
^C
2 packets captured
2 packets received by filter
0 packets dropped by kernel
vagrant@ubuntu-focal:~$ sudo ip netns exec lan tcpdump -tnl -i lan-veth0 "tcp and port 54321"
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on lan-veth0, link-type EN10MB (Ethernet), capture size 262144 bytes
IP 203.0.113.1.36416 > 192.0.2.1.54321: Flags [P.], seq 3055343460:3055343472, ack 2455350498, win 502, options [nop,nop,TS val 264947933 ecr 1674657015], length 12
IP 192.0.2.1.54321 > 203.0.113.1.36416: Flags [.], ack 12, win 509, options [nop,nop,TS val 1674790140 ecr 264947933], length 0
ハガユウキハガユウキ

まとめ

  • SourceNATはIPv4グローバルアドレスを節約するために用いられている技術である。この仕組みは、IPv4を使って構築された家庭やオフィスのネットワークでは、ほとんど必ずといっていいほど利用されている。ただし、グローバルアドレスを節約するためには、実際にはトランスポート層のポート番号まで含めて書きかえるため、厳密にはNAPTになる。
  • DestinationNATは、SourceNATを使っている環境でもインターネットを起点にした通信を実現する。この仕組みは、一般的に「ポートを空ける」という表現で親しまれている。
このスクラップは2023/08/10にクローズされました