站長新書 站長開講 首頁 最新文章 站長著作及審校 FreeBSD 筆記 Linux 筆記 Windows 筆記 虛擬化筆記 網管人雜誌 遊山玩水 關於本站
站長新書 VMware vSphere ICM 團購開跑了!!

IPFilter-利用 IPfilter 輕鬆達成 NAT

前言

NAT 伺服器主要是用來簡化及保有 IP 位址,它可讓原本無法上網且使用內部IP位址的主機可以成功的連接 Internet。如此將大大減少 IP 位址的需求,因為基本上整個內部網路都可藉 NAT 上的一個外部 IP 來連至 Internet。[IPFilter] 因內建 NAT 功能,所以在 NAT(ipnat) 及防火牆 (ipf) 搭配上更有效率,且判斷封包的處理則使用快取與雜湊技術來達成,因此更能有效提升處理效率。本次實作以 [IPFilter] 實作 Protocol Filter 及 NAT 功能 (IPFilter 預設是 pass all),關於 NAT 可參考站內文章 NAT 伺服器的原理與運作流程

IPFilter 相關名詞了解:

實作環境

安裝及設定

步驟1.修改核心 or 啟動模組

要使系統能支援 IPFilter 功能方法有二種,一是修改核心 (Static Build Kernel) 另一方式為直接載入核心模組 (Load Kernel Module)

 options IPFILTER                     //tells the compile to include IPFILTER as part of its core kernel.
 options IPFILTER_LOG                 //enables the option to have IPF log traffic by writing to the ipl packet logging 
 options PFIL_HOOKS                   //20040127:p1 FreeBSD-SA-04:01.mksnap_ffs(less /usr/src/UPDATING) 5.3則不用此行
 options IPFILTER_DEFAULT_BLOCK       //顧名思義,若加入這行的話則 IPFILTER 預設將由 pass all 變成 block all

編釋完成後,重新開機完成後至 /dev 下檢查是否出現下列檔案,出現下列檔案即可成功

 #ls -l ip*
 crw------- 1 root wheel 79, 3 Feb 3 13:37 ipauth
 crw------- 1 root wheel 79, 0 Feb 3 13:37 ipl
 crw------- 1 root wheel 79, 1 Feb 3 13:37 ipnat
 crw------- 1 root wheel 79, 2 Feb 3 13:37 ipstate
 #vi /boot/loader.conf                //加入如下一行
 ipl_load="YES"

重新開機完成後可查看 ipl.ko 模組是否載入成功

 #kldstat
 Id Refs Address    Size     Name
 1    4 0xc0400000 5cdad0   kernel
 2    1 0xc09ce000 1a378    ipl.ko    //出現此行則表示成功載入 ipl.ko 模組
 3   14 0xc09e9000 537f0    acpi.ko

步驟2.修改 /etc/rc.conf

修改 /etc/rc.conf 以便系統重新開機時能自動啟動 IPFilter 及相關服務

 gateway_enable="YES"                 //開啟 NAT 功能
 ipfilter_enable="YES"                //開啟 ipf firewall 功能
 ipnat_enable="YES"                   //開啟 nat/rdr rules 功能
 ipmon_enable="YES"                   //開啟紀錄 Log 功能
 ipmon_flags="Ds"

步驟3.建立防火牆規則 (ipf.rules)

在開始設定防火牆規則 (ipf.rules) 以前先了解一下 IPFilter 的規則由哪些部份組成 (也可參考 /usr/src/contrib/ipfilter/BNF 及 rules)

因為 IPFilter 讀取 Rule 是以 Last Match 的方式 (即會去執行最後一條 Match 到的 Rule 所定義的事)。但可使用 Quick 關鍵字來跳離 Rule (簡單說就是讀到 Quick 這行Rule 就優先執行而不會再往下 Check Match Rule),了解後就可以開始設定 IPFilter 規則了,以下設定範例為此次實作環境 (實際狀況請依個人網路環境自行調整)

 #vi /etc/ipf.rules
 #########################################
 # Interface Information
 # vr0:Public   61.60.59.58
 # vr1:DMZ      192.168.78.1  
 # rl0:LAN      192.168.88.1,192.168.88.10  
 #########################################
 ### Deny LAN to Internet ICMP
 block in quick on rl0 proto icmp from 192.168.88.0/24 to any icmp-type echo   //LAN 所有電腦無法 Ping 到外部 (Internet)
 ### Deny Internet to Localhost ICMP 
 block in quick on vr0 proto icmp from any to 61.60.59.58 icmp-type echo       //外部 (Internet) 無法 Ping 到這台機器 
 ### Deny Internet to Localhost SSH
 block in quick on vr0 proto tcp from any to 61.60.59.58 port = 22             //禁止其他來源使用 SSH 連線

