hana_shinのLinux技術ブログ

Linuxの技術情報を掲載しています。特にネットワークをメインに掲載していきます。

iptablesコマンドの使い方(ターゲットの使い方)



1 はじめに

ターゲットはルールにマッチしたら実行する処理のことです。LOGターゲットならログへの出力、DROPターゲットならパケット廃棄といった処理を行います。なお、iptablesコマンドの基本的な使い方は、iptablesコマンドの使い方 - hana_shinのLinux技術ブログを参照してください。

2 検証環境

2.1 ネットワーク構成

サーバとクライアントの2台構成です。図中のeth0はNICの名前です。

                          192.168.122.0/24
client(eth0) -------------------------------------------(eth0) server
            .153                                    .128

2.2 版数

サーバとクライアントのCentOS の版数は以下のとおりです。

[root@client ~]# cat /etc/redhat-release
CentOS Linux release 7.9.2009 (Core)

カーネル版数は以下のとおりです。

[root@client ~]# uname -r
3.10.0-1160.el7.x86_64

3 事前準備

firewall-cmdコマンドとiptablesコマンドは一緒に使うことはできないので、firewalldサービスを停止します。なお、firewall-cmdの使い方は、firewall-cmdの使い方 - hana_shinのLinux技術ブログを参照してください。

[root@server ~]# systemctl stop firewalld.service

firewalldサービスの状態を確認します。firewalldサービスが停止したことがわかります。

[root@server ~]# systemctl is-active firewalld.service
inactive

4 LOGターゲット

LOGターゲットは、ルールにマッチしたら情報(IPアドレス等)をログに出力するターゲットです。ここでは、ICMPパケットを受信したら、ICMPパケットのIPアドレス等の情報をログに出力するルールを作成してみます。

[root@server ~]# iptables -A INPUT -p icmp -j LOG

作成したルールを確認します。

[root@server ~]# iptables -nvL INPUT
Chain INPUT (policy ACCEPT 47 packets, 3272 bytes)
 pkts bytes target     prot opt in     out     source               destination
    0     0 LOG        icmp --  *      *       0.0.0.0/0            0.0.0.0/0            LOG flags 0 level 4

ログを確認するため、journalctlコマンドを実行します。なお、journalctlコマンドの使い方は、journalctlコマンドの使い方 - hana_shinのLinux技術ブログを参照してください。

[root@server ~]# journalctl -f

クライアントで宛先をサーバにしてpingコマンドを1回実行します。なお、pingコマンドの使い方は、pingコマンドの使い方 - hana_shinのLinux技術ブログを参照してください。

[root@client ~]# ping -c 1 192.168.122.128

pingを実行すると、以下のログが出力されることがわかります。

[root@server ~]# journalctl -f
12月 01 21:15:43 server kernel: IN=eth0 OUT= MAC=52:54:00:78:10:73:52:54:00:8c:76:20:08:00 SRC=192.168.122.153 DST=192.168.122.128 LEN=84 TOS=0x00 PREC=0x00 TTL=64 ID=61606 DF PROTO=ICMP TYPE=8 CODE=0 ID=1456 SEQ=1

5 DROP/REJECTターゲット

5.1 DROPREJECTの違い

DROPは受信したパケットを破棄するだけです。一方、REJECTは受信パケットを廃棄したら、廃棄した旨をパケット送信元に送信します。

DROPの場合は以下のようになります。クライアントからサーバに送信したICMP echo requestパケットは、サーバで廃棄されます。

                          192.168.122.0/24
client(eth0) -------------------------------------------(eth0) server
            .153                                    .128

     ping       ----------- ICMP echo request ---------->  廃棄(DROP)

一方、REJECTの場合は以下のようになります。クライアントからサーバに送信したICMP echo requestパケットはサーバで廃棄されます。そして、廃棄した旨をICMP port unreachableパケットでクライアントに送信します。

                          192.168.122.0/24
client(eth0) -------------------------------------------(eth0) server
            .153                                    .128

     ping       ----------- ICMP echo request ---------->  廃棄(REJECT)
                <---------- ICMP port unreachable ------

5.2 DROPターゲットの動作確認

ICMPパケットを受信したら廃棄するDROPターゲットのルールを作成します。

