在 Mac OS X 上使用 pf.conf 阻止传出流量,除非 OpenVPN 连接处于活动状态

在 Mac OS X 上使用 pf.conf 阻止传出流量,除非 OpenVPN 连接处于活动状态

我已经能够使用 pf.conf 拒绝所有与外部网络的连接,除非我的 OpenVPN 连接处于活动状态。但是,如果通过关闭和打开笔记本电脑盖或关闭和打开 Wi-Fi 来中断连接,我就会失去 Wi-Fi 连接。

  • 我在使用 Mac OS 10.8.1。
  • 我通过 Wi-Fi 连接到网络(从不同位置,包括公共 Wi-Fi)。
  • OpenVPN 连接是使用 Viscosity 设置的。

我在以下位置设置了以下数据包过滤规则/etc/pf.conf

# Deny all packets unless they pass through the OpenVPN connection
wifi=en1
vpn=tun0

block all

set skip on lo
pass on $wifi proto udp to [OpenVPN server IP address] port 443
pass on $vpn

我使用 启动数据包过滤服务sudo pfctl -e并使用 加载新规则sudo pfctl -f /etc/pf.conf

我还编辑/System/Library/LaunchDaemons/com.apple.pfctl.plist并修改了该行<string>-f</string><string>-ef</string>以便数据包过滤器在系统启动时启动。

这一切一开始似乎都很顺利:只有 OpenVPN 连接处于活动状态,应用程序才能连接到网络,因此我永远不会通过不安全的连接泄露数据。

但是,如果我关闭并重新打开笔记本电脑盖或关闭 Wi-Fi 并再次打开,Wi-Fi 连接就会丢失,并且我会在状态栏中的 Wi-Fi 图标中看到一个感叹号。单击 Wi-Fi 图标会显示“警告:无互联网连接”消息:

无互联网连接消息

为了重新连接,我必须断开并重新连接 Wi-Fi,有时要五六次,然后“警告:无互联网连接”消息才会消失,我才能再次打开 VPN 连接。其他时候,Wi-Fi 警报会自动消失,感叹号会消失,我就可以再次连接。无论哪种方式,重新连接可能需要五分钟或更长时间,这可能会令人沮丧。

删除该行block all可以解决问题(但会允许不安全的连接),因此似乎我阻止了 Apple 需要的一项服务,以便重新获得并确认 Wi-Fi 连接。我尝试过:

  • pass on $wifi proto icmp all通过添加到 pf.conf 来启用 icmp
  • 通过添加启用 DNS 解析pass on $wifi proto udp from $wifi to any port 53
  • 尝试通过记录被阻止的数据包(通过更改block allblock log all)来了解更多信息,但 OS X 下的日志记录似乎被禁用,因为查看sudo tcpdump -n -e -ttt -i pflog0日志的结果为“tcpdump:pflog0:不存在这样的设备”。

这些都无法帮助更快地重新建立 Wi-Fi 连接。

我还能做些什么来确定需要哪些服务才能重新获得 Wi-Fi 连接,或者我应该向 pf.conf 添加哪些规则以使 Wi-Fi 重新连接更加可靠?

答案1

通过使用 Little Snitch 监控网络连接,我发现 Apple 在后台使用 mDNSResponder 应用程序来检查 Wi-Fi 连接是否可用。mDNSResponder 向名称服务器发送 UDP 数据包以检查连接性并将主机名解析为 IP。

更改我之前允许的 UDP 规则全部通过 Wi-Fi 的 UDP 数据包允许 mDNSResponder 连接,这意味着 Wi-Fi 现在可以在断开连接后第一次重新连接。为了将来对其他人有所帮助,我最终的 pf.conf 包括 Apple 针对 Mountain Lion 的默认规则,如下所示:

#
# com.apple anchor point
#
scrub-anchor "com.apple/*"
nat-anchor "com.apple/*"
rdr-anchor "com.apple/*"as
dummynet-anchor "com.apple/*"
anchor "com.apple/*"
load anchor "com.apple" from "/etc/pf.anchors/com.apple"

#
# Allow connection via Viscosity only
#
wifi=en1 #change this to en0 on MacBook Airs and other Macs without ethernet ports
vpn=tun0
vpn2=tap0

block all

set skip on lo          # allow local traffic

