如何取消当前进程的网络共享

如何取消当前进程的网络共享

可以使用非 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 脚本取消共享将不起作用,除非 shell 已被专门扩展以公开unshare.

答案1

这可以通过以下方式完成gdb调试器,以及是否可以附加正在运行的进程(改变其可转储状态的程序,或者不能附加到 setgid 等,除非来自 root)。

一些可选文件可以帮助使用 gdb ,例如 libc6 的调试符号,以及一些与 Linux 相关的包含文件,以便稍后获取一些符号的实际值(例如在 Debian 上:(可能)libc6-dbg和软件包),但实际上一旦“配方“制作完成后,可能不再需要它们了。libc6-devlinux-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 从 gdb 子进程更改自身。的内容,此处仅在使用==removenetwork.gdb更改进程时有效:pid:gid1000:1000

更新:为下面的系统调用添加了(近似)返回类型,这应该避免某些版本的 gdb 在非开发环境中抱怨:

call (int)unshare(0x50000000)
call (int)open("/proc/self/setgroups", 1)
call (long)write($2,"deny",4)
call (int)close($2)
call (int)open("/proc/self/uid_map", 1)
call (long)write($5, "0 1000 1", 8)
call (int)close($5)
call (int)open("/proc/self/gid_map", 1)
call (long)write($8, "0 1000 1", 8)
call (int)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 不会改变(映射到自身)。

相关内容