[root@server ~]# iptables -A INPUT -p icmp -j DROP

作成したルールを確認します。

[root@server ~]# iptables -nvL INPUT
Chain INPUT (policy ACCEPT 37 packets, 2572 bytes)
 pkts bytes target     prot opt in     out     source               destination
    0     0 DROP       icmp --  *      *       0.0.0.0/0            0.0.0.0/0

tcpdumpコマンドを実行します。なお、tcpdumpコマンドの使い方は、tcpdumpの使い方(基本編) - hana_shinのLinux技術ブログを参照してください。

[root@client ~]# tcpdump -i eth0 icmp -nn

クライアントでもう1つターミナルをオープンしてpingコマンドを実行します。-cオプションはpingコマンドを実行する回数です。-wはタイムアウトを指定するオプションです。送信したICMP echo requestパケットに対する応答が1秒以内になければpingコマンドを終了するようにします。

[root@client ~]# ping -c 1 -w 1 192.168.122.128

tcpdumpコマンドの実行結果を確認すると、クライアンからサーバにICMP echoパケットが1つ送信されたことがわかります。

[root@client ~]# tcpdump -i eth0 icmp -nn
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on eth0, link-type EN10MB (Ethernet), capture size 262144 bytes
22:04:03.773560 IP 192.168.122.153 > 192.168.122.128: ICMP echo request, id 1544, seq 1, length 64

ルールを確認すると、ICMPパケットが廃棄されていることがわかります。

[root@server ~]# iptables -nvL INPUT
Chain INPUT (policy ACCEPT 99 packets, 6872 bytes)
 pkts bytes target     prot opt in     out     source               destination
    1    84 DROP       icmp --  *      *       0.0.0.0/0            0.0.0.0/0

5.3 REJECTターゲットの動作確認

ICMPパケットを受信したら廃棄します。そして、廃棄した旨を送信元に送信するREJECTターゲットのルールを作成します。

[root@server ~]# iptables -I INPUT -p icmp -j REJECT

作成したルールを確認します。

[root@server ~]# iptables -nvL INPUT
Chain INPUT (policy ACCEPT 69 packets, 5588 bytes)
 pkts bytes target     prot opt in     out     source               destination
    0     0 REJECT     icmp --  *      *       0.0.0.0/0            0.0.0.0/0            reject-with icmp-port-unreachable||<

tcpdumpコマンドを実行します。

[root@client ~]# tcpdump -i eth0 -p icmp -nn

クライアントでもう1つターミナルをオープンしてpingコマンドを実行します。

[root@client ~]# ping -c 1 192.168.122.128

tcpdumpの実行結果を確認すると、ICMP port unreachableパケットがICMP echoパケット送信元に送信されていることがわかります。

[root@client ~]# tcpdump -i eth0 -p icmp -nn
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on eth0, link-type EN10MB (Ethernet), capture size 262144 bytes
22:03:10.907403 IP 192.168.122.153 > 192.168.122.128: ICMP echo request, id 1499, seq 1, length 64
22:03:10.909626 IP 192.168.122.128 > 192.168.122.153: ICMP 192.168.122.128 protocol 1 port 17955 unreachable, length 92

ルールを確認すると、ICMPパケットが廃棄されていることがわかります。

[root@server ~]# iptables -nvL INPUT
Chain INPUT (policy ACCEPT 88 packets, 6952 bytes)
 pkts bytes target     prot opt in     out     source               destination
    1    84 REJECT     icmp --  *      *       0.0.0.0/0            0.0.0.0/0            reject-with icmp-port-unreachable

6 RETURNターゲット

ルールに一致したら、それ以上ルールに一致しているかどうかの処理をしないターゲットです。ルールに一致したら、ルールの呼び出し元に復帰します。

6.1 RETURNターゲットを使用した場合

テスト用のルールを3つ作成します。まず1つ目を作成します。

[root@server ~]# iptables -A INPUT -p icmp -j LOG --log-prefix "--LOG1--"

2つ目のルールを作成します。

[root@server ~]# iptables -A INPUT -p icmp -j RETURN

3つ目のルールを作成します。

[root@server ~]# iptables -A INPUT -p icmp -j LOG --log-prefix "--LOG2--"

