현재 프로세스의 네트워크 공유를 취소하는 방법

현재 프로세스의 네트워크 공유를 취소하는 방법

루트가 아닌 ID를 사용하여 네트워크 액세스 없이 새 명령을 실행할 수 있습니다 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제가 보기에는 현재 프로세스의 네트워크 액세스 공유를 해제하는 것이 실제로는 공유 해제를 구현하는 유일한 방법인 것 같습니다(즉, 인수 등 으로 전달할 수 없습니다 ). 또한 셸이 노출되도록 특별히 확장되지 않는 한 셸 스크립트에서 공유 해제가 작동하지 않음을 나타냅니다 unshare.

답변1

이는 다음을 통해 수행할 수 있습니다.gdb디버거 및 실행 중인 프로세스(덤프 가능한 상태를 변경하는 프로그램 또는 루트가 아니면 setgid 등에 연결할 수 없는 프로그램)에 연결할 수 있는지 여부.

libc6용 디버깅 기호와 같이 gdb 사용에 도움이 되는 일부 선택적 파일과 나중에 일부 기호의 실제 값을 가져오는 일부 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

되돌릴 수 없습니다. 새 사용자 네임스페이스는 원래 네임스페이스로 다시 변경할 수 없습니다. 이는 프로세스가 충분한 권한(예: 루트 및 기능 또는 SELinux 손실 없음)으로 실행 중인 경우 가능합니다(단지 unshare(CLONE_NEWNET)/ 사용 setns(savedopenedfd)) .

물론 이를 파일에 작성하고 허용된 실행 프로세스를 변경하거나 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

고쳐 쓰다: 이 질문에서 알 수 있듯이 루트가 전혀 필요하지 않은 경우 루트에 매핑할 필요가 전혀 없습니다. 발생을 (for == 케이스) write($XX, "0 1000 1", 8)로 바꾸십시오 . 보충 그룹은 여전히 ​​필연적으로 손실되지만 uid/gid는 변경되지 않습니다(자체에 매핑됨).write($XX, "1000 1000 1", 11)uid:gid1000:1000

관련 정보