как отключить сеть для текущего процесса

Можно запустить новую команду без доступа к сети как не-root, используя unshare -r -n , например:

 $ unshare -r -n ls a.txt b.txt 

Команда, которая требует доступа к сети, будет предсказуемо завершаться с ошибкой.

 $ unshare -r -n curl unix.stackexchange.com curl: (6) Could not resolve host: unix.stackexchange.com 

Мне интересно, возможно ли удалить доступ к сети для текущего процесса, возможно, записав магический файл в /sys или что-то подобное.

Я хотел бы иметь возможность сделать что-то вроде

 $ /bin/sh -c 'echo 1 > /sys/unsharethis; curl unix.stackexchange.com' 

Выдержка из strace -ing unshare -r -n ls показывает системный вызов unshare

 open("/usr/lib/locale/locale-archive", O_RDONLY|O_CLOEXEC) = 3 fstat(3, {st_mode=S_IFREG|0644, st_size=4759040, ...}) = 0 mmap(NULL, 4759040, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7f7ec6968000 close(3) = 0 unshare(CLONE_NEWUSER|CLONE_NEWNET) = 0 open("/proc/self/setgroups", O_WRONLY) = 3 write(3, "deny", 4) = 4 

Что наводит меня на мысль, что отключение доступа к сети из текущего процесса на самом деле является единственным способом достижения разделения (то есть его нельзя передать в качестве аргумента для spawn или какого-либо его эквивалента). Это также предполагает, что отказ от совместного использования из сценария оболочки не сработает, если shell не была специально расширена для предоставления оболочки вокруг unshare .

Это можно сделать, в некотором роде, с помощью отладчика gdb , и если к нему можно присоединить запущенный процесс (программы, которые изменяют свое состояние дампа или setgid и т. Д., Не могут быть присоединены, кроме как из-под root). Лучшее достижимое по-прежнему заставит процесс думать, что он теперь root (но, по крайней мере, сможет писать как фактический пользователь).

Некоторые необязательные файлы могут помочь использовать gdb как символы отладки для libc6, а некоторые связанные с Linux файлы include в себя, чтобы получить действительные значения нескольких символов позже (например, в Debian: (возможно) libc6-dbg , libc6-dev и linux-libc-dev пакеты), но на самом деле, как только “рецепт” сделан, они, вероятно, больше не нужны.

Во-первых, что делает больше, чем unshare() unshare -r ? Без этого новый пользователь не останется ни у nobody и даже не сможет написать в качестве своего первоначального пользователя:

 $ id uid=1000(user) gid=1000(user) groups=1000(user) $ strace unshare -r -n /bin/sleep 1 2>&1 |sed -n '/^unshare/,/^execve/p' unshare(CLONE_NEWNET|CLONE_NEWUSER) = 0 open("/proc/self/setgroups", O_WRONLY) = 3 write(3, "deny", 4) = 4 close(3) = 0 open("/proc/self/uid_map", O_WRONLY) = 3 write(3, "0 1000 1", 8) = 8 close(3) = 0 open("/proc/self/gid_map", O_WRONLY) = 3 write(3, "0 1000 1", 8) = 8 close(3) = 0 execve("/bin/sleep", ["/bin/sleep", "1"], [/* 18 vars */]) = 0 

Это будет использовано позже.

 $ ip -4 -br a lo UNKNOWN 127.0.0.1/8 eth0@if19 UP 10.0.3.66/24 $ ping -c1 10.0.3.1 PING 10.0.3.1 (10.0.3.1) 56(84) bytes of data. 64 bytes from 10.0.3.1: icmp_seq=1 ttl=64 time=0.167 ms --- 10.0.3.1 ping statistics --- 1 packets transmitted, 1 received, 0% packet loss, time 0ms rtt min/avg/max/mdev = 0.167/0.167/0.167/0.000 ms $ id uid=1000(user) gid=1000(user) groups=1000(user) $ echo $$ 338 $ 

На другом терминале:

 $ gdb --pid=338 Reading symbols from /bin/bash...(no debugging symbols found)...done. Reading symbols from /lib/x86_64-linux-gnu/libtinfo.so.5...(no debugging symbols found)...done. Reading symbols from /lib/x86_64-linux-gnu/libdl.so.2...Reading symbols from /usr/lib/debug/.build-id/b8/95f0831f623c5f23603401d4069f9f94c24761.debug...done. done. Reading symbols from /lib/x86_64-linux-gnu/libc.so.6...Reading symbols from /usr/lib/debug/.build-id/aa/889e26a70f98fa8d230d088f7cc5bf43573163.debug...done. done. 

[…]

 (gdb) 

Теперь давайте вызовем первую функцию:

 (gdb) call unshare(CLONE_NEWNET|CLONE_NEWUSER) No symbol "CLONE_NEWNET" in current context. 

Хорошо, может быть есть способ, чтобы GDB знал это, но я не гуру:

 (gdb) ! $ grep CLONE_NEW /usr/include/linux/sched.h # man 2 unshare #define CLONE_NEWNS 0x00020000 /* New mount namespace group */ #define CLONE_NEWCGROUP 0x02000000 /* New cgroup namespace */ #define CLONE_NEWUTS 0x04000000 /* New utsname namespace */ #define CLONE_NEWIPC 0x08000000 /* New ipc namespace */ #define CLONE_NEWUSER 0x10000000 /* New user namespace */ #define CLONE_NEWPID 0x20000000 /* New pid namespace */ #define CLONE_NEWNET 0x40000000 /* New network namespace */ $ find /usr/include/ -name fcntl.h |xargs grep O_WRONLY # man 2 open /usr/include/asm-generic/fcntl.h:#define O_WRONLY 00000001 $ exit exit (gdb) call unshare(0x50000000) $1 = 0 (gdb) call open("/proc/self/setgroups", 1) $2 = 3 (gdb) call write($2,"deny",4) $3 = 4 (gdb) call close($2) $4 = 0 (gdb) call open("/proc/self/uid_map", 1) $5 = 3 (gdb) call write($5, "0 1000 1", 8) $6 = 8 (gdb) call close($5) $7 = 0 (gdb) call open("/proc/self/gid_map", 1) $8 = 3 (gdb) call write($8, "0 1000 1", 8) $9 = 8 (gdb) call close($8) $10 = 0 (gdb) quit A debugging session is active. Inferior 1 [process 338] will be detached. Quit anyway? (y or n) y Detaching from program: /bin/bash, process 338 

На измененном процессе можно убедиться, что интерфейс eth0 исчез:

 $ ip -br a lo DOWN 127.0.0.1/8 $ echo $$ 338 $ id uid=0(root) gid=0(root) groupes=0(root) $ touch / touch: setting times of '/': Permission denied $ touch ~/test1 $ ls ~/test1 /home/user/test1 $ ping 10.0.3.1 connect: Network is unreachable 

Обратного пути нет: новое пространство имен пользователя не может вернуться к своему первоначальному пространству имен. Если процесс выполняется с достаточными привилегиями (например, root без утраченных возможностей или SELinux), то это будет возможно (при использовании только unshare(CLONE_NEWNET) / setns(savedopenedfd) ).

Конечно, можно записать его в файл и изменить любой разрешенный запущенный процесс, или сделать так, чтобы shell изменила себя из subprocessа gdb. Содержимое removenetwork.gdb , допустимое здесь только для изменения процесса с помощью pid:gid == 1000:1000 :

 call unshare(0x50000000) call open("/proc/self/setgroups", 1) call write($2,"deny",4) call close($2) call open("/proc/self/uid_map", 1) call write($5, "0 1000 1", 8) call close($5) call open("/proc/self/gid_map", 1) call write($8, "0 1000 1", 8) call close($8) quit $ sh -c 'id; gdb --pid=$$ < removenetwork.gdb >/dev/null 2>&1; id; curl unix.stackexchange.com' uid=1000(user) gid=1000(user) groups=1000(user) uid=0(root) gid=0(root) groups=0(root) curl: (6) Could not resolve host: unix.stackexchange.com 

ОБНОВЛЕНИЕ : если root вообще не нужен, как показано в этом Вопросе, тогда вообще нет необходимости сопоставлять его с root. Просто замените вхождения write($XX, "0 1000 1", 8) на write($XX, "1000 1000 1", 11) (для случая uid:gid == 1000:1000 ). Дополнительные группы все еще неизбежно теряются, но uid / gid не меняется (отображается на себя).