作成したルールを確認します。2番目がRETURN ターゲットのルールです。

[root@server ~]# iptables -nvL INPUT --line-numbers
Chain INPUT (policy ACCEPT 64 packets, 4480 bytes)
num   pkts bytes target     prot opt in     out     source               destination
1        0     0 LOG        icmp --  *      *       0.0.0.0/0            0.0.0.0/0            LOG flags 0 level 4 prefix "--LOG1--"
2        0     0 RETURN     icmp --  *      *       0.0.0.0/0            0.0.0.0/0
3        0     0 LOG        icmp --  *      *       0.0.0.0/0            0.0.0.0/0            LOG flags 0 level 4 prefix "--LOG2--"

ログを確認するため、journalctlコマンドを実行します。

[root@server ~]# journalctl -f

クライアントでpingコマンドを実行します。宛先はサーバを指定します。

[root@client ~]# ping -c 1 192.168.122.128

ログを確認するとLOG1は表示されていますが、LOG2は表示されていないことがわかります。つまり、1行目のルールを実行したあと、2行目のルールを実行してルールの呼び出し元に復帰していることがわかります。

[root@server ~]# journalctl -f
12月 13 20:21:12 server kernel: --LOG1--IN=eth0 OUT= MAC=52:54:00:78:10:73:52:54:00:8c:76:20:08:00 SRC=192.168.122.153 DST=192.168.122.128 LEN=84 TOS=0x00 PREC=0x00 TTL=64 ID=27305 DF PROTO=ICMP TYPE=8 CODE=0 ID=1459 SEQ=1

6.2 RETURNターゲットを使用しない場合

テスト用のルールを2つ作成します。まず1つ作成します。

[root@server ~]# iptables -A INPUT -p icmp -j LOG --log-prefix "--LOG1--"

もう1つルールを作成します。

[root@server ~]# iptables -A INPUT -p icmp -j LOG --log-prefix "--LOG2--"

作成したルールを確認します。

[root@server ~]# iptables -nvL INPUT --line-numbers
Chain INPUT (policy ACCEPT 13 packets, 972 bytes)
num   pkts bytes target     prot opt in     out     source               destination
1        0     0 LOG        icmp --  *      *       0.0.0.0/0            0.0.0.0/0            LOG flags 0 level 4 prefix "--LOG1--"
2        0     0 LOG        icmp --  *      *       0.0.0.0/0            0.0.0.0/0            LOG flags 0 level 4 prefix "--LOG2--"

クライアントでpingコマンドを実行します。宛先はサーバを指定します。

[root@client ~]# ping -c 1 192.168.122.128

ルールを確認すると、RETURNターゲットのルールを指定した時と違って、2つのルールでICMPパケットを処理していることがわかります。

[root@server ~]# iptables -nvL INPUT --line-numbers
Chain INPUT (policy ACCEPT 26 packets, 1900 bytes)
num   pkts bytes target     prot opt in     out     source               destination
1        1    84 LOG        icmp --  *      *       0.0.0.0/0            0.0.0.0/0            LOG flags 0 level 4 prefix "--LOG1--"
2        1    84 LOG        icmp --  *      *       0.0.0.0/0            0.0.0.0/0            LOG flags 0 level 4 prefix "--LOG2--"

7 NFLOGターゲット

tcpdumpのフックポイントはIP層とドライバの間にありますが、NFLOGターゲットを使うとIP層に位置するチェインにフックポイントを設定することができます。なお、フックポイントとは、パケットをキャプチャする場所のことです。

ここでは、INPUTチェインにtcpdumpのフックポイントを設定してみます。

[root@server ~]# iptables -I INPUT -p tcp --dport 20000 -j NFLOG

作成したルールを確認します。INPUTチェインにNFLOGターゲットが設定されていることがわかります。

[root@server ~]# iptables -nvL INPUT --line-numbers
Chain INPUT (policy ACCEPT 5 packets, 380 bytes)
num   pkts bytes target     prot opt in     out     source               destination
1        0     0 NFLOG      tcp  --  *      *       0.0.0.0/0            0.0.0.0/0            tcp dpt:20000

