FreeBSD でグローバルネットワークへ接続するマシンを仕立てる場合に参考になると良いな、という情報を書いておきます。

備忘録という話もありますが。

梅本さんよりアドヴァイスあり。というか、「いっぱい間違いあるよ」……ごめんなさい。指摘の通りに書き換えてみました。ありがとうございました。

酔った頭で聞いていたので抜けがあるかも知れません。その場合は是非是非指摘プリーズ。

というか、読んでいるとは思いませんでした……って、この間は今野さんに言ったような気が。(^^;

ANNOUNCE メーリングリストへ加入する

サーバに限りませんが、グローバルネットワークへ接続するマシンは、セキュリティ関係についてある程度敏感である必要があります。

FreeBSD ANNOUNCE-jp に加入している場合、FreeBSD 本体以外にも、ports に収録されているアプリケーションのセキュリティホールに関する情報なども流れますので、自分が使っているアプリケーションのセキュリティホールに関する情報を得るためにも、最低限加入しておきましょう。

計画設計

計画と言っても簡単なもので、単に外部へ対してどのようなサービスを提供するか、という辺りです。

これによって外部へ解放するポート番号が変動しますが、サービスが無い場合には全て蹴っても良いわけですので、その場合は一般的なクライアントに適用される情報となります。

ここでは、次に挙げるサービスを外部へ公開することとします。

WWW サーバ
Apache 1.3.20 を利用。
SSH サーバ
外部からのメンテナンス用。外部からのメンテナンスを行わない場合には塞ぎます。/etc/rc.conf から sshd_enable="YES" で。
SMTP サーバ
外部からメールを受け取る場合に必要。独自ドメインなどを取っていてメールを受け取れるようにしたい場合には必須。Sendmail なり Postfix なり qmail なり、お好きなものを。
POP3 サーバ
外部から POP3 でメールを取り出したい場合に必要。外部向けの場合は、最低限 APOP 対応のものにしておきましょう。
IMAP4 サーバ
外部から IMAP4 でメールを取り出したい場合に必要。
DNS サーバ
プライマリ/セカンダリ DNS を立てたりする場合に必要。外部でサービスできる場合、特に必要ありません。IPv6 を考えるなら BIND9 をどうぞ。

内部ネットワークに対しては、ここでは信頼して何でも通過させることとします。信頼できないホストがいる場合には、まずそれを弾いてから内部ネットワーク全てのホストへ全サービスを許可させる、などとすると良いでしょう。

準備

ここでは、FreeBSD 標準の ipfw を利用して防火壁を作ることとします。ipfw を利用する為に kernel を作り直すこととします。

以下の設定を kernel 設定に追加します。

  • options IPDIVERT
  • options IPFIREWALL
  • options IPFIREWALL_VERBOSE
  • options IPFIREWALL_VERBOSE_LIMIT=100
  • options IPV6FIREWALL
  • options IPV6FIREWALL_VERBOSE
  • options IPV6FIREWALL_VERBOSE_LIMIT=100
  • options IPSEC
  • options IPSEC_ESP
  • options TCP_DROP_SYNFIN

IPDIVERT は、NAT を利用しない場合には不要です。

IPv4 のみの場合には、IPV6* は不要です。

IP*FIREWALL_DEFAULT_TO_ACCEPT は起動直後で ipfw/ip6fw が起動する前のタイミングに全て受け付けてしまうなどの弱点を作ることになります。設定しないようにした方が良いかもしれません。

IP*FIREWALL_VERBOSE は、ログ取りする場合には入れておきましょう。攻撃があった際などに対処しやすいように、ログを取っておいた方がいいと思った場合にはいれておきましょう。

なお、LIMIT で 100 となっている場合には 1 つのルール辺り 100 行までログを取れますが、最近はちょこちょこと攻撃があるため、私は 1000 に設定しています。ipfw zero <ルール番号> でログの行カウンタをリセットできますので、こまめにリセットされる、という方は少なめでも構いません。

IPSec 関係はオマジナイです。:-) 相手が対応していないと意味がありませんが、対応していれば自動的にパケットが IPSec で保護されますので入れておきましょう。

