それマグで!

知識はカップより、マグでゆっくり頂きます。 takuya_1stのブログ

習慣に早くから配慮した者は、 おそらく人生の実りも大きい。

iptables の使い方 その1 ちょっとだけ理解して使うiptables

最低限のiptables 

余計なことをわすれて、最低限必要なiptablesに絞って、使い方を書いてみた。

iptables は複雑で面倒くさい。

はい、そのとおりです。iptables は面倒くさいので、ufw が作られています。 ufwiptables を目的別に管理してくれる人です。iptables を使うとありがちなミスや面倒な処理を ufw でやってくれるので、単なるポート別のアクセス制限ならufw を使ったほうが楽だし ufw 使うべきだと思います。

centos などには firewalld が用意されています。firewalld も iptables を自動化してくれるラッパーですが、個人的にはufw に比べてfirewalld は中途半端だと思います。

iptables が仕事をするところ

iptables はパケットの流れる箇所に仕込んで動作します。おもにフィルタや転送で使われる。ルーティングテーブルを通ってきたパケットをどうするかや、ルーティング前のパケットをどう処理するかなどに使う。

iptables で出来ること

家庭用のルーターにある機能がiptablesの機能だと思っておくと楽です。無線LANのAP機能はありません。

などが上げられます。

ルーティング機能

パケットはルーティングされる。iproute2 の ip-route機能で管理されるルーティング・テーブルに従ってパケットをルーティングを管理してくれる。

このルーティング機能にわりこんでフィルタを掛けるのが iptables と思えばだいたいわかると思う。 iptables 自体にルーティングテーブルの管理機能があるわけではないです。ルーティング・テーブルを管理する目的では ip-route を使います。。

パケットの流れ(IN・OUT)

インプットとアウトプットは、一番単純なパケットの流れ。

自分宛てに入ってきたパケットを書くポート番号のアプリケーションに渡す。

たとえば、次の図では、インプットアウトプットとプロセスとポートを示している。

ポート80で起動したApacheがにホストIP宛のパケットで80番宛のパケットが渡され、応答パケットはポート80から出ていく。

同様に、nginxが8080 で起動していても同じ。8080宛のパケットを受信したnginxが8080から応答パケットを返す。

f:id:takuya_1st:20180103205555p:plain

一番良く使う IN/OUT のフィルタの場所。

iptablesはインプット・アウトプットのパケットにフィルタを設置する。これが一番良く使われる。理解もしやすい。

フィルタで全てを捨て、必要な分だけ通す設定を書く。

ネットに書かれている殆どの設定の例はこれ。

f:id:takuya_1st:20180103205551p:plain

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 パケットの中身を調査しないとポートがわからない。SSHTCP以外のパケットはありえないので、TCPを条件に含めてる。

iptablesNICバイスを指定する。

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、指定許可 ポリシを使わずに、フィルタだけで構成するなら左側もありかもしれない。

f:id:takuya_1st:20180103205559p:plain

ポリシーとフィルタ

ここまでの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 以外の条件

DROPする以外にも選択肢がある。それがREJECT

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