2010年5月15日土曜日

ServersMan@VPSでのiptables設定(state版)

ServersMan@VPSでのiptablesがstateに正式対応したようなので、以前の改訂版としてstateを使用したルールの書き方をまとめる。

(注意) stateに対応したという情報が流れた後に一時的に「stateの文法が通るようになったが実際は動作しない」という時期があった。5/12日現在においては、一旦rebootすることで正常にstateが動作するようになるので、この記事の内容を試す前に一旦rebootすることをおすすめする。



これからやること
まず、簡単にstateを利用したルールの作り方を書くと、
  1. ESTABLISHED, RELATEDというstateな通信を許可するルールを設定
  2. その後ろにサービス提供したいport宛のNEWというstateな通信を許可するルールを設定
  3. 最後にすべてDROPする
となる。1で設定するルールによってESTABLISHEDなtcpコネクションやDNSの返信パケットなどは自動的に許可されるので、設定する人間は2のルールを決めるためのポリシーを考えるだけでよい。

失敗すると怖いので...
さて、ServersMan@VPSはroot権限はあるが、リモート環境(ssh等でアクセスする必要がある)であり、しかもコンソールアクセスが提供されているわけでもないので、iptablesの設定に失敗してsshでアクセスできなくなったりすると、サポートにお願いしてiptables設定を解除してもらう必要がある。こうなってしまうと少なくとも1日は無駄にしてしまうので、可能な限りiptables設定の失敗は避けたい。

そこで、一旦安全なルールを設定し、stateによる動作を確認した後に厳密なルールを投入するという方法を解説する。(完璧じゃぁないが、だいぶ失敗は防げると思う)

ポリシー決定
提供したいサービスをもとに許可ポリシーを決定する。例として、Webサーバとメールサーバをたてて運用する場合を考えると、以下のようなポリシーになる。
  1. HTTP, HTTPS: 不特定多数の人にWebサーバを公開するつもりなので、すべてのIPアドレスからの接続を許可する。
  2. SMTP: 自分のドメインのメールを受け取るメールサーバにするつもりなので、すべてのIPアドレスからの接続を許可する。
  3. SSH: 管理用に必須なので許可する。自宅、会社、出先などいろいろなところからアクセスする必要があるので、すべてのIPアドレスからの接続を許可する
  4. 前回の解説にも書いたが、ローカル通信は当然許可する。また、icmpも許可。ident(auth)はtcp resetで拒否、その他のすべての通信はDROPで拒否する。

ポリシーから動作確認用ルールを作成
上記のポリシーからiptablesのルールを作成すると以下のようになる。
# これは確認用のルール(結局全部の通信を許可するルール)なので注意すること
*filter
:INPUT ACCEPT [0:0]
:FORWARD ACCEPT [0:0]
:OUTPUT ACCEPT [0:0]
-N MYCHAIN
-A MYCHAIN -m state --state ESTABLISHED,RELATED -j ACCEPT
-A MYCHAIN -p icmp -j ACCEPT
-A MYCHAIN -s 127.0.0.0/8 -d 127.0.0.0/8 -j ACCEPT
-A MYCHAIN -m state --state NEW -m tcp -p tcp --dport 22 -j ACCEPT
-A MYCHAIN -m state --state NEW -m tcp -p tcp --dport 25 -j ACCEPT
-A MYCHAIN -m state --state NEW -m tcp -p tcp --dport 80 -j ACCEPT
-A MYCHAIN -m state --state NEW -m tcp -p tcp --dport 443 -j ACCEPT
-A MYCHAIN -m tcp -p tcp --dport 113 -j REJECT --reject-with tcp-reset
-A MYCHAIN -j ACCEPT
-A INPUT -j MYCHAIN
COMMIT
view raw gistfile1.txt hosted with ❤ by GitHub

これは /etc/sysconfig/iptables にそのまま使える形式(iptables-save(8)の出力)で書いてある。

1行目〜4行目はおまじない。

5行目はMYCHAINという名前のチェイン(ルールの集合)を作成している。こうしておくことで作成したルールの再利用性が高まる。(あんまり再利用する機会もないけど)

