我正在寻找一种解决方案,以防止某些应用程序在我连接到特定 wifi 接入点时访问互联网。我的具体用例是,我想禁用 Dropbox、Spotify 和类似的应用程序,以便在通过手机网络共享进行连接时使用我的所有数据。
该解决方案可能位于网络本身之外的另一个级别,但我更愿意避免“杀死应用程序”解决方案。
我正在运行 archlinux,但如果另一个发行版有一个好的解决方案可用,它肯定可以给我一个好的方向。
答案1
问题可以分为两部分:
识别何时使用受限接入点(网络共享)
识别哪些传出数据包属于受限应用程序
将两者组合在一起将识别属于使用受限接入点发出的受限应用程序的数据包(以便丢弃此类数据包)。
准备
在解释之前而不是在解释之后进行一些粘合,因此按顺序输入所有内容就可以了。
使用两个 iptables 链:一个执行操作 (阻止某些应用程序), 另一个 (系绳激活) 来调用它并且可以轻松刷新:
iptables -N tetheractivated
iptables -N blocksomeapps
iptables -I OUTPUT -j tetheractivated
1. 接入点
这个答案仅假设一接入点随时都在使用中,并且该接入点正在使用已知的网络接口名称 ( wlan0
)。它还假设wpa_supplicant
将由低级守护进程处理它。无论是独立的还是 NetworkManager 的后端都没关系,只要可以查询即可wpa_cli
及其-a
(操作)选项。
动作脚本myaction.sh
:
#!/bin/sh
TETHERSSID='My Tethering'
case "$2" in
'CONNECTED')
ssid="$(wpa_cli -p "$WPA_CTRL_DIR" -i "$1" get_network "$WPA_ID" ssid)"
# Note: ssid's value already comes with an added pair of double quotes around:
# adding one as well
if [ "$ssid" = \""$TETHERSSID"\" ]; then
iptables -A tetheractivated -j blocksomeapps
fi
;;
'DISCONNECTED')
iptables -F tetheractivated
;;
esac
然后,通过保持该程序运行,将该脚本(必须是可执行的)用作事件循环:
wpa_cli -a myaction.sh
这将使 iptables 链阻止某些应用程序连接到目标 SSID 时出现在数据包路径中,并在(任何 SSID)断开连接时从数据包路径中删除。
2. 限制应用
这个比较难处理。有多种方法可以识别属于特定进程的数据包,无论如何都需要额外的准备。有些方法比其他方法需要更多的东西。
一些例子:
- 数据包可以排队到用户空间应用程序中进行额外的检查(必须编写程序,可能是用 C 或 python 编写的)
- 受限制的进程可以作为单独的用户运行:非常容易处理。
- 受限制的进程可以从它们自己的专用网络命名空间运行:一旦完成,就很容易处理。首先将它们放在那里(涉及配置和可能的根访问权限,然后返回用户访问权限来启动它们)可能会带来一些麻烦。
这是本问答中发现的一个非常简单的方法:阻止进程的网络访问?
对来自同一网络CLS组组具有可以在 iptables 中查找的值(除了主要目标: Traffic Controller tc
)。
创建具体的网络CLS分组并给它一个特定的classid:
# mkdir -p /sys/fs/cgroup/net_cls/blocksomeapps
# echo 42 > /sys/fs/cgroup/net_cls/blocksomeapps/net_cls.classid
识别目标进程(必须包括线程),并将它们添加到组中(通过tasks
一次写入一个 pid(或 tid))。如果进程变化很大,这可能会很危险,因此最好在应用程序启动期间完成。当然,一旦完成,孩子们就会自动留在同一组中。
以 firefox 为例(它在多个进程中使用多个线程,因此pgrep
需要--lightweight
输出所有线程id):
# for i in $(pgrep --lightweight -f firefox); do echo $i > /sys/fs/cgroup/net_cls/blocksomeapps/tasks; done
(要从该组中删除的 pid/tid 应写入父级/sys/fs/cgroup/net_cls/tasks
)
添加空链阻塞规则阻止某些应用程序之前准备的:
# iptables -A blocksomeapps -m cgroup --cgroup 42 -j DROP
要仅阻止通过 的数据包wlan0
,请改用:
# iptables -A blocksomeapps -o wlan0 -m cgroup --cgroup 42 -j DROP
但这些应用程序生成的间接活动(例如对本地 DNS 缓存守护进程的 DNS 查询)可能仍会生成一些数据流量,因为 DNS 缓存守护进程本身不会被阻止。
笔记:
- SSID 部分可以通过在某处进行路由检查来改进(并处理多个同时接入点),但很快就会变得非常复杂。简单的
myaction.sh
脚本还不够(因为知道我们连接的 SSID 并不能提供任何有关尚未配置的网络层的信息)。 - 如今,cgroups可能已经通过 systemd 或 cgmanager 进行了安装和配置。如果它不可用,则配置它超出了本问答范围。
- cgroupsv1 正在慢慢被取代cgroupsv2,所以这个答案有一天应该适应 v2。
- 此外,此方法可能与使用创建的容器或网络命名空间不兼容(或至少难以使用),
ip netns
因为两者都对 cgroup 的使用施加限制(例如ip netns
:ip netns exec ...
重新挂载时/sys/
,/sys/fs/cgroup
树变得不可用,并且它不能真正被使用)。安装两次)。 - 所有输出数据包都经过组组匹配。
tetheractivated
可以在通常的有状态规则之后插入ESTABLISHED
以减少负载,但是已经建立的流不会被删除,这可以使它们根据应用程序工作一段时间。这可以帮助conntrack
(-D
或者-F
)。 - 而不是阻塞交通iptables(或者nftables)
tc
(及其组组过滤器)可用于应用严格的带宽限制。请记住,虽然输出流量处于完全控制状态,但传入流量却并非如此:一旦接收到数据包(并且与之前的传出流量关联到相同的流量),即使丢弃,数据也已被消耗。
答案2
有两种情况:
情况1
您只想拒绝某些应用程序的访问
在这里你可以使用一些 iptables 的东西。您需要知道您的应用程序使用的协议(TCP/UDP)。然后你可以阻止它:
sudo iptables -A OUTPUT -p tcp --dport 80 -j DROP
这将阻止任何应用程序访问 TCP 端口 80。遗憾的是,如果您的应用程序没有自己的协议并使用 HTTP 等协议,则这不起作用,那么您也会阻止合法流量。
案例2
如果您只想某些应用程序访问互联网,您可以进行白名单:
sudo iptables -A OUTPUT -i lo -j ACCEPT
sudo iptables -A OUTPUT -m state --state RELATED,ESTABLISHED -j ACCEPT
sudo iptables -A OUTPUT -p tcp --dport 80 -j ACCEPT
sudo iptables -P OUTPUT DROP
这将只允许访问 TCP 端口 80。您当然可以添加更多规则,例如端口 80 规则。但请注意,所有其他传出数据包(环回流量除外)都将被阻止。
此配置在重新启动后无法保留。您可以尝试 iptables-persistent 来解决这个问题。此外,这不会阻止 IPv6 流量,您必须输入相同的命令,但将 iptables 替换为 ip6tables 来过滤 IPv6。