TCP_DROP_SYNFIN は、SYN flood 攻撃対策を行うものです。標準で on になっていませんので、on にしておくと良いでしょう。

現行の FreeBSD 4.4-RELEASE では /etc/rc.conf で以下の設定を行うことで firewall を利用できます。この場合には、kernel option で options IPFIREWALLoptions IPFIREWALL_VERBOSEoptions IPFIREWALL_VERBOSE_LIMIT=100 などは不要になります。

options IPFIREWALLfirewall_enable="YES" に置き換えることができます。この指定がある場合には自動的にモジュールが読み込まれます。

options IPFIREWALL_VERBOSEfirewall_logging="YES" に置き換えることができます。この指定がある場合には、自動的にカーネル設定が変更されます。

options IPFIREWALL_VERBOSE_LIMIT=100 はカーネル設定 net.inet.ip.fw.verbose_limit の値で指定できます。2001-10-20T00:09:39+09:00 現在は rc.conf からは設定できないようです。値を変更したい場合には # sysctl -w net.inet.ip.fw.verbose_limit=1000 などとしましょう。

IPv6 対応物に関しては、/etc/rc.conf の設定では頭に ipv6_ を付けるだけです。sysctl の場合には net.inet6.ip6.* に変わります。IPv6 関係の設定値に関しては sysctl -a | grep inet6 で見つけられます。

IPSec や IPDIVERT が不要な場合には kernel の再構築は不要です。

あとは kernel を build してください。なお、この kernel で設定した場合、/etc/rc.conf で firewall type を open にしておかないと外部と通信できない状態になってしまってはまる場合があるため、必ずコンソールの前で作業しましょう。

ファイアウォールのルール定義

IPv4 ベースのファイアウォールは ipfw コマンドで定義します。まず、/etc/rc.conf を編集します。

gateway_enable="YES"
ルータとして動作させない場合には YES を設定する必要はありません。
firewall_enable="YES"
ファイアウォールを有効にします。グローバルネットワークに置かれる環境は常に YES にしましょう。なお、内部ネットワークに信頼できないホストがあったりする場合にも YES にして制御するのが良いでしょう。
firewall_quiet="YES"
設定が行われる際に静かに行います。詳しくは man ipfw で -q オプションを見てください。
firewall_type="/etc/ipfw.rule"
ファイアウォールのタイプを指定する物ですが、ここでファイル名を指定した場合にはカスタムルールを簡単に適用できるようになっています。今回は細かく制御するためにカスタムルールを定義します。
firewall_logging="YES"
ログを取る場合にはこの設定を入れておきましょう。
ipv6_firewall_enable="YES"
IPv6 ベースでのファイアウォールを有効にします。利用されるコマンドは ip6fw になります。基本的には ipfw と大差ありません。ipv6_* は IPv6 でしか利用されませんので、IPv6 を利用しない場合には不要です。設定を追加しなければ /etc/defaults/rc.conf で NO にされます。
ipv6_firewall_quiet="YES"
ipv6_firewall_type="/etc/ip6fw.rule"
ipv6_firewall_logging="YES"
natd_enable="YES"
ファイアウォールとして動作させる場合に、natd を有効にしたい場合には YES を設定します。
natd_interface="fxp0"
公開側のインタフェース、もしくは使用する IP アドレスを指定します。natd を動かさない場合には意味がありません。
natd_flags="-config /etc/natd.conf"
natd への設定を /etc/natd.conf に記述します。/root/natd.conf でも /home/hoge/natd.conf でも構いませんが、マシンの設定関係は /etc 下にまとめておきましょう。

それでは、カスタムルールを定義します。

カスタムルールの定義は、add <ルール番号> <ルール< [log] <プロトコル> from <パケット送出ホスト> to <パケット受信ホスト> [追加ルール] といった形になります。正確には man ipfw を見てください。

想定環境は、フレッツ ADSL などで PPPoE 接続しているものとします。ADSL モデムなどに繋いでいるのは fxp0、LAN 向けは fxp1 とします。

なお、自分自身が持つ全ての IPv4 アドレスを示すキーワード me は古いシステムでは利用できませんので、ipfw に蹴られた場合には…… 4.x 系の場合には 4.3-RELEASE + security update か 4.4-RELEASE にすることをお薦めします。穴を考えると、ちょっとその辺りのバージョンはお薦めできません。