6行目は前述の「ESTABLISHED, RELATEDというstateな通信を許可するルールを設定」だ。

7行目はicmpの許可

8行目はローカル通信の許可

9〜12行目は各種提供サービスの許可。ここで --state NEW としているが、これは接続開始要求のパケット(SYNパケット)のみ許可するという意味になる。--state NEW をつけない場合は、SYNフラグがたっていないパケット(ESTABLISHEDを偽装したパケット等)を受け付けることになるので --state NEW をつけた方がよい。

13行目はident宛の通信を拒否している。ただし、DROPのように無視するのではなく、tcp reset を送信して拒否することを明示的に相手に知らせている。詳しくは前回の記事参照。

14行目は本来DROPとするはずだが、動作確認用にACCEPTにしている。こうしておけば仮に上のルールが間違っていたとしても完全にロックアウトされることはない。動作確認の方法については後述。

15行目はINPUTチェインですべてのパケットをMYCHAINチェインに飛ばしている。

16行目はおまじない。

動作確認
上記の設定をファイルに落としたら、iptablesのサービスが再起動時に自動的にあがらないようにしたうえで、iptables-restore(8)を使ってルールを反映させる。
iptables.txt というファイルにセーブしているとすると、以下のようなコマンドになる。
root@bangasa# chkconfig iptables off
root@bangasa# chkconfig iptables --list
iptables 0:off 1:off 2:off 3:off 4:off 5:off 6:off
root@bangasa# cat iptables.txt | iptables-restore
view raw gistfile1.txt hosted with ❤ by GitHub

無事通信が継続できたら反映されたルールを確認する。
もしロックアウトされてしまった場合はServersManのアカウントのページからrebootさせればよい。iptablesサービスをoffにしておけば、ルールが勝手にセーブされたり起動時に間違ったルールが自動的に適用されることを防げるはずだ。
なお、VPSサーバからDNSを引けない場合、SSHでアクセスしたときにログインするまでに時間がかかることがあるので、SSHでアクセスして反応がなくてもしばらく待ってみた方が良い。
ルールの確認は /etc/init.d/iptables status でもいいが、ここでは iptables -L -nv を使う。このコマンドを使うことで、どのルールにどれだけのパケットがマッチしたかを確認することができるからだ。
root@bangasa# iptables -L -nv
Chain INPUT (policy ACCEPT 0 packets, 0 bytes)
pkts bytes target prot opt in out source destination
211 24430 MYCHAIN all -- * * 0.0.0.0/0 0.0.0.0/0
Chain FORWARD (policy ACCEPT 0 packets, 0 bytes)
pkts bytes target prot opt in out source destination
Chain OUTPUT (policy ACCEPT 194 packets, 30144 bytes)
pkts bytes target prot opt in out source destination
Chain MYCHAIN (1 references)
pkts bytes target prot opt in out source destination
207 24214 ACCEPT all -- * * 0.0.0.0/0 0.0.0.0/0 state RELATED,ESTABLISHED
0 0 ACCEPT icmp -- * * 0.0.0.0/0 0.0.0.0/0
0 0 ACCEPT all -- * * 127.0.0.0/8 127.0.0.0/8
4 216 ACCEPT tcp -- * * 0.0.0.0/0 0.0.0.0/0 state NEW tcp dpt:22
0 0 ACCEPT tcp -- * * 0.0.0.0/0 0.0.0.0/0 state NEW tcp dpt:25
0 0 ACCEPT tcp -- * * 0.0.0.0/0 0.0.0.0/0 state NEW tcp dpt:80
0 0 ACCEPT tcp -- * * 0.0.0.0/0 0.0.0.0/0 state NEW tcp dpt:443
0 0 REJECT tcp -- * * 0.0.0.0/0 0.0.0.0/0 tcp dpt:113 reject-with tcp-reset
0 0 ACCEPT all -- * * 0.0.0.0/0 0.0.0.0/0
view raw gistfile1.txt hosted with ❤ by GitHub

