2012/05/09

VPSで使えそうなiptables補助スクリプト

仕事でVPSを触る・構築する機会が多く、それらの殆どが単体構成のサーバとしてセットアップしています。用途はDNSであったりMTAであったり、あるいはごく小規模のWebサーバであったりして、ホスト毎にFirewallを毎回設定する必要があります。

最近はBSD系OSを選べるVPSも増えてきていますが、それでもやはりLinuxである事が多く、構築毎に育ってきたFirewall(iptables)の設定スクリプトを公開というか紹介します。

この手のスクリプトは探せばもっと出来のよいものが沢山あると思いますが、最初に軽い気持ちで作った設定スクリプトがちょいちょい手を加えているうちにわりと育ってきました。

WAN側一個のネットワークインターフェイスのみFirewallを立てる、わりと最近のLinuxディストリビューションでiptables(IPv4)を使う、それほど複雑なポリシーを構築する必要がない、というケースで役に立つかもしれない気がします。僕も普段の案件で常用していますので、ブログに書こうかなと思った次第です。

ダウンロードとインストール

中身はシェルスクリプトのfirewall-scriptsという名前のリポジトリでgithubに置いています。適当なディレクトリでgit cloneして、make installすると/etc/firewall以下に必要なファイルがばらまかれます。
# cd /usr/local/src⏎
# git clone git://github.com/azumakuniyuki/firewall-scripts.git⏎
Cloning into firewall-scripts...
...
# cd ./firewall-scripts⏎
# make install⏎
test -d /etc/firewall || mkdir -pm 0700 /etc/firewall
...
./SETUPというファイルに手順が書いていますので、その通りにコマンドを入れるとよいです。インストール先は安易に/etc/firewallとしていますが、他の奴が使ってる、名前が嫌とかであればMakefileのFWDIRマクロを書き換えて下さい。

その他、細かい設定はスクリプト本体(make-linux-firewall)のソースを読めば分かると思いますが、ブログなのでちゃんと説明書きます。

設定

自分のIPアドレスを設定(my-ipv4-address)

必要最低限の設定は/etc/firewall/my-ipv4-addressにFirewallを立てるWAN側I/FのIPアドレスを入れるだけです。サーバのIPアドレスが192.0.2.25であれば、下記のようにIPアドレスだけを書き込みます。

# $Id: my-ipv4-addreess,v 1.1 2010-10-24 02:29:00 ak Exp $
192.0.2.25

今ログインしている所のIPアドレスを設定(trusted-hosts)

今SSHでVPSにログインしている、あるいはSSHでログインする予定の発信元IPアドレスを /etc/firewall/trusted-hosts に書き込みます。今自分のいる場所のWAN側IPアドレスが192.0.2.254であれば、それを書き込むだけです。

# $Id: trusted-hosts,v 1.3 2010-10-07 02:29:23 ak Exp $
# 192.0.2.25  # gw.kyoto.example.jp/Kyoto, Japan
# 192.0.2.29  # gw.wakkanai.example.jp/Hokkaido, Japan
# 192.0.2.129  # gw.dallas.example.com/TX, U.S.A.
# 172.16.0.0/24  # internal network
192.0.2.254  # 会社

保険をかける

遠隔地のFirewallを変更するのは何回やってもドキドキするものです。万が一しくじってSSHでログインできなくなると途方に暮れたり、有償で設定をリセットしてもらったりしなくてはならない事もあるので、心配な時は保険をかけます。

# cd /etc/firewall⏎
# sh ./make-linux-firewall hoken⏎
# Run 'crontab -e' and paste the following lines:
# Make iptables open 6 minutes later
59 23 09 05 * root /etc/firewall/make-linux-iptables open

6分後に全てのパケットを通過させるルールが適用されるcrontabエントリが表示されますので、これをcrontabに書いておくと安心です。

Firewallを有効にする

my-ipv4-addressとtrusted-hostsに必要事項を書いて保険にも入ったら、祈りながら下記のコマンドを実行するとFirewallに設定したルールが適用されます。

# cd /etc/firewall⏎
# sh ./make-linux-firewall wall⏎
# sh ./make-linux-firewall list⏎
Chain INPUT (policy DROP)
num  target     prot opt source               destination         
1    ACCEPT     all  --  0.0.0.0/0            0.0.0.0/0 
...
Chain FORWARD (policy DROP)
num  target     prot opt source               destination         

Chain OUTPUT (policy ACCEPT)
num  target     prot opt source               destination         
1    ACCEPT     all  --  0.0.0.0/0            0.0.0.0/0 
...

適用直後にSSH接続が切れてログインも出来なくなった場合は6分後に来る保険の効果を待つとよいでしょう。意図した通りにiptablesが構成されたらcrontabに入れた保険を解約するのを忘れずに。

もうちょい細かい設定

同じようなポリシーのFirewallをいくつものVPSに構築するといえども、ホストによって微妙に異なる部分はある程度対応できるようになっています。 主に設定ファイルの変数を書き換える、ホストグループ毎にIPアドレスを列挙する事によってそれが可能です。

