IPFilter - 建置 NAT 機制

1、前言

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


IPFilter 相關技術名詞:
  • ipfilter: 主要作用為管制進出主機 Protocol (tcp,upd,icmp...)
  • ipnat: 主要作用就是 NAT (Network Address Translator) 及 RDR (Redirect)
  • ipfstat: 觀看所有封包狀態並顯示統計數值
  • ipmon: 統計過濾 Log 





文章目錄

1、前言
2、實作環境
3、安裝及設定
          步驟1.修改核心 or 啟動模組
          步驟2.修改 /etc/rc.conf
          步驟3.建立防火牆規則 (ipf.rules)
          步驟4.建立 NAT / RDR 規則 (ipnat.rules)
          步驟5.使用 ipfstat 查看過濾訊息
          步驟6.建立 Log
4、參考
5、Me FAQ
          Q1.無法使用載入核心模組 (load kernel module) 來運作IPFilter?





2、實作環境

  • FreeBSD 5.x-RELEASE
  • IP Filter: v3.4.35





3、安裝及設定

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

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

修改核心 (Static Build Kernel): 為在核心加入下列下三行,並重新編釋核心即可 (cd /usr/src ; make kernel)。
 options IPFILTER
 options IPFILTER_LOG
 options PFIL_HOOKS
 options IPFILTER_DEFAULT_BLOCK    //將由 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

載入核心模組(load kernel module): 為載入模組 *.ko 方式 (不用修改及重新編譯 Kernel)。
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)。

ACTION: 指定 IPFilter 動作 (放行或阻擋)
  • pass: 放行
  • block: 阻擋

DIRECTION: 指定過濾封包的方向
  • in: Internet >> 本機
  • out: 本機 >> internet

OPTIONS: 指定 IPFilter 作用於哪片網卡上
  • log: 把封包傳至記錄檔
  • quick: 套用這條規則
  • on interface-name: 適用於哪片網卡

PROTOCOL: 指定要過濾的協定
  • proto (protocol): 一般為 TCP、UDP、ICMP (其它可參考 /etc/protocols)

SOURCE: 指定封包來源及連接埠
  • from source port: 來源為哪裡

DESTINATION: 指定封包目的地及連接埠
  • to destination port: 目的地為哪裡

PACKET-OPTIONS: 指定處理封包時其它選項
  • flags S/SA keep state: 讓 IPFilter 僅比對剛開始建立連線的封包,只要判斷為 Pass 便會記憶封包能被允許穿越 IPFilter 防火牆,並開放同一個連接埠組讓回傳的封包能快速通過不需在比對

因為 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
 ### Deny Internet to Localhost ICMP
 block in quick on vr0 proto icmp from any to 61.60.59.58 icmp-type echo
 ### Deny Internet to Localhost SSH
 block in quick on vr0 proto tcp from any to 61.60.59.58 port = 22

由上述舉例可知道想禁止什麼樣的協定,要擋在那個網卡相信都了解了,但 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 (Port Redirector)
 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
 rdr xl0 61.60.59.58/32 port 110 -> 192.168.78.10 port 110 tcp
 rdr xl0 61.60.59.58/32 port 80 -> 192.168.78.10 port 80 tcp
 ### LAN to DMZ (Port Redirector)
 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
 rdr xl0 192.168.88.10/32 port 21 -> 192.168.78.10/32 port 21 tcp/udp
 rdr xl0 192.168.88.10/32 port 22 -> 192.168.78.10/32 port 22 tcp
 rdr xl0 192.168.88.10/32 port 25 -> 192.168.78.10/32 port 25 tcp
 rdr xl0 192.168.88.10/32 port 53 -> 192.168.78.10/32 port 53 tcp/udp
 rdr xl0 192.168.88.10/32 port 80 -> 192.168.78.10/32 port 80 tcp
 rdr xl0 192.168.88.10/32 port 110 -> 192.168.78.10/32 port 110 tcp

設定完後套用新規則執行指令 (更詳細參數可參考 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 空間當然就是爆啦。





4、參考






5、Me FAQ

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

Error Message:
發生開機訊息 (dmesg) 有下列錯誤,並且當您想手動載入模組 (kldload ipl.ko) 時也出現錯誤訊息 ?
KLD file ipl.ko - could not finalize loading
 link_elf: symbol in6_cksum undefined


Ans:
基本上會出現這樣的錯誤是因為編 kernel 時有把 INET6 選項註解後,就會出現如上所述錯誤訊息。詳請參考 FreeBSD-Bug Could not load ipl.ko when no INET6 in the kernelFreeBSD-Question ipfilter loading on 5.3

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