最低限のiptables
余計なことをわすれて、最低限必要なiptablesに絞って、使い方を書いてみた。
iptables は複雑で面倒くさい。
はい、そのとおりです。iptables は面倒くさいので、ufw が作られています。 ufw は iptables を目的別に管理してくれる人です。iptables を使うとありがちなミスや面倒な処理を ufw でやってくれるので、単なるポート別のアクセス制限ならufw を使ったほうが楽だし ufw 使うべきだと思います。
centos などには firewalld が用意されています。firewalld も iptables を自動化してくれるラッパーですが、個人的にはufw に比べてfirewalld は中途半端だと思います。
iptables が仕事をするところ
iptables はパケットの流れる箇所に仕込んで動作します。おもにフィルタや転送で使われる。ルーティングテーブルを通ってきたパケットをどうするかや、ルーティング前のパケットをどう処理するかなどに使う。
iptables で出来ること
家庭用のルーターにある機能がiptablesの機能だと思っておくと楽です。無線LANのAP機能はありません。
- ファイアウォール
- NAT( IP Masqurade )
- パケット判別機能
などが上げられます。
ルーティング機能
パケットはルーティングされる。iproute2 の ip-route機能で管理されるルーティング・テーブルに従ってパケットをルーティングを管理してくれる。
このルーティング機能にわりこんでフィルタを掛けるのが iptables と思えばだいたいわかると思う。 iptables 自体にルーティングテーブルの管理機能があるわけではないです。ルーティング・テーブルを管理する目的では ip-route を使います。。
パケットの流れ(IN・OUT)
インプットとアウトプットは、一番単純なパケットの流れ。
自分宛てに入ってきたパケットを書くポート番号のアプリケーションに渡す。
たとえば、次の図では、インプットアウトプットとプロセスとポートを示している。
ポート80で起動したApacheがにホストIP宛のパケットで80番宛のパケットが渡され、応答パケットはポート80から出ていく。
同様に、nginxが8080 で起動していても同じ。8080宛のパケットを受信したnginxが8080から応答パケットを返す。
一番良く使う IN/OUT のフィルタの場所。
iptablesはインプット・アウトプットのパケットにフィルタを設置する。これが一番良く使われる。理解もしやすい。
フィルタで全てを捨て、必要な分だけ通す設定を書く。
ネットに書かれている殆どの設定の例はこれ。
INPUT を全部捨てて、特定のプロセス宛を許可する例(SSH)
iptables -P INPUT DROP #1 iptables -A INPUT -p tcp --dport 22 -j ACCEPT #2
- 1: では、 INPUT のフィルタ設定し、全てデフォルト動作をは全て捨てる用にフィルタを設定。
- 2: では、 INPUTのフィルタ追加し、TCPパケットで、且つ、宛先ポート22番、これを通すように設定。
デフォルト動作を設定するポリシは、フィルタにマッチしないパケットをどうするかの基本設定。
他にも80を許可したり
iptables -P INPUT DROP iptables -A INPUT -p tcp --dport 22 -j ACCEPT iptables -A INPUT -p tcp --dport 80 -j ACCEPT
(わかりやすくポート番号だけを条件にしてる。
-p tcp
はナゼ必要?
これは、 「TCPパケットであること」を条件にする書き方。ナゼコレを書くのかというと。
ポート番号はTCPパケットに含まれる。そのためポートを知るには、tcp パケットの中身を調査しないとポートがわからない。SSHでTCP以外のパケットはありえないので、TCPを条件に含めてる。
iptablesでNICデバイスを指定する。
iptables がややこしいのは、IPレイヤでのフィルタだけど、NICデバイスで指定できる所。 IPパケット内容を見るとき、IPパケットが通ってきたネットワークカードを条件に追加できる。
もちろん必要なのだけど、初心者にはレイヤが混じるので理解できないかも??
iptables -A INPUT -i eth0 -p tcp --dport 22 -j ACCEPT
eth0 を通ってきたパケットで、 tcp パケットであり、 宛先ポートが22番は ACCEPT して受け入れる
通常は使わなくても済むのだけれど、パケットがVPN経由だったりPPPoE経由だったりをNICで識別できるので便利に使える。
混乱しそうなポイント
iptablesを触り始めたときに混乱しそうなポイント。
iptablesはパケットを監視して、パケットを書き換えられるのでなんでも出来てしまうので、ざっくりと調べてるだけだと混乱してしまう。
そこで、混乱しそうなポイントにちょっとだけ触れておく。
最初のうちは、INPUT・OUTPUTだけをアレコレ設定してcurl したり ping して慣れていけばいいと思いました。
NIC条件
ネットワークインタフェース(NIC)はIPパケットではないが、iptablesで条件として使える。
ルーティングテーブルの存在
ルーティングテーブルはパケット宛先の条件として作用するが、iptablesの条件とは関係ない。 パケットが届かないときは、ルーティングテーブルが間違ってるのか、iptablesが間違っててパケットが捨てられるのかを考える。
フィルタ条件とデフォルトポリシー
フィルタ条件は先に書いた方からマッチされる。 一方で、ポリシーはマッチしないときに適用される。
ポリシーは最初に書いて、後からフィルタを記述しても大丈夫。
ただしインタラクティブシェルで行ってる場合は、ポリシーは最後にしないといきなりポリシーが適用されてその後のフィルタをを追加できなくなる。
IN・OUTフィルタに全く関係ないポイント
IN・OUTでパケットフィルタするだけの場合、
NAT関連のフィルタは全く勉強する必要がない。 FOWARDも勉強する必要がない。 チェイン知らなくても使えるので気にしなくていい。
なぜか、iptablesでググると atmark IT のテンプレが上位に出てきてしまい、そのサイトでいきなりPREROUTING だの FILTERを同時に説明されるのでパニックになる。なので「知らなくても良いことは知らなくてもいい」と割り切ってしまったら楽になると思う。
ありがちなミス
デフォルトのパケットのポリシーを DROP にしておいて、パケットフィルタを初期化してしまう。
$ iptables -P INPUT DROP $ iptables -A INPUT -p tcp --dport 22 -j ACCEPT $ iptables -F # あっ
iptables -F でフィルタを初期化出来るので、ついつい iptables -F を打ってしまうが、デフォルトのポリシーが全部DROPなので、フィルタが無くなると、全てのパケットは破棄(drop)され、通信ができなくなってしまう。
ミスを防ぐ方法
iptables -L を実行したときの読み方を覚えておく。
takuya@ubuntu01:~$ sudo iptables -L Chain INPUT (policy DROP) target prot opt source destination ACCEPT tcp -- anywhere anywhere tcp dpt:ssh
この表示の で INPUT (policy DROP) に注目する。デフォルトポリシーがDROPでフィルタがACCEPTで構成されている。
そのため、フィルタにマッチしないとき、デフォルトポリシーにあたって、DROPされる。フィルタにマッチしたものは通過する。
ポリシーをACCEPTのまま構成する。
今の例の全く反対の条件で書くことが出来る。
すなわち、全てACCEPTポリシー〜指定ポート以外全部拒否する。
逆のポリシーとかも記述が可能
not 条件を使ったフィルタ
SSH以外のパケットを全て拒否し、否定条件を使って同じことをする。
sudo iptables -P INPUT ACCEPT sudo iptables -A INPUT -p tcp \! --dport 22 -j DROP
この場合は、iptables -L の結果が次のようになる。
takuya@ubuntu01:~$ sudo iptables -L Chain INPUT (policy ACCEPT) target prot opt source destination DROP tcp -- anywhere anywhere tcp dpt:!ssh
同じことを実現するやり方だが、否定条件とド・モルガン則を使って構成すると上記のような条件になったりする。
ポリシーはiptables -F をする前に確認した方がいいよ。
ポリシーを理解する
左側が、デフォルトACCEPT、指定以外全部拒否、右側がデフォルトDROP、指定許可 ポリシを使わずに、フィルタだけで構成するなら左側もありかもしれない。
ポリシーとフィルタ
ここまでの2例を使って、IN・OUTのポリシーとフィルタを理解が出来る。
以上が最初に覚えておくべき iptablesの設定でした。
組合せ。DROP条件を広く取って、指定ポートを追加した例。
ポリシーを使わずにポートを指定した例。
takuya@ubuntu01:~$ sudo iptables -L INPUT Chain INPUT (policy ACCEPT) target prot opt source destination ACCEPT tcp -- anywhere anywhere tcp dpt:http DROP tcp -- anywhere anywhere tcp dpt:!ssh takuya@ubuntu01:~$
DROP 以外の条件
REJECTを条件に書くと、CONECTION REFUSEDと即座に応答パケットが戻ってくるので、通信は届いていて、iptablesがダメ出ししたことが解るので、設定を作ってるときに、他の設定の切り分けに使えるので意外と使ってる。
REJECT を設定した例。
サーバー側で reject を設定した例
takuya@~$ ssh 192.168.56.101 takuya@ubuntu01:~$ sudo iptables -L INPUT Chain INPUT (policy ACCEPT) target prot opt source destination REJECT tcp -- anywhere anywhere tcp dpt:!ssh reject-with icmp-port-unreachable takuya@ubuntu01:~$
クライアントからパケットを送って接続を試みると、即座に応答パケットとしてREJECTが返ってくるので、わかりやすい。
takuya@~$ curl 192.168.56.101 curl: (7) Failed to connect to 192.168.56.101 port 80: Connection refused