Kodama's home / tips.

iptables による firewall の例(Linux iptables メモ)

ipcheins からの移行で混乱しかけたのでメモ.
  1. ipchains と iptables の基本的なモデル
  2. ちょっと複雑な例(ネットワーク図)
  3. iptables の設定スクリプト
  4. まずネットワーク, アドレスなどを定義
  5. iptables の初期設定など
  6. マスカレード(内部から外部)等の設定
  7. 外部から内部サーバへ転送するサービスの処理
  8. 後始末 など(スクリプトの末尾部分)

ipchains と iptables の基本的なモデル


ipchains のモデル
-->INPUT-->routing-->FORWARD(masquerade)--+-->OUTPUT-->
            |                             |
            +------->(local)------------->+

iptables のモデル
-->PREROUTING(DNAT)-->routing ----->  FORWARD  -----> +-->POSTROUTING(SNAT)-->
                       |                              |
                       +-->INPUT-->(local)-->OUTOUT-->+

まず, デフォルトの chain の構成がかなり違っている. 自分自身の入出力のパケット制限を行うだけならほとんど変わりは無いが, ルータ兼ファイアウォールとして使う場合, INPUT, OUTPUT の適用が, 全てのパケットだったものが, local に処理されるパケットのみに変更になっている. 言い替えると, 転送するパケットの入出力の制限は, FORWARD で行うのが基本となる.

masquerade(マスカレード, 内部から外部へのパケットのアドレス変換) は POSTROUTING(SNAT)で行う.

  1. FORWARD でパケットの転送を記述
  2. POSTROUTING で MASQUERADE を指定して, 接続元のアドレス変換 を行う
記述例: 192.168.1.0 をマスカレード
SET_FORWARD(){
  echo forward/masquerade(SNAT) for $1
  iptables -A FORWARD -j ACCEPT -s $1 -i ${INSIDE_DEVICE} -d ${WORLD}
  iptables -t nat -A POSTROUTING -s $1 -o ${OUTSIDE_DEVICE} -j MASQUERADE
}

SET_FORWARD 192.168.1.0/24

port-forwarding(外部から内部への転送) は PREROUTING(DNAT)で行うことになる.

  1. PREROUTING(DNAT) で接続先のアドレス変換を行う.
  2. FORWARD で転送を記述.
記述例: SMTP(25/TCP) を サーバ 192.168.1.100 に転送.
SET_DNAT () {
	echo "DNAT: --protocol $1 --dport $2 --to $3:$2. (open for the WORLD)"
	iptables -A PREROUTING -t nat -d ${OUTSIDE_IP} -p $1 --dport $2 -j DNAT --to $3:$2
	iptables -A FORWARD -j ACCEPT -d $3 -p $1 --dport $2
}

SET_DNAT tcp 25 192.168.1.100

ちょっと複雑な例(ネットワーク図)

最低限, 転送の許可/禁止の違いが出る程度に複雑なネットワークを想定.

ルータ,ファイアウォールなどで区切られたセグメントが SEG0, SEG1, SEG2 の3個ある.

  WORLD(The Internet, 0.0.0.0/0)
   |
  SEG0(DMZ, 10.0.0.0/8)--SERVER0(10.0.0.100, outer server, mail gateway)
   |
(OUTSIDE_DEVICE=eth0, OUTSIDE_IP=10.0.0.200)
 Firewall
(INSIDE_DEVICE=eth1, INSIDE_IP=192.168.1.1)
   |
  SEG1(192.168.1.0/24)--SERVER1(192.168.1.100, inner server, mail hub, http proxy, www server)
   |
  router
   |
  SEG2(192.168.2.0/24)--User's PC(192.168.2.*)

上の "Firewall" が Linux ベースの アドレス変換(NAT) と パケットフィルタによるファイアウォール.

SEG0 は The Internet からアクセスできる所謂 DMZ(DeMilitarized Zone). 例では 10.*.*.* のプライベートアドレスを書いているが, 実際にはプロバイダから与えられた公開用のアドレスを用いる.

DMZ に対外的なサービスを行うサーバ SERVER0 を置く. SERVER0 では 外部向けの メールサーバ(SERVER1 と 外部の転送の中継) などを行う.