ここで、ESTABLISHED のルールと SSHのルール(dpt:22)の行の一番左のカラム(pkts)の数値をみると、カウントアップされていることがわかる。この数値はそのルールにマッチしたパケットの数を示している。もし許可したルールに該当するはずの通信を行っても、ここがカウントアップされていない場合は、そのルールにマッチしていない(=何かが間違っている)ということなので、ルールを見直す。
例えばSSHを許可しているが、新たにSSHで接続しても最後の全ACCEPTのカウントばかりあがっていくようならば、最後のACCEPTをこのあとDROPに変更したときに通信できなくなってしまうはずだ。

意図した通りに動くようになるまで、ルール作成と動作確認を繰り返す。

動作確認用ルールから厳密なルールを作成
これは簡単。最後のACCEPTをDROPに変更するだけだ。
*filter
:INPUT ACCEPT [0:0]
:FORWARD ACCEPT [0:0]
:OUTPUT ACCEPT [0:0]
-N MYCHAIN
-A MYCHAIN -m state --state ESTABLISHED,RELATED -j ACCEPT
-A MYCHAIN -p icmp -j ACCEPT
-A MYCHAIN -s 127.0.0.0/8 -d 127.0.0.0/8 -j ACCEPT
-A MYCHAIN -m state --state NEW -m tcp -p tcp --dport 22 -j ACCEPT
-A MYCHAIN -m state --state NEW -m tcp -p tcp --dport 25 -j ACCEPT
-A MYCHAIN -m state --state NEW -m tcp -p tcp --dport 80 -j ACCEPT
-A MYCHAIN -m state --state NEW -m tcp -p tcp --dport 443 -j ACCEPT
-A MYCHAIN -m tcp -p tcp --dport 113 -j REJECT --reject-with tcp-reset
-A MYCHAIN -j DROP
-A INPUT -j MYCHAIN
COMMIT
view raw gistfile1.txt hosted with ❤ by GitHub


ルール投入
前述の用に iptables-restore  でルールを投入し、問題なければ内容を /etc/sysconfig/iptables に保存し、iptablesサービスを有効化する。
root@bangasa# cp iptables.txt /etc/sysconfig/iptables
root@bangasa# chkconfig iptables on
root@bangasa# /etc/init.d/iptables start
Flushing firewall rules: [ OK ]
Setting chains to policy ACCEPT: mangle filter [ OK ]
Unloading iptables modules: [ OK ]
Applying iptables firewall rules: [ OK ]
view raw gistfile1.txt hosted with ❤ by GitHub


まとめ
今回の要点は以下の通り。
  1. stateをつかったルールは、「ESTABLISHED,  RELATED許可」→「提供サービスのNEWを許可」→「ほかはDROP」が基本形
  2. iptables -L -nv で意図したルールにマッチしているか確認してから最後のDROPを有効化する
コメント、突っ込み等あればお願いします。

4 件のコメント:

  1. 通りすがりです。

    SM@VPSでstateが使えるようになったってマジですか!
    サービスイン初期の時点でそれに気づいて運営へ問い合わせたんですが、「機能提供してません」の一点張りで愕然としてました。
    再起動すれば使えるようになっているとのことなので、試してみます。

    返信削除
  2. マジです。要望が多かったので対応したみたいです。
    http://twitter.com/serversman_vps/status/12294223127
    http://twitter.com/serversman_vps/status/12554243240
    http://dream.jp/vps/faq_08.html
    ぜひ試してみてください。この記事が参考になれば幸いです。

    返信削除
  3. ありがとうございました。このまま参考にしました。本当は,そのような(私のような)レベルの者がServersMan@VPSに手を出すべきではないのかもしれませんが,単なる興味本位で手を出しているのではなく,やりたいことがあるのです。人はそれぞれ専門分野が違うので,門外漢の敷居を下げてくれる情報提供に感謝します。

    返信削除
  4. コメントどうもです。
    この記事があなたのやりたいことの役に立ったのであれば私も嬉しく思います。

    返信削除