1

I’m looking into a solution to prevent some application to accessing the internet when I’m connected to a specific wifi access point. My exact use case is that I would like to disable dropbox, spotify and similar apps to use all my data when connecting through my phone tethering.

The solution could be at another level than network itself, but I’d prefer to avoid the "killing the apps" solution.

I’m running archlinux but if a good solution for another distro is available, it can surely give me a good direction.

  • Before I get flagged for duplicate, I found many questions similar but none address the "per access point" issue. – Jonatan Cloutier Jan 31 '20 at 14:48
  • It might be good to add links to the existing solutions that don't fulfill your requirements, as someone might know how to improve them. – Bodo Jan 31 '20 at 15:10
  • https://unix.stackexchange.com/questions/359461/ubuntu-block-internet-access-to-all-applications-except-chosen-few https://askubuntu.com/questions/45072/how-to-control-internet-access-for-each-program/666171 – Jonatan Cloutier Jan 31 '20 at 15:15
  • Please [edit] your question to add information instead of answering in comments. – Bodo Feb 03 '20 at 11:06

2 Answers2

2

The problem can be split in two parts:

  1. identify when a restricted access point (tethering's) is in use

  2. identify which outgoing packets belong to restricted applications

Combining the two together will identify packets belonging to restricted applications going out using the restricted access point (in order to drop such packets).


Preparation

Some glue before rather than after explanations, so typing everything in order just works.

Use two iptables chains: one doing the action (blocksomeapps), the other (tetheractivated) to call it and that can be easily flushed:

iptables -N tetheractivated
iptables -N blocksomeapps
iptables -I OUTPUT -j tetheractivated

1. Access point

This answer assumes only one access point is in use at any moment, and that this access point is using an already known network interface name (wlan0). It also assumes wpa_supplicant will be the low level daemon handling it. It doesn't matter if standalone or a backend of NetworkManager, as long as it can be queried with wpa_cli and its -a (action) option.

action script 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

This script (which must be executable) is then used as an event loop by keeping this program running:

wpa_cli -a myaction.sh

This will then make the iptables chain blocksomeapps appear in the packet path when connected to the target SSID, and be removed from packet path when (any SSID is) disconnected.


2. Restricted applications

This is more difficult to handle. There are various methods to identify a packet belonging to a specific process, requiring anyway additional preparation. Some methods require more things than others.

Some examples:

  • the packet could be enqueued to an userspace application doing additional checks (the program has to be written, probably in C or python)
  • the restricted processes can be run as a separate user: quite easy to deal with.
  • the restricted processes could be run from their own dedicated network namespace: once done, quite easy to deal with. Putting them there in the first place (involving configuration and probable root access and back to user access to start them) can give some trouble.

Here's a quite simple method found in this Q/A: Block network access of a process? Classify packets from the same net_cls cgroup with a value that can be looked up in iptables (in addition to the main target: Traffic Controller tc).

Create the specific net_cls group and give it a specific classid:

# mkdir -p /sys/fs/cgroup/net_cls/blocksomeapps
# echo 42 > /sys/fs/cgroup/net_cls/blocksomeapps/net_cls.classid

Identify target processes (threads must be included), and add them to the group (by writting to tasks one pid (or tid) at a time. This could be racy if processes change a lot, so it should better done during startup of application. Of course once done, children automatically stay in the same group.

Example with firefox (which uses multiple threads within multiple processes, so pgrep needs --lightweight to output all thread ids):

# for i in $(pgrep --lightweight -f firefox); do echo $i > /sys/fs/cgroup/net_cls/blocksomeapps/tasks; done

( pid/tid to be removed from this group should be written to the parent /sys/fs/cgroup/net_cls/tasks )

Add the blocking rule in the empty chain blocksomeapps prepared before:

# iptables -A blocksomeapps -m cgroup --cgroup 42 -j DROP

To block only packets going through wlan0, use instead:

# iptables -A blocksomeapps -o wlan0 -m cgroup --cgroup 42 -j DROP

but then indirect activity generated by those applications, such as DNS queries to a local DNS caching daemon, might still generate some data traffic since for example the DNS caching daemon wouldn't be itself blocked.


Notes:

  • The SSID part could be improved (and deal with multiple simultaneous access points) by involving route checks somewhere, but would rapidly become very complex. The simple myaction.sh script wouldn't then be enough (because knowing the SSID we connected to gives nothing about the network layer not even configured yet).
  • Nowadays, cgroups have already been mounted and configured, probably by systemd or cgmanager. It's out of this Q/A scope to configure this, if it's not available.
  • cgroups v1 are slowly being replaced by cgroups v2, so this answer should be adapted to v2 someday.
  • Also this method will probably be incompatible (or at least difficult to use) with containers or network namespaces created with ip netns just because both apply restrictions to the use of cgroups (for ip netns: as ip netns exec ... remounts /sys/, the /sys/fs/cgroup tree becomes unavailable, and it can't really be mounted twice).
  • All output packets cross the cgroup match. tetheractivated could be inserted after the usual ESTABLISHED stateful rule to decrease load, but then already established flows wouldn't be dropped, which could keep them working for some time depending on the application. This could be helped with conntrack (-D or -F).
  • Instead of blocking traffic with iptables (or nftables) tc (and its cgroup filter) could be used to apply severe bandwidth restrictions. Just remember that while output traffic is in full control, incoming traffic is not: once a packet is received (and associated to the same flow than previous outgoing), even if dropped, data was already spent.
A.B
  • 36,364
  • 2
  • 73
  • 118
0

There are two cases:

Case 1

You want to deny access only for some applications

Here you can use some iptables stuff. You need to know the protocol (TCP/UDP) your application uses. Then you can block it:

sudo iptables -A OUTPUT -p tcp --dport 80 -j DROP

This would prevent any application from accessing TCP port 80. Sadly this doesn't work if your application has no own protocol and uses for example HTTP, then you would also block legitimate traffic.

Case 2

If you only want some applications to access the internet, you can do whitelisting:

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

This would only allow access to TCP port 80. You can of course add more rules like the port 80 one. But be warned, all other outgoing packets (except loopback traffic) will be blocked.

This configuration does not survive a reboot. You could try iptables-persistent to fix that. Also this doesn't block IPv6 traffic, you have to enter the same commands, but replace iptables with ip6tables to filter IPv6.