SEG1 は firewall で保護されている, サーバ類を置く領域.

SERVER1 で メールサーバ, 外部用 WWW サーバ, 内部用 http proxy などを行う.

SEG2 は一般の PC を置く領域とする. ここからは smtp(メール), http(WWW) は直接には外部にアクセスできないようにする. (メールサーバ, http Proxy を利用してもらうため.)

iptables の設定スクリプト

上で想定したネットワークの firewall のスクリプト例. 説明を加えながらなのでバラバラに見えるが, 順につなぎ合わせると実際に使えるスクリプトになるはず. (FloppyFw を参考にした.)

iptables -L で表示したとき, 先頭側にあるルールから優先して適用される. -A で条件を追加すると既存のルールの末尾に追加される. つまり, 次のように並んでいたら, 上の ACCEPT の方が適用され, DROP の方は無視される.

iptables -A INPUT -j ACCEPT ...
iptables -A INPUT -j DROP ...

iptables 関連の必要なモジュールは適宜組み込んでおくこと. (ipt_MARK, ipt_TOS, ipt_LOG, ip_conntrack_ftp, ip_conntrack_irc, iptable_mangle, ip_nat_ftp, ip_nat_irc, ipt_tos, ipt_mark, ipt_limit など.)

まずネットワーク, アドレスなどを定義

アドレスなどを変数にしておくと確認などが楽になります. 先のネットワーク図に従って変数を定義してゆきます.
# Network Interfaces
OUTSIDE_DEVICE=eth0
OUTSIDE_IP=10.0.0.200
INSIDE_DEVICE=eth1
INSIDE_IP=192.168.1.1

# Networks
LOCAL_NETWORK=127.0.0.0/8
WORLD=0.0.0.0/0
SEG0=10.0.0.0/8 ; OUTSIDE_NETWORK=${SEG0} # DMZ
SEG1=192.168.1.0/24  # server segment
SEG2=192.168.2.0/24  # user's segment

# Servers
SERVER0=10.0.0.100 # outer server
SERVER1=192.168.1.100 # inner server

iptables の初期設定など

一旦, パケット転送機能を停止してから, iptables を操作する. スクリプトの末尾で IP forwarding を有効にする.
## This script is based on FloppyFw-2.0.8(http://www.zelow.no/floppyfw/download.html)
echo "Disabling IP forwarding."
echo "0" > /proc/sys/net/ipv4/ip_forward
iptables の初期化. 大きく分けると, デフォルトで許可して拒否するものを明記するやりかたと, デフォルトで拒否して許可するものを明記するやりかたがあります. ここでは, 拒否をデフォルトとしておきます.

DROP と DENY のどちらも接続を拒否します. DENY では相手に接続出来ないという知らせのパケットを返しますが, DROP では単に無視します.

# Flushing the chains.
iptables -F
for i in `cat /proc/net/ip_tables_names`; do iptables -F -t $i ; done
iptables -X
iptables -Z

echo Policy for forwarding, input and output.(DROP,DENY,ACCEPT)
iptables -P INPUT DROP
iptables -P FORWARD DROP
iptables -P OUTPUT DROP
ssh, telnet の様な対話型の利用では反応を特に重視します. メールの配送のような即座の反応を要しない場合についての優先度を下げて 対話型の利用が快適になるように配慮します.
echo Some attempt to get interactive sesions a bit more interactive under load:
#  ssh/22, ftp/21, ftp-data/20, http/80, nntp/119, smtp/25
iptables -A PREROUTING -t mangle -p tcp --sport 22  -j TOS --set-tos Minimize-Delay
iptables -A PREROUTING -t mangle -p tcp --sport 21 -j TOS --set-tos Minimize-Delay
iptables -A PREROUTING -t mangle -p tcp --sport 20 -j TOS --set-tos Maximize-Throughput
iptables -A PREROUTING -t mangle -p tcp --sport 80 -j TOS --set-tos Maximize-Throughput
iptables -A PREROUTING -t mangle -p tcp --sport 119 -j TOS --set-tos Minimize-Cost
iptables -A PREROUTING -t mangle -p tcp --sport 25 -j TOS --set-tos Minimize-Cost
内部の LAN で使用している Windows のファイル共有などが誤って外に出ない様にします.
echo "We do not like the NetBIOS and Samba leaking.."
iptables -A FORWARD -j DROP -d ${WORLD} -p tcp --dport 135:139
iptables -A FORWARD -j DROP -d ${WORLD} -p udp --dport 135:139
iptables -A FORWARD -j DROP -d ${WORLD} -p tcp --dport 445
iptables -A FORWARD -j DROP -d ${WORLD} -p udp --dport 445
マシンの内的な処理のために 127.*.*.* を許可しておきます.
# echo "loopback 127.0.0.0/8"
iptables -A INPUT -j ACCEPT -s ${LOCAL_NETWORK} -d ${LOCAL_NETWORK} -i lo
iptables -A OUTPUT -j ACCEPT -s ${LOCAL_NETWORK} -d ${LOCAL_NETWORK}