ここで挙げるルールはあくまでサンプルです。自分の環境に合わせて定義内容を吟味しましょう。

add 1000 deny log all from 192.168.0.0/16 to any recv tun0

tun0 から受け取った 192.168.0.0/16 をすべて拒否し、ログを保存します。外部から 192.168.0.0/16 というアドレスは来ることがない (ローカル専用アドレス) の為、蹴っています。

これは、内部ネットワークの IPv4 アドレスに合わせて設定しましょう。たとえば、10.0.0.0/8 に設定するのであれば、外部からこのアドレスでリクエストが来たら蹴るようにしておきます。

add 1010 deny log all from 127.0.0.1 to any recv tun0

localhost からのリクエストが外部から来るわけがないので拒否します。

add 1020 allow tcp from any to any established

この後のルールで既に接続が確立しているものに関しては許可します。

add 1030 allow all from 192.168.0.0/16 to 192.168.0.0/16

内部ネットワーク間での通信を全て許可します。内部ホストが信用できない場合には、信頼できるホストのみを指定します。

add 1040 allow tcp from any domain to me

全てのホストからの TCP での自分に対するドメイン問い合わせを許可します。

add 1050 allow udp from any domain to me

全てのホストからの UDP での自分に対するドメイン問い合わせを許可します。

add 1060 allow icmp from any to me

自分自身に対する ICMP パケットを受け入れます。受け入れる必要が無い場合には記述する必要はありません。

add 2000 allow ipv6 from any to any

トンネルで IPv6 over IPv4 な人には必須の項目です。

IPv6 は ip6fw で引っ掛けますので、ipfw のルールでは全て通しです。

add 3000 allow all from me to 192.168.0.0/16 via fxp1

自分自身から LAN 向けパケットを全て許可します。

add 3010 allow all from 192.168.0.0/16 to me recv fxp1

LAN から自分自身へのパケットを全て許可します。信頼で (略)。

add 3020 allow all from any to any via lo0

localhost からのパケットは全て通します。

add 3030 allow all from any to any via fxp0

