傳統防火牆和網橋式防火牆有什麼區別呢?通常一個防火牆象一個路由器一樣工作:內部系統被設置為將防火牆看做是通向外部網絡的網關,並且外部的路由器被設置為將防火牆看做是連往內部被保護的網絡的網關。一個網橋則是一個聯結一個或多個網段的設備,在各個網段之間轉發數據,而網絡中其他設備並不會感覺到存在一個網橋。換句話說,一個路由器將兩個網絡連接在一起,在兩者之間傳輸數據;一個網橋則更象一段網線,將一個網絡的兩個部分連接在一起。一個網橋防火牆則象網橋一樣工作,而不被兩端設備發現,但是同樣具有過濾通過它的數據包的功能。
為什麼會需要實現網橋式防火牆呢?一般有以下幾個原因:
* 你可以在網絡中添加一個防火牆而不需要修改網絡中任何設備的參數。
* 你可能希望保護網絡的某一個部分而卻沒有權利控制外部路由的參數信息。
我遇到的問題
我的辦公室是一個ADSL連接到Demon互聯網,同時有16個IP地址的子網可供使用。因為英國ISP的特殊原因,線路和路由器是由英國電信公司安裝和擁有,因此我們有權利配置外部路由器來指定誰是內部網絡的網關,這樣我只有兩種選擇:
* 直接將每台主機和ADSL路由器相連接,並且獨立地為每台主機使用iptables來設置防火牆規則。
* 另外一種選擇是使用NAT功能的防火牆來帶動內部網絡訪問互聯網。
第一種方法是不可接受的,因為這樣將大大增加出錯和系統管理開銷。第二種方法也優缺點,雖然大多數應用都能被NAT方式支持,但是也有例外,例如視頻流和VPN等等。一個網橋防火牆則能解決這些問題,防火牆能架設在ADSL路由器和內部網絡之間來保護網絡,但同時不需要修改配置。最後一個障礙是在標准的Linux內核中完全旁路了iptables,因此你能使用網橋或者是iptables防火牆,但是不能同時使用該功能。
解決方案
幸運的是,有一個項目專門實現支持iptables的網橋,因此任何穿過網橋的數據包可以被遞交給iptables規則進行過濾處理。結果是防火牆可以是完全透明於網絡的,不需要特殊的路由功能。就互聯網而言,防火牆並不存在,除了特定的連接被阻塞。網橋軟件是一個內核補丁來支持已有的網橋代碼可以連同iptables一起工作。方便的是開發者已經制作了RPM形式的支持網橋防火牆的內核。但不方便的是相關文檔太少,因此該文章就是幫助那些希望實現網橋式防火牆的人們。
橋接和路由 - 是如何工作的
簡單的說,Linux網橋實現一般是在具有一個或多個網絡接口的設備上實現的,通過檢測多個網段的活動性,橋接代碼學習到哪個MAC地址從哪個接口可以到達,並且使用該信息來判斷是否要中繼一個數據包到另外一個網段。網橋接口本身是沒有分配IP地址的,但是整個網橋被配置作為防火牆的單個接口。
從上圖可以看到,在橋接情況下目的地址為橋設備本身的數據需要經過filter表的INPUT規則鏈和mangle表的PREROUTING規則鏈;從橋設備自身發出的數據需要經過filter表的OUTPUT規則鏈和mangle表的PREROUTING規則鏈;而流經橋設備的數據則要分別經過mangle表的PREROUTING規則鏈和filter表的FORWARD規則鏈和mangle表的POSTROUTING規則鏈。
網絡拓樸
我分配得到的靜態IP地址范圍為xxx.xxx.xxx.48-63,也就是子網掩碼為255.255.255.240。我決定將整個IP分割為兩個網段:xx.xxx.xxx.48-56用於防火牆以外,這包括ADSL路由器自身的IP地址 (xxx.xxx.xxx.49);xxx.xxx.xxx.57-62用在防火牆之後部分。需要注意的是這並不是真正的子網劃分,因為它們是有網橋而不是路由器連接的。
防火牆規則
防火牆規則定義如下:
#!/bin/sh
#
# rc.firewall - Initial SIMPLE IP Firewall test script for 2.4.x
#
# Author: David Whitmarsh
# (c) 2001, 2002 Sparkle Computer Co ltd.
# based on rc.firewall by Oskar Andreasson
# parts (c) of BoingWorld.com, use at your own risk,
# do whatever you please with
# it as long as you don't distribute this without due credits to
# BoingWorld.com and Sparkle Computer Co Ltd
#
###########
# Configuration options, these will speed you up getting this script to
# work with your own setup.
#
# your LAN's IP range and localhost IP. /24 means to only use the first 24
# bits of the 32 bit IP adress. the same as netmask 255.255.255.0
#
# BR_IP is used to Access the firewall accross the network
# For maxium security don't set one up - but then you must do
# everything directly on the firewall.
BR_IP="xxx.xxx.xxx.57"
BR_IFACE=br0
LAN_BCAST_ADDRESS="xxx.xxx.xxx.63"
INTERNAL_ADDRESS_RANGE="xxx.xxx.xxx.56/29"
INET_IFACE="eth1"
LAN_IFACE="eth0"
LO_IFACE="lo"
LO_IP="127.0.0.1"
IPTABLES="/sbin/iptables"
#########
# Load all required IPTables modules
#
#
# Needed to initially load modules
#
/sbin/depmod -a
#
# Adds some iptables targets like LOG, REJECT
#
/sbin/modprobe ipt_LOG
/sbin/modprobe ipt_REJECT
#
# Support for connection tracking of FTP and IRC.
#
/sbin/modprobe ip_conntrack_ftp
/sbin/modprobe ip_conntrack_irc
#
# Take down the interfaces before setting up the bridge
#
ifdown $INET_IFACE
ifdown $LAN_IFACE
ifconfig $INET_IFACE 0.0.0.0
ifconfig $LAN_IFACE 0.0.0.0
# Clean up for a restart
$IPTABLES -F
$IPTABLES -X
#
# Set default policies for the INPUT, FORWARD and OUTPUT chains
#
$IPTABLES -P INPUT DROP
$IPTABLES -P OUTPUT ACCEPT
$IPTABLES -P FORWARD DROP
# Our interfaces don't have IP addresses so we have to start with the mangle
# PREROUTING table
$IPTABLES -t mangle -P PREROUTING DROP
# Now we are pretty secure, let's start the bridge
# This will create a new interface
brctl addbr $BR_IFACE
# and add the interfaces to it
brctl addif $BR_IFACE $INET_IFACE
brctl addif $BR_IFACE $LAN_IFACE
# make us visible to the network again (optional)
if [ "$BR_IP" != "" ] ; then
ifconfig $BR_IFACE $BR_IP
else
# otherwise we must at least bring the interface up for the bridge to work.
ifconfig $BR_IFACE up
fi
# Block obvious spoofs
$IPTABLES -t mangle -A PREROUTING -s 192.168.0.0/16 -j DROP
$IPTABLES -t mangle -A PREROUTING -s 10.0.0.0/8 -j DROP
$IPTABLES -t mangle -A PREROUTING -s 172.16.0.0/12 -j DROP
# Accept internal packets on the internal i/f
$IPTABLES -t mangle -A PREROUTING -i $LAN_IFACE -s $INTERNAL_ADDRESS_RANGE -j ACCEPT
# Accept external packets on the external i/f
$IPTABLES -t mangle -A PREROUTING -i $INET_IFACE ! -s $INTERNAL_ADDRESS_RANGE -j ACCEPT
#
# Accept the packets we actually want to forward
#
$IPTABLES -A FORWARD -p ALL -s $INTERNAL_ADDRESS_RANGE -j ACCEPT
$IPTABLES -A FORWARD -m state --state ESTABLISHED,RELATED -j ACCEPT
$IPTABLES -A FORWARD -m limit --limit 3/minute --limit-burst 3 -j LOG --log-level 7 --log-prefix "IPT FORWARD packet died: "
#
# Create separate chains for ICMP, TCP and UDP to traverse
#
$IPTABLES -N icmp_packets
#
# ICMP rules
#
$IPTABLES -A icmp_packets -p ICMP -s 0/0 --icmp-type 0 -j ACCEPT # echo reply
$IPTABLES -A icmp_packets -p ICMP -s 0/0 --icmp-type 3 -j ACCEPT # dest unreachable
$IPTABLES -A icmp_packets -p ICMP -s 0/0 --icmp-type 5 -j ACCEPT # redirect
$IPTABLES -A icmp_packets -p ICMP -s 0/0 --icmp-type 11 -j ACCEPT # time exceeded
$IPTABLES -A FORWARD -p ICMP -j icmp_packets
#
# UDP ports
#
$IPTABLES -N udpincoming_packets
$IPTABLES -A udpincoming_packets -p UDP -s 0/0 --source-port 53 -j ACCEPT # DNS
$IPTABLES -A udpincoming_packets -p UDP -s 0/0 -