マスカレード(内部から外部)等の設定

内部から外部に向けてのマスカレードを行う. 外部からは Firewall の外側アドレスからのパケットのように見えることになる.

ついでに内部同士(SEG1,SRG2)のルーティングも許可. (本来は内部のルータで行うべきだが, こうすると, SERVER1 のルーティングがいい加減でもなんとかなる. つまり, 正しくは, SERVER1 では, SEG0 方向と SEG2 方向へのルーティングをちゃんと設定するべきだが, SEG0 方向にパケットを投げればとりあえず動く....)

SET_FORWARD(){
  echo forward/masquerade(SNAT) for $1
  iptables -A FORWARD -j ACCEPT -s $1 -i ${INSIDE_DEVICE} -d ${WORLD}
  iptables -t nat -A POSTROUTING -s $1 -o ${OUTSIDE_DEVICE} -j MASQUERADE
}

一般の(ユーザが使う) PC 用のセグメントから直接外部に出したくないプロトコルなどを止める. セキュリティ方針に従って, 内部ルータやサーバのアクセス制限と組み合わせる. 複数のセキュリティレベルがある場合には, それにあわせて制限をつける.

SET_FORWARD_DROP () {
  echo "DROP services for $1"
  # echo mail/25, http/80, http proxy/8080 deny
  iptables -A FORWARD -j DROP -s $1 -p tcp --dport 25
  iptables -A FORWARD -j DROP -s $1 -p udp --dport 25
  iptables -A FORWARD -j DROP -s $1 -p tcp --dport 80
  iptables -A FORWARD -j DROP -s $1 -p udp --dport 80
  iptables -A FORWARD -j DROP -s $1 -p tcp --dport 8080
  iptables -A FORWARD -j DROP -s $1 -p udp --dport 8080
}

上のスクリプトを使って実際にパケット転送を設定.

SET_FORWARD ${SEG1} # server segment
SET_FORWARD_DROP ${SEG2} ; SET_FORWARD ${SEG2} # user segment

外部から内部サーバへ転送するサービスの処理

メールと WWW で異なる方法で外部へのサービスを行ってみた.

メールサーバにはユーザが登録されているので直接外部に露出したくない. そこで中継サーバを置く事にする.

内部のユーザが使うメールサーバは SERVER1. 外部向けのメールはここから SERVER0 に転送してから当該サイトのサーバに送り込む.

一般のPCからは外部に直接 smtp 接続を出来ない様にする. これによって, PC がウイルスやワームに感染した場合でも, 外部への踏台となる事は避けられる.

外部からのメールは, 一旦 SERVER0 で受け取ってから, SERVER1 に転送する. Firewall は SERVER0 からの smtp パケットを SERVER1 に転送しなければならない.

更に, サーバでの透過的なウイルス検出も行うと良いが, ここでは解説しない.

The Internet
  |
SERVER0 (mail gateway)
  |
Firewall
  |
SERVER1 (mail hub)
  |
User's PC

WWW サーバは DMZ に置かずに firewall の内側に置いてみた. 外部向けのサービスを行うので, 外からの http パケットがここに到達する必要がある. Firewall は http/https パケットを SERVER1 に転送しなければならない. 外部からは Firewall の外側アドレスに WWW サーバがあるように見える.