ADSL モデムとの通信を全て通します。外部との通信は tun0 経由で行われますので、fxp0 経由のパケットは通しで問題ありません。下手に止めると通信ができなくなります。(^^;

add 3040 allow all from 192.168.0.0/16 to any keep-state

LAN から外部向けのパケットを全て通過させ、接続が確立した場合にその状態を保持します。接続確立状態を保持するようにすることで、1020 番で確立した接続に関するパケットが通過するようになります。

NAT を利用する場合には必須と言えます。

add 3050 allow icmp from 192.168.0.0/16 to any

LAN 側から ICMP パケットを通すようにします。

add 3060 allow icmp from any to 192.168.0.0/16 icmptype 0,3,11

全てのホストから LAN 側へ ICMP パケットの内、0 番 (エコー返答)、3 番 (終点不到達)、11 番 (時間超過) などを通します。ping などを LAN 側から外部ホストへ打ったときに返るようになります。

add 3990 check-state

keep-state で自動生成される、接続確立状態保持内容をチェックします。

add 5000 allow tcp from any to me smtp,pop3,imap,domain,http

あらゆるホストから自分自身に対する SMTP/POP3/IMAP4/Domain QUERY/HTTP の各ポートへのパケットを通過させます。通しても、デーモンが居なければ受け入れられませんので注意してください。

add 5010 allow udp from any to me domain

あらゆるホストから自分自身に対する Domain QUERY のパケットを通過させます。

add 5020 allow log from any to me ssh

あらゆるホストから自分自身に対する SSH 用ポートへのパケットを通過させます。ログを取るために独立させています。たとえば、SSH に対する自分以外の人がポートを叩いた事を検知したりできます。

なお、自分が契約するプロバイダが持つ IP アドレスの範囲を whois でチェックして、その範囲だけに指定しておく方がより良いでしょう。

add 6000 deny log all from any to me

自分自身に対する、今までに引っ掛からなかった要求を全て拒否し、ログに保存します。

add 7000 deny log all from any to any

あらゆるホスト間での、今までに引っ掛からなかったパケットを全て拒否し、ログに保存します。

要は、LAN に向けた要求を蹴ることになります。例えば、LAN 側から外部へ HTTP で通信した際に、HTTP 応答とは別になんらかのパケットを投げてきた場合などにそれを止め、ログに保存します。

以下は ip6fw 用の定義です。IPv6 を使わない場合には不要です。

ここでは、私の管理している範囲である 3ffe:505:2036::/48 で記述しますが、実際には自分の環境に合うように書き換えてください。そのまま使われると、私の環境からの IPv6 パケットがいきなり落とされます (涙)。

なお、IPv4 のポリシーと同じように記述している為、無駄な部分もあるかもしれません。指摘大歓迎。

IPv6 に関しては、IPv6 入門 が良いかも。

add 1000 deny log all from 3ffe:505:2036::/48 to any recv tun0

外部から自分の管理する領域を名乗るパケットが来た場合に蹴ります。

add 1010 deny log all from ::1 to any recv tun0

外部から自分自身 (localhost) を名乗るパケットが来た場合に蹴ります。

add 2000 allow tcp from any to any established

既に確立した接続は通過させます。

add 2010 allow ipv6-icmp from any to any

ICMPv6 パケットを通します。

add 2020 allow esp from any to any

ESP なパケットを通過させても良い場合にのみ設定してください。通常は通して良いでしょう。

add 2030 allow ah from any to any

AH なパケットを通過させても良い場合にのみ設定してください。

ただし、AH ヘッダ付きのパケットは元パケット全てが保護されている為、AH ヘッダ付きのパケットを通したら中間セキュリティゲートウェイまで通って、その先で元パケットが解釈されて行くため、常に通していい訳ではありません。

この部分については議論の余地があるそうです。

add 2040 allow all from any to ff02::/16

リンクローカルマルチキャストなパケットを全て通過させます。これは基本的に通してください。

add 3000 allow all from 3ffe:505:2036:2002::/64 to 3ffe:505:2036::/48

内部ネットワークを 3ffe:505:2036:2002::/64 にまとめておき、3ffe:505:2036::/48 ネットワーク内全てに対してのパケットを通します。

add 3010 allow all from 3ffe:505:2036::/48 to any

自分自身が属するネットワークからあらゆるホストへのパケットを通します。

add 4000 allow tcp from any to 3ffe:505:2036:2000::/64 smtp,pop3,domain,http

サービスを提供するホストのネットワークを 3ffe:505:2036:2000::/64 にまとめておき、TCP での SMTP/POP3/Domain QUERY/HTTP のポートへのパケットを通します。

add 4010 allow udp from any to 3ffe:505:2036:2000::/64 domain

サービスを提供するホストのネットワーク 3ffe:505:2036:2000::/64 への UDP での Domain QUERY パケットを通します。

add 5000 deny log all from any to 3ffe:505:2036:2000::/64

サービス提供ネットワーク 3ffe:505:2036:2000::/64 への、今までに引っ掛からなかったパケットをすべて拒否し、ログに保存します。

add 6000 allow all from any to 3ffe:505:2036:2002::/64

ローカルネットワーク 3ffe:505:2036:2002::/64 への、全てのパケットを通します。ローカルネットワークでは、各ホスト毎に防衛策を考える場合にはこれで構いませんが、それが望めない場合にはもっと細かく定義しましょう。

私の場合は各ホスト全てでそれぞれの環境にあったファイアウォールを立てているため、これで済ませています。

add 7000 deny log all from any to any

今までに引っ掛からなかったパケットを全て拒否し、ログに保存します。

IPv6 の場合、各ホストが 1 対 1 で通信するのが普通の為、ルータは通過させるルールと自分自身の防衛だけ考えれば良く、IPv4 に比べてルール数が飛躍的に少なくなっています。特に、IPv6 の場合はプロトコルスタックに IPSec が必須となっているため、IPv4 に比べてプロトコルスタックレベルで安全性が高められており、また、ESP ヘッダを有効にすることで暗号化されたパケットが送出されるようになりますので、盗聴防止にも役立ちます。