1、前言
以下介紹為引用 FreeBSD 使用手冊 第26章 防火牆 26.4 OpenBSD Packet Filter (PF) 和 ALTQ:2003 年 7 月, OpenBSD 的防火牆, 也就是常說的 PF 被成功地移植到了 FreeBSD 上, 並可以通過 FreeBSD Ports Collection 來安裝了; 第一個將 PF 集成到基本系統中的版本是 2004 年 11 月發行的 FreeBSD 5.3。 PF 是一個完整的提供了大量功能的防火牆軟件, 並提供了可選的 ALTQ (交錯隊列, Alternate Queuing) 功能。
ALTQ 提供了服務品質 (QoS) 帶寬整形功能, 這個功能能夠以基於過濾規則的方式來保障不同服務的帶寬。 OpenBSD Project 在維護 PF 用戶指南方面已經做了非常卓越的工作,因此我們不打算在這本使用手冊中進行更進一步的闡述, 以避免不必要的重複勞動。
警告: 在瀏覽 pf 用戶手冊時,請時刻注意,在 FreeBSD 中所包含的 pf 的版本和 OpenBSD 中是不一樣的。 在 FreeBSD 5.X 中 pf 相當於 OpenBSD 3.5 中的版本, 而 FreeBSD 6.X 中則相當於 OpenBSD 3.7。更多的詳細信息, 可以在 FreeBSD 版本的 PF 網站 上找到。
文章目錄
1、前言2、實作環境
3、安裝及設定
步驟1.修改核心啟用 PF
步驟2.修改/etc/rc.conf
步驟3.修改 PF 設定檔 (pf.conf)
步驟4.PF 常用指令
4、參考
5、Me FAQ
Q1. pfctl: /dev/pf: No such file or directory?
Q2. 所設定的 PF 都無法生效?
Q3. 設定 route-to 時語法錯誤?
2、實作環境
- FreeBSD 6.x
3、安裝及設定
若您不需要 PF ALTQ 功能則可不用透過修改核心來支援 PF,請直接在 /etc/rc.conf 內加入 pf_enable="YES" 即可,並跳到步驟 3 繼續,不過前提是您必須要確定你的核心檔內下列三個選項沒有註解掉。 options INET
options INET6
device bpf
步驟 1. 修改核心啟用 PF
修改核心加入如下項目來啟用 Packet Filter 防火牆。 device pf //啟動 Packet Filter 防火牆
device pflog //啟動 pflog0 網卡並以 bpf 格式來記錄網路流量
device pfsync //啟動 pfsync0 網卡用來監控「狀態的改變」
options ALTQ //啟動 ALTQ 功能 (注意不是所有網卡皆支援 ALTQ 功能)
options ALTQ_CBQ //啟動 Class Bases Queuing (CBQ) 功能
步驟 2. 修改 /etc/rc.conf
修改 /etc/rc.conf 中加入下列的設定以便在系統重新開機時時會啟動 PF 服務。 pf_enable="YES" //啟動 PF (如果需要的話載入模組)
pflog_enable="YES" //啟動 pflogd 功能
gateway_enable="YES" //啟動 LAN Gateway (若要做 NAT 的話)
步驟 3. 修改 PF 設定檔 (pf.conf)
PF,Packet Filter 防火牆特性為 Default Pass ALL、Last Match Rules 當撰寫適合自已的 pf.conf 時記得 pf.conf 語法順序如下:- 巨集 Macro: 包含 IP 位址、介面名稱等自定變數
- 表格 Tables: 包含 IP 位址列表的表格
- 選項 Options: 各種控制 PF 運作的選項
- 清理 Traffic Normalization: 正規化與重組分段封包等再處理
- 佇列 Queueing: 提供頻寬控制與封包優先順序
- 轉譯 Translation: 控制網路位址轉譯與封包轉向
- 過濾規則 Packet Filtering: 當封包通過任何網路介面時允許選擇性的過濾或阻擋封包
遠端操作 PF 注意事項
當您在遠端操作 PF 若是沒有十足的把握最好不要亂動,因為可能動一動之後把自已擋掉了在 #bsdchat 上看到順便就記下來吧。
16:01 <@f0rth> pf -f 的時候不忘加上 ;sleep 10; pf -d
16:02 <@priv> f0rth: "pf設定已經變更,請問是否確認,二十秒鐘後恢復原設定值(Y/N)"
16:03 <@priv> 要這樣?
範例 1. 簡單 Web Server PF 設定檔
一個 Web Server 的基本 PF 設定內容如下 (Service FTP、SSH、DNS、HTTP),以下僅為範例實際請依個人網路環境自行調整:# vi /etc/pf.conf
### Macros: define common values, so they can be referenced and changed easily.
ext_if="em0"
private_addr="{192.168.0.0/16, 192.168.0.0/16, 10.0.0.0/8, 127.0.0.0/8, 0.0.0.0/8, 169.254.0.0/16, 192.0.2.0/24,
204.152.64.0/23, 224.0.0.0/3}"
pass_in_service="{53 80}"
### Normalization: reassemble fragments and resolve or reduce traffic ambiguities.
scrub in all
### Filtering: the implicit first two rules are
### Pass Loopback
pass in quick on lo0 all
pass out quick on lo0 all
### Block the ftp ssh bruteforce bastards
block drop in quick on $ext_if from <ssh-bruteforce>
### Pass Ping
pass in quick on $ext_if proto icmp all
### Special rule for FTP/SSH
pass in quick on $ext_if proto tcp from any to ($ext_if) port { ftp ssh } flags S/SA keep state (max-src-conn-rate 3/90,
overload <ssh-bruteforce> flush global)
### Pass FTP
pass in quick on $ext_if proto tcp from any to $ext_if port > 49151 keep state
### Pass IN Service DNS HTTP
pass in quick on $ext_if proto { tcp, udp } from any to $ext_if port $pass_in_service keep state
### Pass Localhost to Any
#pass out quick on $ext_if all keep state
### Block Internet Private Address to Me
block in quick on $ext_if from $private_addr to any
### Block Another all
block return-rst in quick on $ext_if proto tcp all
block return-icmp(net-unr) in quick on $ext_if proto udp all
block in quick on $ext_if all
範例 2. 阻擋惡意 Try SSH/FTP 的 PF 設定檔
上面範例中可能只有一個地方比較不容易看懂,就是有關於會自動阻擋惡意嘗試連結您主機 SSH 及 FTP 服務的 IP 部份,詳細內容可參考 阻擋惡意 Try SSH/FTP IP。### special rule for ssh/ftp
pass in on $ext_if proto tcp from any to ($ext_if) port { ftp ssh } flags S/SA keep state (max-src-conn-rate 3/30,
overload <ssh-bruteforce> flush global)
### block the ssh bruteforce bastards
block drop in quick on $ext_if from <ssh-bruteforce>
上面二條 PF 規則運作流程大概是這樣:
- 首先 Pass 對外進來的流量
- 記錄連結 Port {ftp ssh} 30 秒內 3 次 (max-src-conn-rate 指定時間內允許連結次數)
- 若有符合這樣條件的 IP 就將該 IP 加入 Table <ssh-bruteforce> 內
- 阻擋 (Block) 該 Table <ssh-bruteforce> 內列表的 IP
如何查看惡意嘗試 IP 列表? 執行如下指令即可查詢目前在 ssh-bruteforce table 內的 IP List
# pfctl -t ssh-bruteforce -Tshow //可看到惡意 IP 已加入 table 內
61.95.172.140
61.177.147.97
62.29.248.240
203.129.254.212
210.192.98.99
222.122.60.205
如何手動把惡意 IP 加入 table 內? 若想將已經的惡意 IP 加入 Table ssh-bruteforce 內請執行如下指令
# pfctl -t ssh-bruteforce -T add 218.70.0.0/16 //將 218.70.x.x 網段加入阻擋的 table 內
定期清除 Table <ssh-bruteforce> 內列表的 IP方法? 下列方式請依個人喜好擇一即可
定期清除 Table 內 IP 列表 (方式一)
/usr/ports/security/expiretable //安裝 expiretable
*/5 * * * * /usr/local/sbin/expiretable -t 3600 ssh-bruteforce //定期清除 Table <ssh-bruteforce>
手動清除 Table 內 IP 列表 (方式二)
# pfctl -t ssh-bruteforce -T delete 218.70.0.0/16
範例 3. Gateway Server PF 設定檔
下列範例為使用 PF 當成 Gateway,負責的服務有 NAT (2 Routing)、WAN to DMZ 及 LAN to DMZ 的 Port Forwarding、Block Bad User IP,以下僅為範例實際請依個人網路環境自行調整。/etc/rc.conf 內容
### TFN FTTB ###
defaultrouter="219.20.230.254" #TFN
ifconfig_em0="inet 219.22.22.222 netmask 255.255.255.0"
### Hinet ADSL ###
ifconfig_bge0="inet 202.60.43.111 netmask 255.255.255.0"
### DMZ Interface ###
ifconfig_em1="inet 192.168.10.1 netmask 255.255.255.0"
### LAN Interface ###
ifconfig_em2="inet 192.168.20.1 netmask 255.255.255.0"
ifconfig_em2_alias0="inet 192.168.10.2 netmask 255.255.255.255" # DMZ Mail
ifconfig_em2_alias1="inet 192.168.10.8 netmask 255.255.255.255" # DMZ FTP
ifconfig_em2_alias2="inet 192.168.10.9 netmask 255.255.255.255" # DMZ LDAP
#NAT
gateway_enable="YES"
#PF
pf_enable="YES"
pflog_enable="YES"
/etc/pf.conf 內容
###Macros###
ext_if1="em0" #TFN
ext_if2="bge0" #Hinet
dmz_if="em1" #DMZ
lan_if="em2" #LAN
dmz_net="192.168.10.0/24" #DMZ Subnet
lan_net="192.168.20.0/24" #LAN Subnet
weithenn_home="61.60.59.58/32" #Weithenn Home IP
hinet_gw="202.60.43.254/32" #Routing to Hinet Gateway
Allow_ftp_ip="{ 61.60.59.58/32 }" #Allow FTP IP List
icmp_types = "echoreq" #ICMP
lan_use_icmp="{ 192.168.20.137/32 }" #Allow Lan User Use ICMP List
lan_bt="{ 192.168.20.171/32 }" #Lan User Use BT ? Block it
###Tables###
table <ssh-bruteforce> persist
###Options###
set skip on lo0
set block-policy return
###Scrub###
scrub in all
###NAT/RDR###
###1.Lan User Default Routing go TFN
###2.If Lan User will Change Routing go Hinet Please Remark 41,85
###3.If Lan User will Change Routing go KBT Please Remark 82
#NAT on TFN (Primary Gateway)
nat on $ext_if1 from { $dmz_net $lan_net } to any -> ($ext_if1)
#NAT on Hinet (Backup Gateway)
nat on $ext_if2 from $lan_net to any -> ($ext_if2)
#RDR Outside to DMZ Hosts
rdr on $ext_if1 proto tcp from any to $ext_if1/32 port { 25 80 110 } -> 192.168.10.2 #outside to MAIL pub ip
rdr on $ext_if2 proto tcp from $Allow_ftp_ip to $ext_if2 port 21 -> 192.168.10.8 port 21 #outside to FTP
#RDP from weithenn
rdr on $ext_if1 proto tcp from $weithenn_home to $ext_if1/32 port 3389 -> 192.168.79.10 port 3389
#RDR LAN to DMZ Hosts
rdr on $lan_if proto { tcp, udp } from $lan_net to 192.168.88.2/32 port { 22 25 53 80 110 } -> 192.168.10.2 #to MAIL
rdr on $lan_if proto { tcp, udp } from $lan_net to 192.168.88.8/32 port { 21 22 80 137 138 139 389 443 2355 } -> 192.168.10.8 #to ftp
rdr on $lan_if proto { tcp, udp } from $lan_net to 192.168.88.9/32 port { 22 25 53 80 110 137 138 139 389 443 465 995 } -> 192.168.10.9 #to ldap
#RDR LAN outgoing FTP requests to the ftp-proxy
rdr on $lan_if proto tcp from any to any port ftp -> 127.0.0.1 port 8021
###Filter###
pass quick on lo0 all
#AntiSpoof
antispoof quick for $lan_if
#Lan User Routing Change to Hinet
#pass in quick on $lan_if route-to ($ext_if2 $hinet_gw) from $lan_net to ! 192.168.0.0/16
#special rule for ssh/ftp
pass in on $ext_if1 proto tcp from any to ($ext_if1) port { ftp ssh } flags S/SA keep state (max-src-conn-rate 3/30, overload <ssh-bruteforce> flush global)
pass in on $ext_if2 proto tcp from any to ($ext_if2) port { ftp ssh } flags S/SA keep state (max-src-conn-rate 3/30, overload <ssh-bruteforce> flush global)
#block the ssh bruteforce bastards
block drop in quick on $ext_if1 from <ssh-bruteforce>
block drop in quick on $ext_if2 from <ssh-bruteforce>
#LAN use ICMP
pass in quick on $lan_if proto icmp from $lan_use_icmp to any icmp-type $icmp_types
block in quick on $lan_if proto icmp from $lan_net to any icmp-type $icmp_types
#Block Bad User
block in quick on $lan_if from $lan_bt to any keep state queue bt
步驟 4. PF 常用指令
修改完 PF 設定檔 (pf.conf) 後,常使用以下指令來控制 PF 更詳細的用法請參考 Man Page pf.conf。# pfctl -f /etc/pf.conf //重新載入 pf.conf 設定檔
# pfctl -nf /etc/pf.conf //檢查 PF 語法是否正確 (未載入)
# pfctl -e //啟用 PF (開啟 Packet Filter 功能)
# pfctl -d //禁用 PF (關閉 Packet Filter 功能)
# pfctl -Nf /etc/pf.conf //僅載入 NAT 的設定檔
# pfctl -Rf /etc/pf.conf //僅載入防火牆的過濾設定檔
# pfctl -sn //顯示現階段 NAT 的規則
# pfctl -sr //顯示現階段過濾的規則
# pfctl -ss //顯示現階段封包運作狀態
# pfctl -si //顯示現階段過濾封包的統計資料
# pfctl -sa //顯示現階段所有統計的資料
# pfctl -vsr //顯示現階段過濾封包的統計資料
# pfctl -t ssh-bruteforce -Tshow //顯示 table 內資料
# pfctl -Fa -e -f /etc/pf.conf //清除所有規則、啟用 PF、載入 pf.conf 設定檔
4、參考
- PF: The OpenBSD Packet Filter
- pf中文手冊(for openbsd,但同樣適用FREEBSD)
- FreeBSD 使用手冊 第26章 防火牆 26.4 OpenBSD Packet Filter (PF) 和 ALTQ
- FreeBSD with Packet Filter(PF) Firewall - (1)
- FreeBSD with Packet Filter(PF) Firewall - (2)
- FreeBSD with Packet Filter(PF) Firewall - (3)
- FreeBSD with Packet Filter(PF) Firewall - (4)
- FreeBSD with Packet Filter(PF) Firewall - (5)
- FreeBSD with Packet Filter(PF) Firewall - (6)
5、Me FAQ
Q1. pfctl: /dev/pf: No such file or directory?
Error Message:執行 pfctl 指令欲載入 PF 規則時出現如下錯誤訊息
# pfctl -f /etc/pf.conf
pfctl: /dev/pf: No such file or directory
Ans:
原因使用 PF 時未修改核心 (Kernel) 且也尚未載入模組所導致,請鍵入下列指令手動載入 pf.ko 模組。
# kldload pf.ko //載入 pf.ko 模組
# kldstat //查詢目前系統載入的模組
Id Refs Address Size Name
1 4 0xc0400000 6a743c kernel
2 1 0xc0aa8000 6a45c acpi.ko
3 1 0xc470e000 33000 pf.ko //pf.ko 已載入
此時再次執行 pfctl 指令便可順利執行
# pfctl -f /etc/pf.conf
No ALTQ support in kernel
ALTQ related functions disabled
Q2. 所設定的 PF 都無法生效?
Error Message:執行 pfctl 指令載入 PF 規則成功,但測試 PF 功能時卻發現所設定的規則都未生效 (ex. 禁止 Ping)。
Ans:
若是未修改核心 (Kernel) 方式來使用 PF 建議重新開機使系統完全載入相關模組及檔案,在重開機前請先確定於 rc.conf 內加入此行
# vi /etc/rc.conf
pf_enable="YES"
Q3. 設定 route-to 時語法錯誤?
Error Message:當備份線路的 NAT 規則設定為這樣時使用 pfctl -nf /etc/pf.conf 會顯示語法有問題無法套用 PF 規則
nat on $wan2_if from $lan_net to any -> ($wan2_if)
pass in quick on $lan_if route-to ($wan2_if $backup_gw) from $lan_net to ! 10.0.0.0/13
Ans:
將其中的 $backup_gw 不要使用參數,改為使用 IP 位址表示即可順利生效!!
nat on $wan2_if from $lan_net to any -> ($wan2_if)
pass in quick on $lan_if route-to ($wan2_if 61.60.59.254) from $lan_net to ! 10.0.0.0/13