由上述舉例可知道想禁止什麼樣的協定,要擋在那個網卡相信都了解了,但 IPFilter 預設是 pass all 的所以一個一個禁止協定個人覺得似乎太累了,不如反過來思考我先 pass我要的之後全部 block 掉 (當然你可在編 kernel 時把 options IPFILTER_DEFAULT_BLOCK 編進去讓預設的 pass all 變 block all,但若您在遠端做這件事的話編譯好核心重開機時您就會斷線了!!),一個 Web Server 的基本 IPF 設定內容如下

 #vi /etc/ipf.rules
 #########################################
 # Interface Information
 # em0:61.60.59.58
 #########################################
 ### Pass Loopback
 pass in quick on lo0 all
 pass out quick on lo0 all
 ### Pass Ping
 pass in quick on em0 proto icmp all
 ### Pass IP Range
 pass in quick on em0 from 71.70.69.68/32 to 61.60.59.58/32 keep state
 pass out quick on em0 from 61.60.59.58/32 to 71.70.69.68/32 keep state
 ### Pass HTTP(s) Service
 pass in quick on em0 proto tcp from any to 61.60.59.58/32 port = 80 flags S keep state
 pass out quick on em0 proto tcp from 61.60.59.58/32 port = 80 to any flags S keep state
 pass in quick on em0 proto tcp from any to 61.60.59.58/32 port = 443 flags S keep state
 pass out quick on em0 proto tcp from 61.60.59.58/32 port = 443 to any flags S keep state
 ### Block Internet Private Address to Me
 block in quick on em0 from 192.168.0.0/16 to any
 block in quick on em0 from 172.16.0.0/12 to any
 block in quick on em0 from 10.0.0.0/8 to any
 block in quick on em0 from 127.0.0.0/8 to any
 block in quick on em0 from 0.0.0.0/8 to any
 block in quick on em0 from 169.254.0.0/16 to any
 block in quick on em0 from 192.0.2.0/24 to any
 block in quick on em0 from 204.152.64.0/23 to any
 block in quick on em0 from 224.0.0.0/3 to any
 ### Block frags
 block in quick on em0 all with frags
 ### Block short tcp packets
 block in quick on em0 proto tcp all with short
 ### Blcok source routed packets
 block in quick on em0 all with opt lsrr
 block in quick on em0 all with opt ssrr
 ### Block nmap OS fingerprint attempts
 block in quick on em0 proto tcp all flags FUP
 ### Block anything with special options
 block in quick on em0 all with ipopts
 ### Deny Another
 block return-rst in quick on em0 proto tcp all
 block return-icmp-as-dest(port-unr) in on em0 proto udp all
 block in quick on em0 all

設定完後套用新規則執行指令 [FreeBSD Man Pages - ipf]

 #ipf -Fa -f /etc/ipf.rules         //-Fa 為清除所有的過濾規則,-f 為載入過濾規則

步驟4.建立 NAT / RDR 規則 (ipnat.rules)

建立 NAT 規則以下設定範例為此次實作環境 (實際狀況請依個人網路環境自行調整),為何 DMZ 跟 LAN 要在不同網段? 主要考量就是把 DMZ 跟 LAN 切開,而 LAN 跟 DMZ 溝通則由 Gateway bind 一個 LAN 網段的 IP 在利用 RDR 對應到 DMZ 實體 IP 且只開必要的 Port 通行即可,如此安全性將更加提升。當然你可以把 DMZ 跟 LAN 的 IP 在同一網段這樣你的 ipnat rules 將會更簡短。[FreeBSD Man Pages - ipnat]

 #vi /etc/ipnat.rules
 #########################################
 #Interface Information
 #xl0:Public   61.60.59.58
 #xl1:DMZ      192.168.78.1  
 #xl2:LAN      192.168.88.1,192.168.88.10  
 #########################################
 ### NAT
 map xl0 192.168.0.0/16 -> 61.60.59.58/32                              //讓 LAN 及 DMZ 出 Internet
 ### Public to DMZ
 map xl0 192.168.78.10/32 -> 61.60.59.58/32 
 rdr xl0 61.60.59.58/32 port 25 -> 192.168.78.10 port 25 tcp           //將 Public IP 25 Port 對應至 DMZ IP 25 Port
 rdr xl0 61.60.59.58/32 port 110 -> 192.168.78.10 port 110 tcp         //將 Public IP 110 Port 對應至 DMZ IP 110 Port
 rdr xl0 61.60.59.58/32 port 80 -> 192.168.78.10 port 80 tcp           //將 Public IP 80 Port 對應至 DMZ IP 80 Port
 ### LAN to DMZ ip
 map xl0 192.168.78.10/32 -> 192.168.88.10/32                          //使 LAN 網段能跟提供服務的 DMZ 網段來溝通
 rdr xl0 192.168.88.10/32 port 20 -> 192.168.78.10/32 port 20 tcp/udp  //將 LAN 網段 IP 20 Port 對應到 DMZ 實體 IP 20 Port
 rdr xl0 192.168.88.10/32 port 21 -> 192.168.78.10/32 port 21 tcp/udp  //將 LAN 網段 IP 21 Port 對應到 DMZ 實體 IP 21 Port
 rdr xl0 192.168.88.10/32 port 22 -> 192.168.78.10/32 port 22 tcp      //將 LAN 網段 IP 22 Port 對應到 DMZ 實體 IP 22 Port
 rdr xl0 192.168.88.10/32 port 25 -> 192.168.78.10/32 port 25 tcp      //將 LAN 網段 IP 25 Port 對應到 DMZ 實體 IP 25 Port
 rdr xl0 192.168.88.10/32 port 53 -> 192.168.78.10/32 port 53 tcp/udp  //將 LAN 網段 IP 53 Port 對應到 DMZ 實體 IP 53 Port
 rdr xl0 192.168.88.10/32 port 80 -> 192.168.78.10/32 port 80 tcp      //將 LAN 網段 IP 80 Port 對應到 DMZ 實體 IP 80 Port
 rdr xl0 192.168.88.10/32 port 110 -> 192.168.78.10/32 port 110 tcp    //將 LAN 網段 IP 110 Port 對應到 DMZ 實體 IP 110 Port

