分类: IT职场
2005-12-02 14:07:53
近日因为宿舍的adsl猫的路由功能被电信屏蔽,因此使用FreeBSD5.1+ipf+ipnat+ipfw+双网卡+pppoe搞定带流量控制的透明网关。
原来使用ipfw+natd,因为pppoe得到的tun0的ip是动态的,无法做到端口映射。虽然可以成功代理和可控,但是没有达到预定目的,故弃用。
下面是我的配置过程:
(1)当然是重新编译内核。(注:如果你是新装的系统,建议修改完cpu类型以后来一次make world,可以提高系统的效率。)
添加下列项目:
options IPSTEALTH 是防火墙透明
options IPFILTER 打开ipf
options IPFILTER_LOG 打开ipf的日志
options IPFILTER_DEFAULT_BLOCK ipf默认规则,deny all
然后编译内核。
注意:如果要ipf和ipfw同时使用,切记要将ipf编译到内核里面,然后再kld ipfw。如果将两者都编译进内核会导致系统启动的时候卡在一个检测网络接口的地方。
(2)安装新内核以后,就是详细配置相关规则了。
(a):ppp拨号的设置比较简单,下面就给出一个示范性的ppp.conf文件,内容如下:
default:
set log Phase Chat LCP IPCP CCP tun command
set redial 15 28800
set reconnect 15 28800
pppoe:
#加入在kernel中配置的会连接ADSL设备的网卡的名称,我的是rl0
set device PPPoE:rl0
set mru 1492
set mtu 1492
set speed sync
enable lqr
set lqrperiod 5
set cd 5
set dial
set login
set timeout 0
set authname Your UserName used to login
set authkey Your Password used to login
set ifaddr 10.0.0.1/0 10.0.0.2/0 255.255.255.0 0.0.0.0
add default HISADDR # Add a (sticky) default route
enable dns
(b):在rc.conf中添加下列项目:
ppp_enable="YES"
ppp_mode="ddial" 断线自动重拨
ppp_mode="background"
ppp_profile="pppoe" 根据你的ppp.conf文件设置,这里我使用的是pppoe
ipfilter_enable="YES"
ipnat_enable="YES'
gateway_enable="YES"
然后重新启动系统使系统自动拨号,启用ipf防火墙。
注意:因为默认的ipf规则是block all,因此使用网络管理配置的此时会失去与系统的联系。因此需要预先在/etc目录中添加暂时性的ipf.rules项目。
暂时性的ipf.rules内容如下:pass in all
pass out all
(c):重新启动以后就可以看到ipf已经起到作用了。又因为我们使用的是pppoe拨号,因此得到的是动态ip。我们要想详细配置防火墙和端口映射就需要写一个脚本(ipf.sh)来自动生成ipf.rules和ipnat.rules。然后把它放到自启动目录中,加上可执行属性,使系统启动以后可以自动生成规则集。
示范性脚本如下:
#!/bin/sh
##########################################
#ipf.sh 是一个自动检测ADSL分配的IP地址和利用这个IP地址动态创建IPFILTER过
#滤规则的脚本。
#使用方法:
#步骤一:要确定使用ipf.sh脚本的服务器能正常使用ADSL拨号和IPFILTER能正常运
#行。
#步骤二:把ipf.sh脚本修改成你的实际网络情况。修改如下:
#内部网卡设备号、内部网段、服务器IP地址、ADSL拨号网络设备号或自己增加网络设备
#和相应的过滤规则。
#步骤三:把ipf.sh移动到 /usr/local/etc/rc.d中,记得加上可执行属性。
###############################################################
#内部网卡设备号vr0
INTARNDEV="vr0"
INTARN=`ifconfig $INTARNDEV | grep inet | cut -d ' ' -f 2`
#内部网段192.168.0.0/24
INTARNNET="192.168.0.0/24"
#服务器IP地址
#注意:服务器IP地址为0.0.0.0时是不会做相应的服务映射,只在填写了正确的IP地址后才会映射到该IP地址上。
INTARNFTP="0.0.0.0" #FTP服务器
INTARNSSH="0.0.0.0" #SSH服务器
INTARNEMAIL="0.0.0.0" #EMAIL服务器
INTARNDNS="0.0.0.0" #DNS服务器
INTARNWEB="0.0.0.0" #WEB服务器
INTARNSSL="0.0.0.0" #SSL服务器
INTARNRTSP="0.0.0.0" #RTSP服务器
INTARNMYSQL="0.0.0.0" #MYSQL服务器
INTARNJABBER="0.0.0.0" #JABBER服务器
#ADSL拨号网络设备号tun0
ADSLDEV="tun0"
ADSLIP=`ifconfig $ADSLDEV | grep inet | cut -d ' ' -f 2`
until [ $ADSLIP ]
do
sleep 5
done
#动态生成ipnat.rules规则
echo '######################################################' >; /etc/ipnat.rules
echo '#/etc/ipnat.rules #' >;>; /etc/ipnat.rules
echo '######################################################' >;>; /etc/ipnat.rules
echo ''
echo '#把所有的内部网络IP伪装成ADSL拨号IP' >;>; /etc/ipnat.rules
echo 'map '$ADSLDEV' '$INTARNNET' ->; '$ADSLIP'/32 portmap tcp/udp 10000:65000' >;>; /etc/ipnat.rules
if [ $INTARNFTP != "0.0.0.0" ]
then
echo ''
echo '#把对ADSL拨号IP的FTP服务映射到服务网络的FTP服务器上' >;>; /etc/ipnat.rules
echo 'rdr '$ADSLDEV' '$ADSLIP'/32 port 20 ->; '$INTARNFTP' port 20' >;>; /etc/ipnat.rules
echo 'rdr '$ADSLDEV' '$ADSLIP'/32 port 21 ->; '$INTARNFTP' port 21' >;>; /etc/ipnat.rules
fi
if [ $INTARNSSH != "0.0.0.0" ]
then
echo ''
echo '#把对ADSL拨号IP的SSH服务映射到服务网络的网管工作站上' >;>; /etc/ipnat.rules
echo 'rdr '$ADSLDEV' '$ADSLIP'/32 port 22 ->; '$INTARNSSH' port 22' >;>; /etc/ipnat.rules
fi
if [ $INTARNEMAIL != "0.0.0.0" ]
then
echo ''
echo '#把对ADSL拨号IP的EMAIL服务映射到服务网络的EMAIL服务器上' >;>; /etc/ipnat.rules
echo 'rdr '$ADSLDEV' '$ADSLIP'/32 port 25 ->; '$INTARNEMAIL' port 25' >;>; /etc/ipnat.rules
echo 'rdr '$ADSLDEV' '$ADSLIP'/32 port 110 ->; '$INTARNEMAIL' port 110' >;>; /etc/ipnat.rules
fi
if [ $INTARNDNS != "0.0.0.0" ]
then
echo ''
echo '#把对ADSL拨号IP的DNS服务映射到服务网络的DNS服务器上' >;>; /etc/ipnat.rules
echo 'rdr '$ADSLDEV' '$ADSLIP'/32 port 53 ->; '$INTARNDNS' port 53' >;>; /etc/ipnat.rules
fi
if [ $INTARNWEB != "0.0.0.0" ]
then
echo ''
echo '#把对ADSL拨号IP的WEB服务映射到服务网络的WEB服务器上' >;>; /etc/ipnat.rules
echo 'rdr '$ADSLDEV' '$ADSLIP'/32 port 80 ->; '$INTARNWEB' port 80' >;>; /etc/ipnat.rules
fi
if [ $INTARNSSL != "0.0.0.0" ]
then
echo ''
echo '#把对ADSL拨号IP的SSL服务映射到服务网络的SSL服务器上' >;>; /etc/ipnat.rules
echo 'rdr '$ADSLDEV' '$ADSLIP'/32 port 443 ->; '$INTARNSSL' port 443' >;>; /etc/ipnat.rules
fi
if [ $INTARNRTSP != "0.0.0.0" ]
then
echo ''
echo '#把对ADSL拨号IP的RTSP服务映射到服务网络的RTSP服务器上' >;>; /etc/ipnat.rules
echo 'rdr '$ADSLDEV' '$ADSLIP'/32 port 554 ->; '$INTARNRTSP' port 554' >;>; /etc/ipnat.rules
fi
if [ $INTARNMYSQL != "0.0.0.0" ]
then
echo ''
echo '#把对ADSL拨号IP的MYSQL服务映射到服务网络的MYSQL服务器上' >;>; /etc/ipnat.rules
echo 'rdr '$ADSLDEV' '$ADSLIP'/32 port 3306 ->; '$INTARNMYSQL' port 3306' >;>; /etc/ipnat.rules
fi
if [ $INTARNJABBER != "0.0.0.0" ]
then
echo ''
echo '#把对ADSL拨号IP的JABBER(客户端口5222和服务器端口5269)服务映射到服务网络的JABBER服务器上' >;>; /etc/ipnat.rules
echo 'rdr '$ADSLDEV' '$ADSLIP'/32 port 5222 ->; '$INTARNJABBER' port 5222' >;>; /etc/ipnat.rules
echo 'rdr '$ADSLDEV' '$ADSLIP'/32 port 5269 ->; '$INTARNJABBER' port 5269' >;>; /etc/ipnat.rules
fi
#动态生成ipf.rules规则
echo '#######################################################' >; /etc/ipf.rules
echo '#/etc/ipf.rules #' >;>; /etc/ipf.rules
echo '#######################################################' >;>; /etc/ipf.rules
echo '#阻塞所有存在安全问题的数据包'>;>; /etc/ipf.rules
echo 'block in log quick all with short' >;>; /etc/ipf.rules
echo 'block in log quick all with ipopts' >;>; /etc/ipf.rules
echo 'block in log quick all with frag' >;>; /etc/ipf.rules
echo 'block in log quick all with opt lsrr' >;>; /etc/ipf.rules
echo 'block in log quick all with opt ssrr' >;>; /etc/ipf.rules
echo '' >;>; /etc/ipf.rules
echo '#外部网络的数据只有FTP(使用20和21端口)、www、dns、smtp、pop3、mysql、ssh、rtsp、jabber和ssl的服务可以进入' >;>; /etc/ipf.rules
if [ $INTARNFTP != "0.0.0.0" ]
then
echo 'pass in quick on '$ADSLDEV' proto tcp from any to any port = 20 keep state' >;>; /etc/ipf.rules
echo 'pass in quick on '$ADSLDEV' proto tcp from any to any port = 21 keep state' >;>; /etc/ipf.rules
fi
if [ $INTARNSSH != "0.0.0.0" ]
then
echo 'pass in quick on '$ADSLDEV' proto tcp from any to any port = 22 keep state' >;>; /etc/ipf.rules
fi
if [ $INTARNEMAIL != "0.0.0.0" ]
then
echo 'pass in quick on '$ADSLDEV' proto tcp from any to any port = 25 keep state' >;>; /etc/ipf.rules
echo 'pass in quick on '$ADSLDEV' proto tcp from any to any port = 110 keep state' >;>; /etc/ipf.rules
fi
if [ $INTARNDNS != "0.0.0.0" ]
then
echo 'pass in quick on '$ADSLDEV' proto udp from any to any port = 53 keep state' >;>; /etc/ipf.rules
echo 'pass out quick on '$ADSLDEV' proto udp from any port = 53 to any keep state' >;>; /etc/ipf.rules
fi
if [ $INTARNWEB != "0.0.0.0" ]
then
echo 'pass in quick on '$ADSLDEV' proto tcp from any to any port = 80 keep state' >;>; /etc/ipf.rules
fi
if [ $INTARNSSL != "0.0.0.0" ]
then
echo 'pass in quick on '$ADSLDEV' proto tcp from any to any port = 443 keep state' >;>; /etc/ipf.rules
fi
if [ $INTARNRTSP != "0.0.0.0" ]
then
echo 'pass in quick on '$ADSLDEV' proto tcp from any to any port = 554 keep state' >;>; /etc/ipf.rules
echo 'pass in quick on '$ADSLDEV' proto udp from any to any port = 554 keep state' >;>; /etc/ipf.rules
fi
if [ $INTARNMYSQL != "0.0.0.0" ]
then
echo 'pass in quick on '$ADSLDEV' proto tcp from any to any port = 3306 keep state' >;>; /etc/ipf.rules
fi
if [ $INTARNJABBER != "0.0.0.0" ]
then
echo 'pass in quick on '$ADSLDEV' proto tcp from any to any port = 5222 keep state' >;>; /etc/ipf.rules
echo 'pass in quick on '$ADSLDEV' proto tcp from any to any port = 5269 keep state' >;>; /etc/ipf.rules
fi
echo '' >;>; /etc/ipf.rules
echo '#阻塞内部网络访问以下指定IP地址' >;>; /etc/ipf.rules
echo 'block out quick proto tcp/udp from any to any port = 6995 #不能连接BT ' >;>; /etc/ipf.rules
echo '' >;>; /etc/ipf.rules
echo '#内部网络可以访问外部网络' >;>; /etc/ipf.rules
echo 'pass out log on '$ADSLDEV' proto icmp all keep state' >;>; /etc/ipf.rules
echo 'pass out log on '$ADSLDEV' proto tcp/udp from any to any keep state' >;>; /etc/ipf.rules
echo '' >;>; /etc/ipf.rules
echo '#阻塞外部网络的其它请求' >;>; /etc/ipf.rules
echo 'block return-rst in log on '$ADSLDEV' proto tcp from any to '$ADSLIP' flags S/SA' >;>; /etc/ipf.rules
echo 'block return-icmp(net-unr) in log on '$ADSLDEV' proto udp from any to '$ADSLIP'' >;>; /etc/ipf.rules
echo 'block in log on '$ADSLDEV' all' >;>; /etc/ipf.rules
echo '' >;>; /etc/ipf.rules
echo '#阻塞内部网络访问以下指定IP地址' >;>; /etc/ipf.rules
echo '#block in log quick on '$ADSLDEV' proto tcp from any to 202.106.185.77 flags S/SA #不能连接163.com' >;>; /etc/ipf.rules
echo '' >;>; /etc/ipf.rules
echo '#内部网络的数据全部可以通过防火墙' >;>; /etc/ipf.rules
echo 'pass in on '$INTARNDEV' all' >;>; /etc/ipf.rules
echo 'pass out on '$INTARNDEV' all' >;>; /etc/ipf.rules
echo 'pass in on lo0 all' >;>; /etc/ipf.rules
echo 'pass out on lo0 all' >;>; /etc/ipf.rules
echo '' >;>; /etc/ipf.rules
echo '#让VPN能通过防火墙' >;>; /etc/ipf.rules
echo 'pass in quick on '$ADSLDEV' proto tcp from any to any port = 47 keep state' >;>; /etc/ipf.rules
echo 'pass out quick on '$ADSLDEV' proto tcp from any port = 47 to any keep state' >;>; /etc/ipf.rules
echo 'pass in quick on '$ADSLDEV' proto tcp from any to any port = 1723 keep state' >;>; /etc/ipf.rules
echo 'pass out quick on '$ADSLDEV' proto tcp from any port = 1723 to any keep state' >;>; /etc/ipf.rules
echo 'pass in proto gre from any to any keep state' >;>; /etc/ipf.rules
echo 'pass out proto gre from any to any keep state' >;>; /etc/ipf.rules
echo 'pass in on '$ADSLDEV' all' >;>; /etc/ipf.rules
echo 'pass out on '$ADSLDEV' all' >;>; /etc/ipf.rules
/sbin/ipf -Fa -f /etc/ipf.rules
/sbin/ipnat -CF -f /etc/ipnat.rules
##END IPF.SH###########
(d):因为我们采用的是pppoe拨号,有断线重拨的可能。因此有不断监视和更新ipf、ipnat规则的需要(我们前面已经在rc.conf里面设置ppp断线重拨了,重播以后tun0得到的ip会改变)。因此我们在crontab的root文件里面加上一条规则,使ipf.sh能每隔一段时间(3-5min)自动执行一次。
(e):然后就是启用ipfw来进行流量管理了。干脆写一个脚本来进行了事了。示例脚本如下:
#!/bin/sh
#load ipfw in ipf env
#by fzfh
/sbin/kldload /boot/kernel/dummynet.ko
#加载ipfw和dummynet模块,默认deny all
/sbin/ipfw -f flush
#清空所有ipfw规则
/sbin/ipfw add 00050 pipe 1 all from any to ip
/sbin/ipfw pipe 1 config bw 400Kbit/s
#限制任意地址到ip的总流量不超过400Kbit/s
/sbin/ipfw add 00060 pipe 2 all from ip to any
/sbin/ipfw pipe 2 config bw 400Kbit/s
#限制ip到任意地址的总流量不超过400Kbit/s
/sbin/ipfw add 100 all from any to any
#任意连接通过
然后把ipfw.sh加上可执行属性,放到ipf.sh同一个目录里面在开机时自动执行即可。(/usr/local/etc/rc.d)
注意:加载ipfw模块以后会马上生效,默认是deny all,位置在65535。因此,通过网络进行设置的话需要写一个脚本加载ipfw模块,务必使加载ipfw模块以后要生成一条在65535前通过所有连接的规则。建议序号在10000之前。
(3):到此,已经完成配置。测试达到个人的预定目的。
#########################################
重要增加:
注明:本文在配置的时候参考文章《使用FreeBSD配置基于ADSL的VPN防火墙(IPFILTER)网关 》
陈永光(Charlin) < charlin@88vip.com >;
引用其中动态生成ipf.rules和ipnat.rules的脚本文件。特此说明并感谢!
#########################################
如果个人使用就不需要那么复杂。可以大大精简。下面给出本人自己的精简脚本文件。
动态生成ipf.rules和ipnat.rules的脚本文件:
#!/bin/sh
#power by fzfh
#定义变量
INTARNNET="192.168.0.0/24"
#截取adsl的动态ip
ADSLDEV="tun0"
ADSLIP=`ifconfig $ADSLDEV | grep inet | cut -d ' ' -f 2`
until [ $ADSLIP ]
do
sleep 3
done
#make ipnat.rules
echo '##################' >; /etc/ipnat.rules
echo '#/etc/ipnat.rules #' >;>; /etc/ipnat.rules
echo '##################' >;>; /etc/ipnat.rules
echo 'map '$ADSLDEV' '$INTARNNET' ->; '$ADSLIP'/32 proxy port ftp ftp/tcp' >;>; /etc/ipnat.rules
echo 'map '$ADSLDEV' '$INTARNNET' ->; '$ADSLIP'/32 portmap tcp/udp 10000:50000' >;>; /etc/ipnat.rules #这里的10000:50000可以用auto代替。
echo 'map '$ADSLDEV' '$INTARNNET' ->; '$ADSLIP'/32' >;>; /etc/ipnat.rules
echo 'rdr '$ADSLDEV' '$ADSLIP'/32 port X ->; IP port X' >;>; /etc/ipnat.rules #这里的端口号X和IP可以相应更改。每一个一行。
#make ipf.rules
echo '#############################' >; /etc/ipf.rules
echo '#/etc/ipf.rules #' >;>; /etc/ipf.rules
echo '#############################' >;>; /etc/ipf.rules
echo 'block in log quick all with short' >;>; /etc/ipf.rules
echo 'block in log quick all with ipopts' >;>; /etc/ipf.rules
echo 'block in log quick all with frag' >;>; /etc/ipf.rules
echo 'block in log quick all with opt lsrr' >;>; /etc/ipf.rules
echo 'block in log quick all with opt ssrr' >;>; /etc/ipf.rules
echo '' >;>; /etc/ipf.rues
echo 'pass in quick on '$ADSLDEV' proto tcp from any to any port = X keep state' >;>; /etc/ipf.rules #这里的X就是上面你需要转换的端口。每一个端口一行。可是与此相同。
#如果不需要做别的限制,此处就可以再加上下面两句作结了。
echo 'pass in all' >;>; /etc/ipf.rules
echo 'pass out all' >;>; /etc/ipf.rules
#do thems
/sbin/ipf -Fa -f /etc/ipf.rules
/sbin/ipnat -CF -f /etc/ipnat.rules