tcpdumpを実行します。このとき、インタフェースにnflogを指定します。なお、NFLOGを指定した場合、tcpdumpがキャプチャするパケットは、-wオプションを使って、いったんファイルに保存する必要があります。なお、tcpdumコマンドの使い方は、tcpdumpの使い方(基本編) - hana_shinのLinux技術ブログを参照してください。

[root@server ~]# tcpdump -i nflog -w nflog.cap
tcpdump: listening on nflog, link-type NFLOG (Linux netfilter log messages), capture size 262144 bytes

サーバでncコマンドを実行します。TCPの20000番ポートでncプロセスがListenするようにします。なお、ncコマンドの使い方は、ncコマンドの使い方(ネットワーク実験の幅が広がるなぁ~) - hana_shinのLinux技術ブログを参照してください。

[root@server ~]# nc -kl 20000

クライアントでncコマンドを実行します。宛先はサーバの20000番ポートを指定します。

[root@client ~]# nc 192.168.122.128 20000

Ctrl+Cを押下して、tcpdumpコマンドを終了します。

[root@server ~]# tcpdump -i nflog -w nflog.cap
tcpdump: listening on nflog, link-type NFLOG (Linux netfilter log messages), capture size 262144 bytes
^C2 packets captured
2 packets received by filter
0 packets dropped by kernel

tsharkコマンドの実行結果を確認すると、INPUTチェインで処理するクライアントからサーバへのパケットのみがキャプチャされていることがわかります。サーバからクライアントへのSYN+ACKはINPUTチェインで処理されないので、キャプチャされていません。なお、tsharkコマンドのインストール方法、使い方はtsharkコマンドの使い方 - hana_shinのLinux技術ブログを参照してください。

[root@server ~]# tshark -r nflog.cap -nn
Running as user "root" and group "root". This could be dangerous.
  1          0 192.168.122.153 -> 192.168.122.128 TCP 164 53888 > 20000 [SYN] Seq=0 Win=29200 Len=0 MSS=1460 SACK_PERM=1 TSval=3752814 TSecr=0 WS=128
  2          0 192.168.122.153 -> 192.168.122.128 TCP 156 53888 > 20000 [ACK] Seq=1 Ack=1 Win=29312 Len=0 TSval=3752815 TSecr=3758610

8 REDIRECTターゲット

宛先ポート番号を変更するターゲットです。ここでは、受信パケットの宛先ポート番号を10000から20000に変更してみます。ポート番号をREDIRECTターゲットで変更することで、クライアントからサーバへの10000番ポート宛てのパケットが20000番ポートで待ち受けているプロセス(nc)にパケットが届きます。

[root@server ~]# iptables -t nat -A PREROUTING -p tcp --dport 10000 -j REDIRECT --to-port 20000

PREROUTINGチェインに設定したREDIRECTターゲットを確認します。このターゲットは宛先ポート番号を10000から20000に変更します。

[root@server ~]# iptables -t nat -nvL PREROUTING --line-numbers
Chain PREROUTING (policy ACCEPT 0 packets, 0 bytes)
num   pkts bytes target     prot opt in     out     source               destination
1        0     0 REDIRECT   tcp  --  *      *       0.0.0.0/0            0.0.0.0/0            tcp dpt:10000 redir ports 20000

サーバでncコマンドを実行します。

[root@server ~]# nc -kl 20000

lsofコマンドを実行すると、ncプロセスがTCPの20000番ポートでListenしていることがわかります。
なお、lsofコマンドのインストール、使い方は、lsofコマンドの使い方 - hana_shinのLinux技術ブログを参照してください。

[root@server ~]# lsof -c nc -a -i -a -nP
COMMAND  PID USER   FD   TYPE DEVICE SIZE/OFF NODE NAME
nc      1702 root    3u  IPv6  34357      0t0  TCP *:20000 (LISTEN)
nc      1702 root    4u  IPv4  34358      0t0  TCP *:20000 (LISTEN)

クライアントでncコマンドを実行します。宛先はサーバの10000番ポートを指定します。

[root@client ~]# nc 192.168.122.128 10000
12345

tcpdumpコマンドの実行結果を確認すると、10000番のポート番号を使ってTCPコネクションを確立していることがわかります。tcpdumpのフックポイントは、IP層とドライバの間にあります。PREROUTINGチェインはIP層にあります。したがって、PREROUTINGチェインで宛先ポート番号を10000から20000に変更する前のポート番号が表示されていることがわかります。

