## PF 防火墙
当我们使用 Linux 时,iptables 能够很容易的实现对入站和出站的访问控制,也能够很容易的完成端口转发与 NAT 转化等需求。那么在 Mac 上该怎么做到呢?
实际上,macOS 预置了从 OpenBSD 引入的 PF 防火墙。
## 基本结构
PF Firewall 主要由以下三个文件完成配置和加载
* **/etc/pf.conf**
PF Firewall 的配置文件,规则的存放位置,锚点 (anchors) 文件也可以在这里被指定进行加载。
* **/etc/pf.conf/anchors/***
pf.conf 中需要加载的锚点文件的存放目录。
* **/System/Library/LaunchDaemons/com.apple.pfctl.plist**
在启动时执行 "pfctl -f /etc/pf.conf" 完成 PF Firewall 的加载。
## 修改配置文件
你可以直接修改 /etc/pf.conf, 但是该文件会在系统每次升级后被重置。因此,最好的办法是另外写一份配置文件 /opt/pf.conf,然后设置为开机自动加载。
> 配置文件
en3 是当前正在使用的物理网卡,请根据情况配置
```config
# PF Set
set block-policy drop
set fingerprints '/etc/pf.os'
set skip on lo0
scrub-anchor "com.apple/*"
# NAT NET
nat on en3 from 198.51.100.0/24 to any -> (en3)
# TABLES
table <Everyone> {0.0.0.0/0 ::/0 } persist
table <192.168-net> {192.168.0.0/16 } persist
table <10-net> {10.0.0.0/8 } persist
table <172.16-net> {172.16.0.0/12 } persist
table <IPv6-net> {fe80::/10 } persist
table <169.254-net> {169.254.0.0/16 } persist
table <bruteforce> { } persist
table <Blacklist> { 104.24.28.50 104.24.29.50 } persist
table <sshguard> { } persist
table <spam> { } persist
table <NatLanInterfaces> {en3 }
# Inbond Block
block in quick from <sshguard> to any label "SSHGuard_BruteForce"
block in quick proto tcp from <spam> to any port {25 465 }
block in log quick from <Blacklist> to any label "BlackList_IN"
block out log quick from any to <Blacklist> label "BlackList_OUT"
block in quick from no-route to any label "NO_BACK_ROUTE"
block in quick from urpf-failed label "uRPF"
#block log inet all label "Generic_blocks_(IPv4)"
block log inet6 all label "Generic_blocks_(IPv6)"
# MDNS
pass proto igmp allow-opts
pass quick from any to {224.0.0.0/4 ff00::/8} allow-opts
pass in quick proto udp from any port {5353} to any port {5353} allow-opts
pass out quick proto udp from any port {5353} to any port {5353} allow-opts
# APPLE ANCHOR
anchor 'com.apple/*'
load anchor 'com.apple' from '/etc/pf.anchors/com.apple'
# INBOUND ALLOW
block in quick from <bruteforce> to any label "Inbound"
block in proto {tcp, udp} from any to any port {49152:65535} label "Inbound"
block in proto {tcp, udp} from any to any port {53 67 68 123 389 546 547 636 5353 5354} label "Inbound"
pass in proto {tcp, udp} from <192.168-net> to any port {53 67 68 123 389 546 547 636 5353 5354} flags S/SA keep state label "Inbound"
pass in proto {tcp, udp} from <10-net> to any port {53 67 68 123 389 546 547 636 5353 5354} flags S/SA keep state label "Inbound"
pass in proto {tcp, udp} from <172.16-net> to any port {53 67 68 123 389 546 547 636 5353 5354} flags S/SA keep state label "Inbound"
pass in proto {tcp, udp} from <IPv6-net> to any port {53 67 68 123 389 546 547 636 5353 5354} flags S/SA keep state label "Inbound"
pass in proto {tcp, udp} from <192.168-net> to any port {49152:65535} flags S/SA keep state label "Inbound"
pass in proto {tcp, udp} from <10-net> to any port {49152:65535} flags S/SA keep state label "Inbound"
pass in proto {tcp, udp} from <172.16-net> to any port {49152:65535} flags S/SA keep state label "Inbound"
pass in proto {tcp, udp} from <IPv6-net> to any port {49152:65535} flags S/SA keep state label "Inbound"
# OUTBOUND
pass out proto {tcp, udp} from any to any port {1:65535} label "Outbound"
# OUTBOUND NAT
pass inet proto {tcp, udp} from { 198.51.100.0/24 } to !<NatLanInterfaces> port {1:65535} label "NAT_Clients"
```
## 创建执行脚本
为了完成 pf.conf 的加载,我们需要创建一份开机脚本来加载 pf.conf 并打开系统的路由转发功能,这是 NAT 规则所需要的。
创建 /etc/mypfctl.sh,并修改权限
```
chown root:wheel /etc/mypfctl.sh
chmod a+x /etc/mypfctl.sh
```
> 脚本文件
```
#!/bin/sh
# Trap On TERM Signals
trap 'exit 1' 15
# Wait For All The Interfaces
ipconfig waitall
sleep 5
# System Sysctl
sysctl -w net.inet6.ip6.fw.verbose=0
sysctl -w net.inet.ip.fw.verbose=0
sysctl -w net.inet.ip.fw.verbose_limit=0
# Enable Interface Forwarding
sysctl -w net.inet.ip.forwarding=1
# Enable PF And Load Rules
/sbin/pfctl -e
/sbin/pfctl -Ef /opt/pf.conf
# Enable PF Logs
ifconfig pflog0 create
# /usr/local/bin/pfloggerd
/usr/sbin/tcpdump -lnettti pflog0 | /usr/bin/logger -t pf -p local2.info
# Exit With a Clean Status
exit 0
```
## 设置开机启动
/etc/mypfctl.sh 虽然已经创建完成,但是并不会在开机时自动执行。可以使用 Launch Daemon 来完成 /etc/mypfctl.sh 的开机自动加载。
* 创建 /Library/LaunchDaemons/pf.mypfctl.plist 并修改权限。
```
chmod 644 /Library/LaunchDaemons/pf.mypfctl.plist
chown root:wheel /Library/LaunchDaemons/pf.mypfctl.plist
```
* 编辑 /Library/LaunchDaemons/pf.mypfctl.plist
```
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>pf.firewall</string>
<key>ProgramArguments</key>
<array>
<string>/bin/sh</string>
<string>-c</string>
<string>/etc/mypfctl.sh</string>
</array>
<key>RunAtLoad</key>
<true/>
<key>ExitTimeOut</key>
<integer>1</integer>
</dict>
</plist>
```
## 查看实时日志
```
clear; sudo /usr/sbin/tcpdump -lnettti pflog0
```