The Internet
  |
Firewall
  |
SERVER1 (WWW)

SET_DNAT は外部(The Internet) から内部サーバへのパケット転送. SET_DNAT_DMZ は DMZ のサーバから内部サーバへのパケット転送.

echo Corresponding rules for the port-forward/DNAT.
echo Takes care of connections from the outside to the inside.

SET_DNAT () {
	echo "DNAT: --protocol $1 --dport $2 --to $3:$2. (open for the WORLD)"
	iptables -A PREROUTING -t nat -d ${OUTSIDE_IP} -p $1 --dport $2 -j DNAT --to $3:$2
	iptables -A FORWARD -j ACCEPT -d $3 -p $1 --dport $2
}

SET_DNAT_DMZ () {
	echo "DNAT: --proocol $1 --dport $2 --to $3:$2. (restricted DMZ only)"
	iptables -A PREROUTING -t nat -d ${OUTSIDE_IP} -p $1 --dport $2 -j DNAT --to $3:$2
	iptables -A FORWARD -j ACCEPT -s ${OUTSIDE_NETWORK} -d $3 -p $1 --dport $2
	iptables -A FORWARD -j DROP -s ${WORLD} -d $3 -p $1 --dport $2
}

実際にサービス毎に転送を設定.

SET_DNAT_DMZ tcp 25 ${SERVER1} # smtp/25 tcp port forward to mail hub
SET_DNAT tcp 80 ${SERVER1} # http/80 tcp port forward to WWW server
SET_DNAT tcp 443 ${SERVER1} # https/443 tcp port forward to WWW server

後始末 など(スクリプトの末尾部分)

echo "drop private/multicast addresses"
SET_DROP(){
   iptables -A INPUT -j DROP -s $1
   iptables -A FORWARD -j DROP -s $1
}
SET_DROP 127.0.0.0/8 # loopback
SET_DROP 10.0.0.0/8 # private
SET_DROP 172.0.0.0/12 # private
SET_DROP 192.168.0.0/16 # private
SET_DROP 224.0.0.0/4 # multicast
SET_DROP 0.0.0.0/8 # reserved
SET_DROP 255.255.255.255 # broadcast

安直に ICMP を全て止めているサイトもあるようだが...それで良いのか?

echo Ping and friends.
iptables -A INPUT -j ACCEPT -p icmp --icmp-type destination-unreachable # MTU check. required
iptables -A INPUT -j ACCEPT -p icmp --icmp-type echo-reply # ping
iptables -A INPUT -j ACCEPT -p icmp --icmp-type echo-request # ping
iptables -A INPUT -j ACCEPT -p icmp --icmp-type time-exceeded #  traceroute
iptables -A INPUT -j DROP -p icmp

以下で接続が確立したものについては通信を許可している. INPUT, FORWARD のルールについては 通信の開始を許可するかどうかを(ここより上で)指示すると良い事になる. OUTPUT は NEW を許可しているので, 自分から接続を開始することはできる.

echo Keep state.
iptables -A OUTPUT -m state --state NEW,ESTABLISHED,RELATED -j ACCEPT
iptables -A INPUT -m state --state ESTABLISH,RELATED -j ACCEPT
iptables -A FORWARD -m state --state ESTABLISH,RELATED -j ACCEPT
# So, basically, we have to write rules about NEW state for INPUT table (and FORWARD if needed).

IP forwarding を有効にしておしまい.

echo "Enabling TCP syn cookies."
echo "1" > /proc/sys/net/ipv4/tcp_syncookies

echo "Enabling anti spoofing."
for f in /proc/sys/net/ipv4/conf/*/rp_filter; do
   echo 1 > $f
done

echo "Enabling dynamic IP address following."
echo 7 > /proc/sys/net/ipv4/ip_dynaddr

echo "trying to stop some smurf attacks.(broadcast ping attack)"
echo 1 > /proc/sys/net/ipv4/icmp_echo_ignore_broadcasts

echo "Enabling IP forwarding."
echo "1" > /proc/sys/net/ipv4/ip_forward

echo List all rules.
iptables -L -n

Kodama's home / tips.