[root@server ~]# tcpdump -i eth0 port 10000 or port 20000 -nn
21:42:49.730489 IP 192.168.122.153.39516 > 192.168.122.128.10000: Flags [S], seq 2432710170, win 29200, options [mss 1460,sackOK,TS val 5123058 ecr 0,nop,wscale 7], length 0
21:42:49.730701 IP 192.168.122.128.10000 > 192.168.122.153.39516: Flags [S.], seq 924901017, ack 2432710171, win 28960, options [mss 1460,sackOK,TS val 5128854 ecr 5123058,nop,wscale 7], length 0
21:42:49.732036 IP 192.168.122.153.39516 > 192.168.122.128.10000: Flags [.], ack 1, win 229, options [nop,nop,TS val 5123061 ecr 5128854], length 0

INPUTチェインで採取したパケットを確認してみます。PREROUTINGチェインで宛先ポート番号を10000番から20000番に変更した後なので、ポート番号が20000番になっていることがわかります。

[root@server ~]# tshark -r nflog.cap -nn
Running as user "root" and group "root". This could be dangerous.
  1          0 192.168.122.153 -> 192.168.122.128 TCP 164 39516 > 20000 [SYN] Seq=0 Win=29200 Len=0 MSS=1460 SACK_PERM=1 TSval=5123058 TSecr=0 WS=128
  2          0 192.168.122.153 -> 192.168.122.128 TCP 156 39516 > 20000 [ACK] Seq=1 Ack=1 Win=29312 Len=0 TSval=5123061 TSecr=5128854

9 MARKターゲット

パケットに印をつけるターゲットです。ここでは、受信したICMPパケット対して、PREROUTINGチェインでMARKターゲットを実行します。MARKターゲットを実行すると、ICMPパケットに100番の印がつけられます。そして、INPUTチェインでは、100番のパケットに対して、LOGターゲットを実行します。比較のため、INPUTチェインで200番のルールも作成しますが、200番のルールでは、ICMPパケットはLOGターゲットで処理されないことを確認します。

+--------------------------+
|                          |
|      INPUT chain         | 100番のパケットに対してLOGターゲットを実行します。
|                          |
+--------------------------+
           A
           |
           |
+--------------------------+
|                          |  ICMPパケットに対してMARKターゲットを実行します。
|      PREROUTING chain    |  MARKターゲットを実行すると、ICMPパケットに100番の印が付けられます。
|                          |
+--------------------------+
           A
           |
           |
       ICMPパケット

PREROUTINGチェインでICMPパケットに100番の印を付けるルールを作成します。

[root@server ~]# iptables -t mangle -I PREROUTING -p icmp -j MARK --set-mark 100

作成したルールを確認します。

[root@server ~]# iptables -t mangle -nvL PREROUTING --line-numbers
Chain PREROUTING (policy ACCEPT 21 packets, 1484 bytes)
num   pkts bytes target     prot opt in     out     source               destination
1        0     0 MARK       icmp --  *      *       0.0.0.0/0            0.0.0.0/0            MARK set 0x64

100番の印が付いたパケットをLOGターゲットで処理するルールをINPUTチェインに作成します。

[root@server ~]# iptables -I INPUT -m mark --mark 100 -j LOG

200番の印が付いたパケットをLOGターゲットで処理するルールをINPUTチェインに作成します。

[root@server ~]# iptables -I INPUT -m mark --mark 200 -j LOG

作成したルールを確認します。1番目のルールが200番(0xc8)の印が付いたパケットを処理するルール、2番のルールが100番(0x64)の印が付いたパケットを処理するルールです。

[root@server ~]# iptables -nvL INPUT --line-numbers
Chain INPUT (policy ACCEPT 5 packets, 364 bytes)
num   pkts bytes target     prot opt in     out     source               destination
1        0     0 LOG        all  --  *      *       0.0.0.0/0            0.0.0.0/0            mark match 0xc8 LOG flags 0 level 4
2        0     0 LOG        all  --  *      *       0.0.0.0/0            0.0.0.0/0            mark match 0x64 LOG flags 0 level 4

クライアントでpingコマンドを実行します。宛先はサーバを指定します。