ポリシーの設定(firewall-rules)

ポリシーを個別に設定します。中身は変数を設定しているだけのシェルスクリプトです。このファイルを何も編集せずにiptablesのルールを生成、適用した場合はSSH(22), DNS(53), SNMP(161), SMTP(25,587), POP3(110), IMAP4(143), IMAP4 over SSL(993), POP3 over SSL(995), HTTP(80) を通過させるルールがよしなに生成されます。

下記に列挙する、基本的なポリシーの動作を決定する変数が幾つかあります。

ALLOW_ANY_CONNECTION_FROM_TRUSTEDHOSTS

1に設定すると ./trusted-hostsファイルに書いたIPアドレスから全てのパケットを通過させます。ちゃんとしたネットワーク内における同一セグメントみたいな扱いをしてもよいホストを想定しています。既定値は0です。

ALLOW_ANY_CONNECTION_FROM_EXTERNALROOT

1に設定すると ./extroot-hostsファイルに書いたIPアドレスから全てのパケットを通過させます。組織外にSSHで入ってくるユーザがいるケースを想定しています。既定値は0です。

ALLOW_ANY_CONNECTION_FROM_MONITORHOSTS

1に設定すると monitor-hostsファイルに書いたIPアドレスから全てのパケットを通過させます。監視サーバから何でも来いな状態にします。既定値は0です。

ALLOW_SSH_CONNECTION_FROM_WORLDWIDENET

1に設定すると、SSH接続はどこからでもOKな状態になります。SSHでログインする人が固定IPアドレス以外から入ってくるから穴を開ける必要があるケースを想定しています。既定値は0です。

ALLOW_FTP_CONNECTION_FROM_WORLDWIDENET

1に設定すると、FTP接続はどこからでもOKな状態になります。SFTPが使えなくていろんな所からFTPで繋ぐ必要がある人がいて、仕方なく穴を開けるケースを想定しています。既定値は0です。

DENY_ANY_CONNECTION_FROM_ATTACKERHOSTS

1に設定すると ./attacker-hostsファイルに書いたIPアドレスからのパケット全てを拒否します。なぜかサーバが攻撃されて困っているケースを想定しています。既定値は0です。

firewall-rulesのサンプル

大文字の変数は基本的なFirewallのポリシーを決定する変数です。port_*はよく使われるであろうサービスのポートを定義しています。port_*をコメントアウトすると、そのポートへの接続ルールは生成されません。

port_*を有効にすると、末尾のコメント部分に書いているホストグループからの接続に限定して、接続を許可するルールが生成されます。例えばSSH(port_opensshd)であれば、TXと書いているので、trusted-hostsとextroot-hostsに列挙されたIPアドレスからの接続が許可されます。


ホストグループ用のファイル

スクリプトが生成するポリシーには、ホストグループのようなものがあって、それぞれどの程度サービスへの接続を許可するかとか、そう言った事を区別できるようになってます。

あくまで簡易な、そしてiptablesのルール生成のための補助スクリプトなので、厳格な設計には不向きですが、ある程度の用途に於いては有用です。

IPアドレスの記述は、/sbin/iptablesにそのまま渡しているので、iptablesが解釈できる形式であればどんな形式でもOKです。

trusted-hosts

trustedの名の通り、常に信頼してもよいホストのIPアドレスを列挙します。リモートログインの基点となるIPアドレスを入れるとよいでしょう。

変数ALLOW_ANY_CONNECTION_FROM_TRUSTEDHOSTS=1を設定した場合はこのファイルに列挙したIPアドレスからのパケットを全て通過させます。 firewall-rulesファイルの各ポート定義の末尾コメント部分にTの文字があるサービスが対象になります。

monitor-hosts

監視サーバのIPアドレスを入れる事を想定しています。このファイルに列挙したIPアドレスからのSNMP(161)やMunin(4949), Zabbix(10050,10051)等の監視系サービスへのパケットを通過させます。

ALLOW_ANY_CONNECTION_FROM_MONITORHOSTS=1を設定した場合は、このファイルに列挙したIPアドレスからの全てのパケットを通過させます。 firewall-rulesファイルの各ポート定義の末尾コメント部分にMの文字があるサービスが対象になります。

extroot-hosts

共同管理しているサーバで、VPN環境がない・使えないケースではいろんな所からSSHでログインする事になります。このファイルには、サーバの主たる管理者以外の人の基点となるIPアドレスを入れる事を想定しています。

ALLOW_ANY_CONNECTION_FROM_EXTERNALROOT=1を設定した場合は、このファイルに書いているIPアドレスからの全てのパケットを通過させます。firewall-rules ファイルの各ポート定義の末尾コメント部分にXの文字があるサービスが対象になります。

internal-hosts

Linodeのように、最近のVPSでは同じデータセンター内にあるVPS同士をプライベートアドレスのネットワークで接続する事ができるようです。このファイルにIPアドレスを列挙すると、発信元IPアドレスがこのファイルに書いたIPアドレス(かネットワーク)で宛先IPアドレスがmy-ipv4-addressでないパケットを全て通過させるルールが生成されます。