設定完後套用新規則執行指令 (更詳細參數可參考 man ipnat) 而有關 Port Number 及 tcp/udp 可參考 /etc/services

 #ipnat -CF                                                            //清除現有的轉換規則 
 #ipnat -f /etc/ipnat.rules                                            //讀取轉換規則 
 #ipnat -l                                                             //查看當前設置生效的規則
 #ipnat -s                                                             //查看進出狀態

步驟5.使用 ipfstat 查看過濾訊息

利用 ipfstat 指令來查看 IPFilter 過濾訊息,順便了解目前套用規則的過濾數以作為 Loading 的依據。[FreeBSD Man Pages - ipfstat]

 #ipfstat -in                                                          //查看 IPF Rules Input 部份
 #ipfstat -on                                                          //查看 IPF Rules Output 部份
 #ipfstat -ih                                                          //查看 Input Rules 過濾數
 #ipfstat -oh                                                          //查看 Output Rules 過濾數

步驟6.建立 Log

若前面修改 /etc/rc.conf 時有加入 ipmon 選項希望紀錄 IPFilter 的過濾訊息則必需有些相關設定如下。[FreeBSD Man Pages - ipmon]

 #touch /var/log/ipfilter.log                                          //建立 Log 檔
 #vi /etc/syslog.conf                                                  //修改 syslog 設定
 local0.*                /var/log/ipfilter.log
 #/etc/rc.d/syslogd restart                                            //重新啟動 syslogd 服務以便載入新設定

當然建立完成後別忘了去修改 /etc/newsyslog.conf 不然 ipfilter.log 將日漸肥大最後 /var 空間當然就是爆啦。

參考

[ IPFilter 的官方網站]

[FreeBSD Man Pages - ipf]

[FreeBSD Man Pages - ipnat]

[FreeBSD Man Pages - ipfstat]

[FreeBSD Man Pages - ipmon]

[ IP Filter FAQ]

[ IP Filter Based Firewalls HOWTO]

[FreeBSD Handbook 24.5 The IPFILTER (IPF) Firewall]

[Newzilla 以 FreeBSD 為例,架設自己的防火牆]

[FreeBSD使用大全-設置和使用ipfilter]

Me FAQ

Q1.無法使用載入核心模組 (load kernel module) 來運作IPFilter?

Error Message:

 KLD file ipl.ko - could not finalize loading //發生開機訊息 (dmesg) 有此行錯誤
 link_elf: symbol in6_cksum undefined         //而當您想手動載入模組 (kldload ipl.ko) 則會出現此錯誤訊息 

Ans:

基本上會出現這樣的錯誤是因為編 kernel 時有把 INET6 選項給 mark 起來,就會出現如上所述錯誤訊息

 解決方法有三:(適個人喜好擇一)
 1.編 kernel 方式載入IPFILTER 
 2.重新修改 kernel 時 INET6 選項不要 mark 掉
 3.修改 /etc/make.conf 加入如下行(這樣才會避免不打開 IPv6 支援)
 NOINET6=yes     //5.x
 NO_INET6=yes    //6.x

[FreeBSD-Bug Could not load ipl.ko when no INET6 in the kernel]

[FreeBSD-Question ipfilter loading on 5.3]

Go To Oddmuse OrgGo To FreeBSD OrgCreative Commons 2.5 Taiwansitestates.com