1. Introduction

The internet in recent years has become localized. In other words, users face many restrictions based on the geolocation of the IP address. Most importantly, on a Linux landscape, Governments, businesses, or organizations Block Access of a Process to the Network mainly to put a check on unethical hacking and phishing attacks.

For example, let’s say a curl implementation got modified locally by a hacker and “phones home”. We want to use it anyway to download and print the slides of a most recent casync talk, but we don’t trust it. So, we need to make sure that it doesn’t connect anywhere except where we tell it to.

In this tutorial, we’ll discuss how to restrict the running environment of untrusted applications using Linux capabilities such as Linux namespace and sandboxing.

2. What Is a Network Namespace in Linux?

 To begin with, let’s understand the network namespace in Linux.

Linux has a feature called network namespace that allows multiple network stacks on the same machine and assigns one to a program when running it. It’s a private view of the globally shared kernel resources, such as the network stack, process table, and mount table.

Creating and managing a new namespace is fairly easy. We’ll take a practical look at how to create a new namespace. Let’s create one using ip netns with the add option:

$ root@host:~# ip netns add jail

We’ll now use sub-commands to produce a private view, “jail”, with no network access. Any command required to be run with no network can just be run in that “jail”. However, let’s first switch to that namespace. To do so, we’ll probably have to bring up lo in it, and that’s it:

$ root@host:~# ip netns exec jail /bin/bash
$ root@host:~# ip addr add 127.0.0.1/8 dev lo
$ root@host:~# ip link set dev lo up
$ root@host:~# exit

Let’s now test ping to google.com using the “jail” namespace:

$ root@host:~# ip netns exec jail su user -c 'ping 8.8.8.8'
connect: Network is unreachable

As desired, the network is unreachable.

3. Blocking a Single Process

The above illustration using ip netns will form the basis for discussing other built-in Linux tools. They’re simpler and precisely used to do the same job, which is to block the access of a process to the network.

3.1. Using unshare

With the unshare tool, starting a process without network access is as simple as:

$ unshare -n process

To get the unshare tool up and working, we must have the network namespaces enabled in our kernel (CONFIG_NET_NS=y) and util-linux installed.

The command creates an empty network namespace (with no network access) for the process. However, this requires root permission. Consequently, we may add -r to run the program only after the current effective user and group IDs have been mapped to the superuser. This would also avoid the need for users to run it using sudo:

$ unshare -r -n ping 127.0.0.1
connect: Network is unreachable

When we need to run a program in a network interface with no access to open ports, we set a new one up:

$ unshare -n -- sh -c 'ip link set dev lo up; ping 127.0.0.1'
PING 127.0.0.1 (127.0.0.1) 56(84) bytes of data.
64 bytes from 127.0.0.1: icmp_seq=1 ttl=32 time=0.066 ms

Then, we simply execute:

$ unshare -r -n process

3.2. Using firejail

Firejail, a SUID sandbox program, does almost the same thing. That is, it can also be used to block a process’s access to the network. Unlike unshare, this one does not require root permission. Any user can benefit from it:

$ firejail --noprofile --net=none <process or path to executable>

In this example, –noprofile disables the default sandbox, and –net=none disables networking

However, Firejail mandatorily requires a build toolchain and namespace or seccomp-enabled kernel.

3.3. Using proxify – SOCKS

tsocks is another solution that earmarks a program to a null or fake proxy.

It acts as a wrapper or interceptor that enables a SOCKS proxy for all outgoing TCP communication done by a process. It is designed for use in machines that are firewalled from the internet. To gain access, these machines use SOCKS to reach the internet.

Using the SOCKS proxy server, we’d implement the same but as a reverse technique. Therefore, instead of imparting access to restricted content, which is why a proxy is normally used, it would implement a filter. Configuring the shell env with the tsocks package, we’d be able to block access of a process to the network.

3.4. Using proxychains

ProxyChains is a command-line tool that redirects TCP connections made by programs through various proxies like SOCKS4, SOCKS5, or HTTP. As the name suggests, it works as user-defined list of proxies chained together.

We can try a few possibilities to use proxychains as a filter that prevents access of a process to the network. It needs to be set up such that it uses an “invalid” proxy, a proxy that does not exist. The configuration in this case involves manually setting up the http_proxy or https_proxy environment variables.

Another possibility could be using a local proxy (like “tinyproxy“, “squid“, or “privoxy“) that acts as a filter. A local proxy like Tinyproxy has privacy features to let us configure which HTTP headers should be allowed through and which should be blocked. It manages access control also by only allowing requests from a certain subnet or interface.

3.5. Kernel MAC

Kernel MACs can emulate a firewall. The most known is AppArmor. This solution is the best one when it comes to stability and efficiency.

AppArmor proactively protects the operating system and applications from external or internal threats. It even prevents zero-day attacks by enforcing a specific rule set on a per-process basis. Security policies can completely control what system resources individual processes can access, and with what privileges. In the case of AppArmor, they are defined in a file termed the AppArmor profile.

An AppArmor profile is a simple customizable text file that can be edited by using either a text editor or aa-logprof. For a ping command:

$ ping nu.novell.com
PING cs184.wpc.gammacdn.net (192.229.145.212) 56(84) bytes of data.
64 bytes from 192.229.145.212 (192.229.145.212): icmp_seq=1 ttl=57 time=13.5 ms
64 bytes from 192.229.145.212 (192.229.145.212): icmp_seq=2 ttl=57 time=9.51 ms
64 bytes from 192.229.145.212 (192.229.145.212): icmp_seq=3 ttl=57 time=10.8 ms
--- cs184.wpc.gammacdn.net ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2001ms
rtt min/avg/max/mdev = 9.513/11.270/13.505/1.664 ms