[root@client ~]# ping -c 1 192.168.122.128

ルールを確認します。1番目のルールではなく、2番目のルールでパケット数が1増加していることがわかります。

[root@server ~]# iptables -nvL INPUT --line-numbers
Chain INPUT (policy ACCEPT 35 packets, 2492 bytes)
num   pkts bytes target     prot opt in     out     source               destination
1        0     0 LOG        all  --  *      *       0.0.0.0/0            0.0.0.0/0            mark match 0xc8 LOG flags 0 level 4
2        1    84 LOG        all  --  *      *       0.0.0.0/0            0.0.0.0/0            mark match 0x64 LOG flags 0 level 4

journalctlコマンドを実行します。100番の印が付いたパケットを受信していることがわかります。

[root@server ~]# journalctl -f
-snip-
Dec 14 20:51:49 server kernel: IN=eth0 OUT= MAC=52:54:00:78:10:73:52:54:00:8c:76:20:08:00 SRC=192.168.122.153 DST=192.168.122.128 LEN=84 TOS=0x00 PREC=0
x00 TTL=64 ID=46647 DF PROTO=ICMP TYPE=8 CODE=0 ID=1485 SEQ=1 MARK=0x64

10 TCPMSSターゲット

TCPヘッダに定義されているMSS(Maximum Segment Size)を変更するためのターゲットです。MSSはMTUからTCP/IPヘッダサイズを引いた値になります。通常、Ethernet環境ではMTUは1500(byte)です。また、TCP/IPの各々のヘッダサイズは20(byte)となるので、MSSは1460(byte)になります。しかし、最近ではTCP TimeStampオプション(12byte)が付くことが一般的なので、MSSは1448(byte)になります。

MSSを1000に変更するターゲットをPOSTROUTINGチェイン設定します。

[root@client ~]# iptables -t mangle -A POSTROUTING -p tcp --tcp-flags SYN SYN -o eth0 -j TCPMSS --set-mss 1000

作成したルールを確認します。POSTROUTINGチェインにTCPMSSターゲットが作成され、MSSが1000に設定されていることがわかります。

[root@client ~]# iptables -t mangle -nvL POSTROUTING --line-numbers
Chain POSTROUTING (policy ACCEPT 7 packets, 968 bytes)
num   pkts bytes target     prot opt in     out     source               destination
1        0     0 TCPMSS     tcp  --  *      eth0    0.0.0.0/0            0.0.0.0/0            tcp flags:0x02/0x02 TCPMSS set 1000

サーバとクライアントのTCPコネクション確立時のパケットのやり取りを確認するため、tcpdumpコマンドを実行します。

[root@server ~]# tcpdump -i eth0 port 11111 -nn

サーバでncコマンドを実行します。TCPの11111番ポートでncプロセスがListenするようにします。

[root@server ~]# nc -kl 11111

クライアントでncコマンドを実行します。宛先はサーバの11111番ポートを指定します。

[root@client ~]# nc 192.168.122.128 11111

tcpdumpコマンドの実行結果を確認します。MSSが1000に設定されたSYNパケットが送信されていることがわかります。

[root@server ~]# tcpdump -i eth0 port 11111 -nn
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on eth0, link-type EN10MB (Ethernet), capture size 262144 bytes
21:37:53.952382 IP 192.168.122.153.54832 > 192.168.122.128.11111: Flags [S], seq 1449643484, win 29200, options [mss 1000,sackOK,TS val 6401001 ecr 0,nop,wscale 7], length 0
21:37:53.952545 IP 192.168.122.128.11111 > 192.168.122.153.54832: Flags [S.], seq 3488042956, ack 1449643485, win 28960, options [mss 1460,sackOK,TS val 6405591 ecr 6401001,nop,wscale 7], length 0
21:37:53.953831 IP 192.168.122.153.54832 > 192.168.122.128.11111: Flags [.], ack 1, win 229, options [nop,nop,TS val 6401003 ecr 6405591], length 0

11 NOTRACKターゲット

コネクショントラッキングを実行しないターゲットです。

Z 参考情報

私が業務や記事執筆で参考にした書籍を以下のページに記載します。
Linux技術のスキルアップをしよう! - hana_shinのLinux技術ブログ