ipfw でフィルタリング
 (オプション)nmap を用いた開放ポートの確認
nmap を実行すると,そのホストで外部からの接続を受け付けている 開放ポートを確認できる.
nmap のインストール
パッケージからインストールする.
pkg install nmap
ports からビルドする場合は次の通り.
cd /usr/ports/security/nmap make install clean
スキャン
TCP のコネクトスキャンの例
nmap -sT -Pn -T4 -p 1-65535 -r -oN result-tcp.txt -v -A localhost
- -sT: コネクトスキャン(TCP の接続を最後まで確立する方式)
- -Pn: 事前の host discovery を行わない(旧来の -P0 は -Pn に置き換えられた)
- -T0 から -T5: タイミングテンプレート.数字が大きいほど高速で負荷が大きい.-T4 は高速側
- -p 1-65535: ポート番号の範囲
- -r: ポート番号を順番にスキャンする(ランダム化しない)
- -oN: 通常形式でファイルに出力
- -v: 詳細表示
- -A: OS 検出,バージョン検出,スクリプトスキャンを有効にする(root 権限が必要)
UDP のスキャンの例
nmap -sU -Pn -T4 --top-ports 100 -oN result-udp.txt -v localhost
- -sU: UDP スキャン.root 権限が必要
- -Pn: 事前の host discovery を行わない
- -T0 から -T5: タイミングテンプレート.数字が大きいほど高速で負荷が大きい
- --top-ports 100: 利用頻度の高い 100 ポートに限定する.UDP スキャンは 1 ポートあたりの待ち時間が長く,全 65535 ポートの走査には非常に長い時間がかかるため,範囲を限定する
- -oN: 通常形式でファイルに出力
- -v: 詳細表示
自分のマシンで待ち受けているポートを確認するだけであれば, スキャンを行わずに sockstat を使う方が速く,正確である.
sockstat -4 -6 -l
/etc/rc.conf を編集し ipfw を有効にする
ルールは /etc/ipfw.rules に自分で記述し,そのファイルを firewall_script で指定する. /etc/rc.firewall は OS の更新で上書きされるため,直接編集しない.
/etc/rc.conf に,次の行を追加する.
firewall_enable="YES" firewall_script="/etc/ipfw.rules" firewall_logging="YES"
- firewall_enable: 起動時に ipfw を有効にする
- firewall_script: 読み込むルールファイルの絶対パス.独自のルールファイルを指定した場合,firewall_type は使用されない
- firewall_logging: log キーワードの付いたルールのログ出力を有効にする(sysctl の net.inet.ip.fw.verbose が 1 に設定される)
sysrc コマンドを使って設定することもできる.
sysrc firewall_enable="YES" sysrc firewall_script="/etc/ipfw.rules" sysrc firewall_logging="YES"
外部への通信の遮断(クライアントとして使う場合の設定)
最初に,外部への通信を遮断する設定を行う.
- 外部への通信(自分が接続を開始する通信)と,外部からの通信(外部が接続を開始する通信)は区別して設定する
- LAN 内の通信は全て許可する. LAN 内の IP アドレスを,このページでは「XXX.YYY.ZZZ.0/24」と書く
- 外部への通信について,許可するポートのポート番号を指定する(指定しなかったポートへの通信は遮断される)
- keep-state と check-state を使い,許可した通信の戻りパケットだけを通す(ステートフルフィルタリング)
外部への通信を許可するポートを決める
次のポートを許可しておけば,一般的な用途では支障が少ない(用途に応じて増減させる).
- 22 ・・・ ssh
- 53 (UDP, TCP) ・・・ DNS
- 80, 443 ・・・ HTTP, HTTPS
- 123 (UDP) ・・・ NTP
- 587 ・・・ メール送信(SMTP submission)
- 993 ・・・ IMAPS
- 995 ・・・ POP3S
平文でパスワードやデータを送る ftp (20, 21),pop3 (110),imap (143),smtp (25) は, TLS を用いる代替(sftp または scp,995,993,587)に置き換える. ftp は,制御用とデータ用の 2 本の接続を使い,データ用のポート番号が動的に決まるため, フィルタリングの設定も複雑になる.
ipfw ルールファイルの例
/etc/ipfw.rules を作成し,次の内容を記述する. XXX.YYY.ZZZ.0/24 の部分は,自分の LAN のアドレスに変更する.
#!/bin/sh
fwcmd="/sbin/ipfw -q"
# ルールの初期化
${fwcmd} -f flush
# ループバック
${fwcmd} add 100 allow ip from any to any via lo0
${fwcmd} add 110 deny ip from any to 127.0.0.0/8
${fwcmd} add 120 deny ip from 127.0.0.0/8 to any
# 確立済み通信(動的ルール)の照合
${fwcmd} add 200 check-state
# LAN 内の通信は許可
${fwcmd} add 300 allow ip from XXX.YYY.ZZZ.0/24 to me
${fwcmd} add 310 allow ip from me to XXX.YYY.ZZZ.0/24
# 外部への TCP 通信(22:ssh, 53:DNS, 80:http, 443:https, 587:submission, 993:imaps, 995:pop3s)
${fwcmd} add 400 allow tcp from me to any 22,53,80,443,587,993,995 setup keep-state
# 外部への UDP 通信(53:DNS, 123:NTP)
${fwcmd} add 410 allow udp from me to any 53,123 keep-state
# ping(echo request)と,経路 MTU 探索などに必要な ICMP
${fwcmd} add 420 allow icmp from me to any icmptypes 8 keep-state
${fwcmd} add 421 allow icmp from any to me icmptypes 3,4,11
# DHCP でアドレスを取得する場合
${fwcmd} add 430 allow udp from any 67 to me 68 in
# 上記以外は遮断し,ログに記録する
${fwcmd} add 65000 deny log ip from any to any
- setup: TCP の接続開始パケット(SYN のみ)にマッチする
- keep-state: マッチしたパケットに対応する動的ルールを生成し,その通信の戻りパケットを通す
- check-state: 動的ルールとの照合を行う位置を指定する.check-state を書かない場合,最初の keep-state ルールの位置で照合される
- me: そのマシンに設定されている全ての IP アドレス
設定の反映
ルールファイルを保存したあと,次のコマンドで反映する.
service ipfw restart
設定内容の確認
ipfw の設定情報(ルールリスト)は,ipfw show で確認できる. show はパケットカウンタ付きの表示である.
ipfw show
動的ルール(keep-state により生成されたルール)は,ipfw -d list で確認できる.
ipfw -d list
ipfw ルールの動作確認
許可したポートへの通信ができること,許可していないポートへの通信が遮断されることを確認する. 接続確認には nc を使う.-z は接続確認のみ,-v は詳細表示,-w は待ち時間(秒)である.
nc -vz -w 3 www.freebsd.org 443 nc -vz -w 3 www.freebsd.org 23
443 は接続に成功し,23(telnet)は遮断されて時間切れになる.
どのルールにマッチしたかは,ipfw show のパケットカウンタで確認できる. 遮断ルールのカウンタが増えていれば,そのルールで遮断されている.
ipfw show
log キーワードを付けたルールにマッチしたパケットは, /var/log/security に記録される.
tail -f /var/log/security
ログが出力されない場合は,sysctl で net.inet.ip.fw.verbose の値を確認する. 0 のときは 1 に設定する(この設定は再起動すると元に戻る. 恒久的に有効にするには,/etc/rc.conf に firewall_logging="YES" を記述する).
sysctl net.inet.ip.fw.verbose sysctl net.inet.ip.fw.verbose=1
同一ルールのログ出力回数には上限があり,上限に達するとログが止まる. 上限は net.inet.ip.fw.verbose_limit で設定する(0 は無制限).
sysctl net.inet.ip.fw.verbose_limit=100
公開サーバでの設定例(Web/SSH サーバを例として)
公開サーバの場合,外部からの通信については,公開するポートへの通信だけを許可し, それ以外のポートへの通信を遮断する.
/etc/ipfw.rules を編集し,最後の deny ルール(例では 65000 番)よりも前に,次のルールを追加する.
# 外部から受け付けるサービス(22:ssh, 80:http, 443:https)
${fwcmd} add 500 allow tcp from any to me 22,80,443 setup keep-state
ssh を公開する場合には,パスワード認証を無効にし,公開鍵認証を使う. /etc/ssh/sshd_config で PasswordAuthentication no,PermitRootLogin no を設定し, sshd を再起動する.
service sshd restart
ルールファイルを保存したあと,次のコマンドで反映する.
service ipfw restart
ipfw の設定情報(ルールリスト)は,ipfw show で確認できる.
ipfw show
ipfw 使用法
ルールの追加と削除
ルール追加の書式は次の通り.
ipfw [-q] add [ルール番号] アクション [log] プロトコル アドレス [ オプション ]
-q
-
ルールの追加や flush のときにメッセージを出力しない.スクリプト内で使用する.
-
アドレスやサービス名を,可能であれば名前に変換して表示する.
-
ルールリストにエントリを追加する.
-
ルールリストから指定した番号のエントリを削除する.
-
デフォルトルールを除く全てのルールを削除する.
-
パケットカウンタを 0 に戻す.
-
1 から 65534 の値を指定する.ルールは番号の小さい順に照合され,最初にマッチしたルールが適用される.
番号を省略した場合,最後のルール(65535 番のデフォルトルールを除く)の番号に 100 を足した番号になる.
番号 65535 はデフォルトルールであり,変更や削除ができない.
デフォルトルールの内容は,標準のカーネル設定では「deny ip from any to any」である.
-
マッチしたルールをログに出力する.出力先は /var/log/security であり,
出力には sysctl の net.inet.ip.fw.verbose が 1 である必要がある.
アクション
-
allow
-
パケットを通過させる(別名: pass, accept, permit).
-
パケットを捨てる.発信元への通知は行わない(別名: drop).
-
パケットを捨て,ICMP の到達不能メッセージを発信元へ送る.
unreach host,unreach port などを指定する.
旧来の reject は非推奨であり,unreach host と同じ意味である.
-
TCP パケットを捨て,発信元へ TCP リセット(RST)を送る.
-
パケットカウンタを更新するだけで,通過も遮断もしない.照合は次のルールから続けられる.
-
動的ルールとの照合を行う.マッチした場合,そのルールの動作が適用され,照合は終了する.
-
マッチしたパケットを,port にバインドされている divert(4) ソケットへ送る.
-
カーネル内 NAT の処理を行う.
-
指定した番号以降のルールから照合を続ける.
プロトコル
-
ip または all
-
任意の IP パケットにマッチする.
-
ICMP パケットにマッチする.
-
TCP パケットにマッチする.
-
UDP パケットにマッチする.
アドレスとポートの指定
from <address>[/masklen] [port] to <address>[/masklen] [port]
- port の指定は,ポートを持つプロトコル(TCP と UDP)の場合にだけ可能である. 「80,443,8080」のようにカンマ区切りの列挙,「1024-65535」のような範囲指定ができる
- address には,IP アドレス,ホスト名,キーワードを指定する. any は任意の IP アドレス,me はそのマシンに設定されている IP アドレスを表す
- masklen は,アドレスマスクの上位何ビットを 1 にするかを示す十進数値である. 「192.216.222.1/24」は,クラス C のサブネット(この場合 192.216.222.0 から 192.216.222.255)の任意のアドレスにマッチする
- 多数のアドレスをまとめて扱う場合には,テーブルを使う(ipfw table 1 add 192.0.2.0/24 のように登録し,ルールでは「table(1)」と書く)
主なオプション
- via <interface>: 指定したインタフェースを通るパケットだけにマッチする(例: via em0)
- in, out: 入力方向,出力方向のパケットだけにマッチする
- setup: TCP の接続開始パケット(SYN のみ)にマッチする
- established: 確立済みの TCP 接続のパケット(ACK または RST)にマッチする
- keep-state: 動的ルールを生成し,その通信の戻りパケットを通す
- limit src-addr N: 同一の発信元アドレスからの同時接続数を N 個に制限する
- icmptypes types: 指定した ICMP タイプにマッチする(8 は echo request,0 は echo reply,3 は destination unreachable)
- frag: 断片化されたパケットの先頭以外にマッチする
ルールリストの表示
書式は次の通り.
ipfw [-a] [-d] [-t] [-N] list
-
-a
-
パケットカウンタの値も表示する.「ipfw show」は「ipfw -a list」と同じである.
-
keep-state により生成された動的ルールも表示する.
-
各ルールが最後にマッチした時刻を表示する.この時刻の表示形式は,ipfw(8) の入力形式とは異なる.
-
アドレスやサービス名を,可能であれば名前に変換して表示する.
【関連する外部ページ】 ipfw(8) マニュアル, FreeBSD ハンドブック「ファイアウォール」