It’s the /etc/apparmor.d/bin.ping file that can be found inside /etc/apparmor.d:

#include <tunables/global>
/{usr/,}bin/ping {
  #include <abstractions/base>
    # block ipv4 acces
    deny network inet,
    # ipv6
    deny network inet6,
    # raw socket
    deny network raw,
}

As we can see, it blocks the website:

$ ping nu.novell.com
ping: socket: Operation not permitted

4. Blocking a Process via Network Service

Network services such as systemd and iptables can both control network traffic. Let’s see how blocking a process becomes easy using these two tools.

4.1. Using iptables

Another easy solution is iptables. It can be set up to block a process belonging to a group. Blocking the network activity of a group would block all the processes belonging to that group.

Let’s first create and validate a new user frand under a new group named no-internet:

$ groupadd no-internet
$ grep no-internet /etc/group
no-internet:x:1006:
$ useradd -g no-internet frand
$ groups frand
frand : no-internet

Now, to drop the network activity of the group no-internet, we need to add the iptables rule with the owner module:

$ iptables -I OUTPUT 1 -m owner --gid-owner no-internet -j DROP
$ iptables-save
-A OUTPUT -m owner --gid-owner 1006 -j DROP
$ iptables -L -v -n
Chain OUTPUT (policy ACCEPT 44864 packets, 28M bytes)
 pkts bytes target     prot opt in     out     source               destination
    0     0 DROP       all  --  *      *       0.0.0.0/0            0.0.0.0/0            owner GID match 1006

Let’s check it, for example, on ping by running:

[frand]$ ping www.google.com
ping: www.google.com: Temporary failure in name resolution

If we want to block a process running under a dedicated user ID, we’ll replace –gid-owner with –uid-owner:

$ iptables -A OUTPUT -m owner --uid-owner 1234 -j DROP

We must not forget to make the changes permanent so that they’ll be applied automatically after reboot.

In an exceptional case, to allow a process to access the local network*,* we need to add the iptables rule:

$ iptables -A OUTPUT -m owner --gid-owner no-internet -d 192.168.1.0/24 -j ACCEPT
$ iptables -A OUTPUT -m owner --gid-owner no-internet -d 127.0.0.0/8 -j ACCEPT

Another approach to make the changes permanent is by applying the iptables rule at boot as a service with systemd

4.2. Using systemd

systemd can do per-service access control for IP address ranges. With systemd, the kind of resource that can be controlled per unit is network traffic (specifically, IP).

Two unit file settings are added in this (IP Access Control) context. 

IPAddressDeny= takes an IP address prefix (an IP address with a network mask). All traffic from and to this address will be prohibited for processes of the service. 

IPAddressAllow= is the matching positive counterpart to IPAddressDeny. All traffic matching this IP address/network mask combination will be allowed, even if otherwise listed in IPAddressDeny.

In this section, we’ll mainly talk about the IPAddressDeny. Let’s now take a look at IP access control with systemd-run. It’s dropped if a packet matches an IPAddressDeny entry configured for the service. In other words, IPAddressDeny implements a blacklist:

$ systemd-run -p IPAddressDeny=any -t /bin/sh 
$ systemd-run -p IPAddressDeny=any curl http://0pointer.de/public/casync-kinvolk2017.pdf | lp

In the example above, we used IPAddressDeny=any. Here, the any identifier is a shortcut for writing 0.0.0.0/0, which is a shortcut for everything on both IPv4 and IPv6. Although they can match against specific IP addresses, they apply to IP networking only.

5. Blocking the Socket System Call

We can use seccomp-bpf to block system calls. For example, we may want to block the socket system call to prevent a process from creating sockets FD.

Let’s look at an approach that prevents the socket system call from working using libseccomp. The summary (without error checking) is:

scmp_filter_ctx ctx = seccomp_init(SCMP_ACT_ALLOW);
seccomp_arch_add(ctx, SCMP_ARCH_NATIVE);
seccomp_rule_add(ctx, SCMP_ACT_ERRNO(EACCES), SCMP_SYS(socket), 1, SCMP_CMP(0, SCMP_CMP_EQ, pf));
seccomp_load(ctx);
execvp(argv[1], argv+1);

This does not require root privileges.

6. Blocking Everything Except Applications in the Firewall Whitelist

What we wish to achieve is a whitelisting – a rule on a firewall that says, “block everything except for what is explicitly allowed”*.*

This way, we set up the whitelist – the list of good destinations and processes to which we want to grant network access. Only processes in this list will be able to communicate.

Earlier, we saw IP access control with systemd using the IPAddressDeny. Let’s now see how to get a transient service running an interactive shell that also uses the IPAddressAllow unit setting:

$ systemd-run -p IPAddressDeny=any -p IPAddressAllow=8.8.8.8 -p IPAddressAllow=127.0.0.0/8 -t /bin/sh

Here, the access list we set up uses IPAddressDeny=any to define an IP whitelist: All traffic will be prohibited for the session, except for what is explicitly whitelisted. In this command line, we whitelisted two address prefixes: 8.8.8.8 (with no explicit network mask but /32, which means the mask with all bits turned on) and 127.0.0.0/8. As a result, the service can communicate with Google’s DNS server and everything on the local loop-back, but nothing else.

Long story short, depending on our use case, one, the other, both, or neither might be suitable for sandboxing our service.

7. Conclusion

In this article, we saw some common security-related challenges that governments and businesses face in the current state of the internet. We then understood how they meet those challenges using popular Linux capabilities.

Throughout this article, many examples and illustrations helped us familiarize ourselves with the concept of network namespace. We learned how its use by tools and services restricts running environments of untrusted applications. Finally, we concluded with a topic covering whitelisting explaining the significance of sandboxing our services.