Should setting the listen-port require CAP_SYS_ADMIN in the socket namespace?

Julian Orth ju.orth at gmail.com
Sun Sep 9 11:13:43 CEST 2018


Hello list,

Consider the following scenario:

1. Sysadmin runs a hostile application `h1` in container `c1`.
2. Sysadmin creates a Wireguard device `wg0` in the init namespace.
3. Sysadmin moves `wg0` into `c1`.
4. On the same server, a user wishes to sometimes run an application `a1` that
    listens on a well-known unprivileged port in the init namespace.

`h1` has a full set of capabilities in `c1`. This allows `h1` to listen on an
arbitrary unprivileged port in the init namespace by setting the listen-port
of `wg0`. This allows `h1` to block the usage of `a1`.

`h1` cannot gain access to the traffic of `a1` unless `a1` was also intending
to use this port for a Wireguard device `wg1` and the private key of `wg1` is
already known to `h1`.

Therefore: Should setting the listen-port require some capability in the
network namespace of the socket?

setns(<fd>, CLONE_NEWNET) requires CAP_SYS_ADMIN in the user namespace of <fd>
[1]. The leaves getting access to <fd>:

* If the namespace of <fd> is mounted somewhere in the file system, then `h1`
   might be able to open that file without any additional requirements. (If the
   namespace was created via ip(1), then it is mounted at /run/netns/<name>.)
* If /proc is mounted, opening /proc/<pid>/ns/net seems to require
   CAP_SYS_PTRACE in /proc/<pid>/ns/user.

I don't know if there are any other ways to get access to <fd> without help
from outside `c1`.

Running `c1` in a separate mount namespace with a separate / mount and a
separate pid namespace might be sufficient to prevent access to <fd>. This is
the case for Docker containers. Someone with a better overview of the
namespace model might be able to answer the following question: Are network
namespaces already considered insecure unless combined with mount and pid
namespaces? (A mount namespace is probably required to hide /sys/class/net.)

setns(<fd>, CLONE_NEWNET) is sufficient to create a socket in that network
namespace but also gives the caller other abilities in that namespace.
A less-powerful capability might therefore be better in the case of
listen-port.

Julian

PS: This problem becomes more severe with the transit-net series because it
     allows `h1` to create the socket in any network namespace it can refer to
     via process id or file descriptor. Note that the CAP_SYS_PTRACE
     requirement does not apply in this case because `h1` does not have to open
     /proc/<pid>/ns/net.

     This allows `h1` to gain network access by creating a Wireguard device and
     moving the transit namespace to PID 1.

     Once again, this might not a problem in containers with separate mount
     namespaces, separate / mounts, and separate pid namespaces.

     Since the selling point of the transit-net series is enabling users to
     create working Wireguard devices without capabilities, it series will have
     to be reworked as follows:

     The caller has to prove that he already had access to intended transit
     namespace. This can be done by passing matching UDP sockets into the
     kernel. Unfortunately this makes it harder to use this feature from Bash
     scripts alone.

     I'll add this requirement in v2.

[1] Not described in the man page. But see netns_install:
 
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/net/core/net_namespace.c?h=v4.18#n1126


More information about the WireGuard mailing list