belogged-hosts

このファイルに列挙したIPアドレスが発信元または宛先であるパケットを全てログに記録します。パケットの通過・拒否等のルールは生成しません。

spammer-hosts

スパムを送ってくる、投稿するホストを列挙する事を想定しています。このファイルに書いたIPアドレスからのSMTP(25), HTTP(80), HTTPS(80)への接続を拒否します。

attacker-hosts

侵入目的で絨毯爆撃のようにログインを試行してくるようなホストを列挙する事を想定しています。このファイルに列挙したIPアドレスからのSSHやFTPなどログインを必要とするサービスへの接続を拒否します。

DENY_ANY_CONNECTION_FROM_ATTACKERHOSTS=1を設定した場合は、このファイルに書いているIPアドレスからの全てのパケットを落とします。

例外的なルールと処理の記述(other-commands)

05/11追記:

IIJ GIOのVPSは、記憶している限り初期状態で幾つかのiptablesのルールが入っています。そう言った場合は最初からあるルールに自分のルールを結合するのが望ましいので、./other-commandsファイルが役に立ちます。

./other-commands ファイルは単なるシェルスクリプトで、スクリプト本体から処理の一番最後に呼び出されて実行されます。最初から入っていたルールはこのファイルにシェルスクリプトとして実行できる形で書き写しておくとよいです。また、シェルスクリプトですのでiptables以外の処理を入れてもよいでしょう。

コマンドとオプション

スクリプトmake-linux-firewallは、設定ファイルfirewall-rulesを読み込んで、上記のホストグループファイルのIPアドレス毎のルールをよしなに生成します。 サブコマンドを何も付けない、またはhelpを入れると下記の画面が表示されます。
# sh /etc/firewall/make-linux-iptables help⏎
Usage: 
 /etc/firewall/make-linux-iptables [ OPTION ] COMMAND

  OPTION: Overrides the value defined in /etc/firewall/firewall-rules
   -a         : Deny any connection from hosts listed in /etc/firewall/attacker-hosts
   -m         : Allow any connection from hosts listed in /etc/firewall/monitor-hosts
   -t         : Allow any connection from hosts listed in /etc/firewall/trusted-hosts
   -x         : Allow any connection from hosts listed in /etc/firewall/extroot-hosts

  COMMAND:
   help       : Print this screen
   open       : Open firewall widely: Accept any connection
   wall       : Set firewall: Execute firewall commands in this script
   list       : Print firewall rule list
   dump       : Print the results of iptables-save
   insurance  : Print crontab entry for firewall configuration failure
   version    : Print version

open

openを実行すると全てのパケットを通過させるルールが適用されます。

wall

wallサブコマンドは、firewall-rulesの内容を読み込み、ホストグループファイル毎に接続の許可・拒否のルールを生成します。

list

listサブコマンドは、iptables -n -L --line-numbersのラッパーです。

dump

dumpサブコマンドは、iptables-saveコマンドのラッパーです。

insurance

insuranceサブコマンドは、6分後にopenを実行するcrontabのエントリを出力します。遠くにあるVPSでfirewallの設定を変更する時、失敗しても6分後に全ての通信が許可されるルールに変更されるので、保険としての役割を果たします。

内部ではPerlとTime::Pieceモジュールを使っています。もしもTime::Pieceがない場合はdateコマンドで6分後を計算しますが、コマンド実行時刻が23時59分であった場合、同じ日の23:65と出力されるぐらい雑な実装です。
# sh make-linux-firewall insurance⏎
# Run 'crontab -e' and paste the following lines:
# Make iptables open 6 minutes later
59 23 09 05 * root /etc/make-linux-iptables open
insuranceの代わりにhokenと打ってもOKです。

オプション(-a, -m, -t, -x)

オプションを付けて実行すると、設定ファイルfirewall-rulesの先頭に定義している変数を上書きした状態でFirewallのルールが適用されます。

自動的に起動する

firewall-scriptsはiptablesのルールを生成して適用する補助コマンドです。スクリプトはUbuntuとCentOSで動作を確認しています。僕は実際に運用でも使っていますが、システム起動時にiptablesを有効にするために、このスクリプトを呼び出してもよいですし、iptables saveとしてもよいでしょう。
# /etc/rc.d/init.d/iptables save⏎
Linux(CentOS)の起動スクリプトがある場合は、現在のルールを保存しておく。

# echo '/etc/firewall/make-linux-iptables wall' >> /etc/rc.local⏎
/etc/rc.dにiptablesの起動スクリプトがない場合は、/etc/rc.localに書いておく。

もっと細かく柔軟な設定

このスクリプトはあくまでVPS上で単体動作するLinuxの90%ぐらいをカバーする用途ですので、少し設定を変えたい場合はスクリプト本体を書き換える、更に細かい設定が必要な場合は、dumpサブコマンドで出てくるルールを書き換えるか、スクラッチでルールを書くとよいでしょう。

0 件のコメント:

コメントを投稿