pass on p2p0            #allow AirDrop
pass on p2p1            #allow AirDrop
pass on p2p2            #allow AirDrop
pass quick proto tcp to any port 631    #allow AirPrint

pass on $wifi proto udp # allow only UDP packets over unprotected Wi-Fi
pass on $vpn            # allow everything else through the VPN (tun interface)
pass on $vpn2           # allow everything else through the VPN (tap interface)

不幸的是,这意味着少数使用 UDP 协议的应用程序现在可以在 Wi-Fi 上泄露数据,例如 ntpd(用于时间同步)和 mDNSResponder。但这似乎仍然比允许数据在不受保护的情况下通过 TCP 传输要好,而大多数应用程序都使用 TCP。如果有人对改进此设置有任何建议,欢迎发表评论或进一步回答。

答案2

你不需要允许全部UDP。mDNS 中的“m”表示“多播”,它使用称为“链路本地多播地址”的特定多播目标 IP 地址和UDP端口号5353

这意味着,在上述解决方案中,您不必要地允许所有 65535 个 UDP 端口的流量绕过 VPN,从而绕过全球所有 37 亿个可路由 IP 地址。您会惊讶于有多少应用程序使用 UDP,因此您大大违背了当 VPN 中断时阻止传出流量的初衷。

为什么不使用这个规则呢:

pass on $wifi proto udp to 224.0.0.251 port 5353

防火墙配置的一个非常重要的经验法则是,当通过防火墙设置例外时,始终尝试使用尽可能最具体的规则。有时,这种具体性是以牺牲便利性和易用性为代价的,例如,您可能会发现还有其他一些需要通过的本地链路协议,因此又添加了另一条特定规则。

如果您替换上述规则并发现原来的 wifi 问题再次出现,那么您的 PF 可能阻止了 DHCP,即用于自动配置网络设备 IP 地址的协议。(在家庭网络中,通常您的宽带路由器将是您的 DHCP 服务器)。您需要允许 DHCP 的规则将是:

pass on $wifi proto udp from 0.0.0.0 port 68 to 255.255.255.255 port 67

*注意:您可能需要替换0.0.0.0anyDHCPREQUEST的计算机首次发送的数据包具有源地址,0.0.0.0因为在那个阶段,您的计算机还没有 IP 地址。
老实说,我更倾向于使用any。另一个选择是删除任何源规范,即pass on $wifi proto udp to 255.255.255.255 port 67,但这意味着我们丢失了规则的源端口部分,尽可能具体始终是最安全的选择。

希望对您有所帮助。以下是一些有用的链接:

移动DNS:http://en.wikipedia.org/wiki/Multicast_DNS#Packet_structure

DHSP:http://en.wikipedia.org/wiki/Dynamic_Host_Configuration_Protocol#DHCP_discovery

答案3

为了创造PF 规则 以一种“简单”的方式,识别现有的活动接口,包括当前的(vpn)接口这么小终止开关可以使用程序,

仍在进行中,但可以成为识别外部 IP 和活动接口以便正确创建防火墙规则的一个良好开端。

-i使用(info) 选项的示例或输出:

$ killswitch -i
Interface  MAC address         IP
en1        bc:57:36:d1:82:ba   192.168.1.7
ppp0                           10.10.1.3

public IP address: 93.117.82.123

传递服务器 ip -ip

# --------------------------------------------------------------
# Sat, 19 Nov 2016 12:37:24 +0100
# sudo pfctl -Fa -f ~/.killswitch.pf.conf -e
# --------------------------------------------------------------
int_en1 = "en1"
vpn_ppp0 = "ppp0"
vpn_ip = "93.117.82.123"
set block-policy drop
set ruleset-optimization basic
set skip on lo0
block all
pass on $int_en1 proto udp to 224.0.0.251 port 5353
pass on $int_en1 proto udp from any port 67 to any port 68
pass on $int_en1 inet proto icmp all icmp-type 8 code 0
pass on $int_en1 proto {tcp, udp} from any to $vpn_ip
pass on $vpn_ppp0 all

远非完美,但工作正在进行中,更多信息/代码可以在这里找到:https://github.com/vpn-kill-switch/killswitch

答案4

-- 作为补充 --

您可能需要添加以下行:

pass on $wifi inet6 proto udp from any to FF02:0000:0000:0000:0000:0000:0000:00FB port 5353

允许 mDNS 在 ipv6 上运行

相关内容