2

Rationale

User namespaces (CLONE_NEWUSER), although useful security feature, do increase kernel attack surface. For example, one can gain access to iptables:

$ iptables -S
iptables v1.4.21: can't initialize iptables table `filter': Permission denied (you must be root)
Perhaps iptables or your kernel needs to be upgraded.
$ unshare -rn
# iptables -S
-P INPUT ACCEPT
-P FORWARD ACCEPT
-P OUTPUT ACCEPT

Although this network namespace does can't affect usual real network, it can help local root exploits.

In addition user namespaces seem to override PR_SET_NO_NEW_PRIVS, reducing its effectiveness in shrinking the attack surface:

$ ping 127.0.0.1
64 bytes from 127.0.0.1: icmp_seq=1 ttl=64 time=0.067 ms
^C
$ setpriv --no-new-privs bash
$ ping 127.0.0.1
ping: icmp open socket: Operation not permitted
$ unshare -rn
# ifconfig lo up
# ping 127.0.0.1
64 bytes from 127.0.0.1: icmp_seq=1 ttl=64 time=0.053 ms
^C

Workarounds

User namespaces fail in chrooted environments since kernel 3.9. They can also be disabled in kernel config.

  • “User namespaces seem to fail in chrooted environment” Uh, what? No they don't. What the filesystem root is doesn't matter to the kernel when it's creating a namespace. However, many utilities (not the underlying system calls) will fail if /proc is not available. But many other things fail if /proc is not available. – Gilles Jan 24 '17 at 23:05
  • @Gilles, github.com/torvalds/linux/blob/master/kernel/… – Vi. Jan 25 '17 at 0:10
  • Oh, I didn't know that. Thank you. – Gilles Jan 25 '17 at 0:23
2

Implemented myself. Here is a little tool using libseccomp:

https://github.com/vi/syscall_limiter/blob/master/ban_CLONE_NEWUSER.c

#include <linux/sched.h>                // for CLONE_NEWUSER
#include <seccomp.h>                    // for seccomp_rule_add, etc
#include <stdint.h>                     // for uint32_t
#include <stdio.h>                      // for fprintf, perror, stderr
#include <unistd.h>                     // for execve

// gcc ban_CLONE_NEWUSER.c -lseccomp -o ban_CLONE_NEWUSER

int main(int argc, char* argv[], char* envp[]) {

    if (argc<2) {
        fprintf(stderr, "Usage: ban_CLONE_NEWUSER program [arguments]\n");
        fprintf(stderr, "Bans unshare(2) with any flags and clone(2) with CLONE_NEWUSER flag\n");
        return 126;
    }

    uint32_t default_action = SCMP_ACT_ALLOW;

    scmp_filter_ctx ctx = seccomp_init(default_action);
    if (!ctx) {
        perror("seccomp_init");
        return 126;
    }

    int ret = 0;
    ret |= seccomp_rule_add(ctx, SCMP_ACT_ERRNO(1),  seccomp_syscall_resolve_name("unshare"), 0);
    ret |= seccomp_rule_add(ctx, SCMP_ACT_ERRNO(1),  seccomp_syscall_resolve_name("clone"), 1, SCMP_CMP(0, SCMP_CMP_MASKED_EQ, CLONE_NEWUSER, CLONE_NEWUSER));

    if (ret!=0) {
        fprintf(stderr, "seccomp_rule_add returned %d\n", ret);
        return 124;
    }

    ret = seccomp_load(ctx);
    if (ret!=0) {
        fprintf(stderr, "seccomp_load returned %d\n", ret);
        return 124;    
    }

    execve(argv[1], argv+1, envp);

    perror("execve");
    return 127;
}
$ ban_CLONE_NEWUSER /bin/bash
$ unshare -r
unshare: unshare failed: Operation not permitted

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.