It's possible to run a new command without network access as non-root using unshare -r -n
, for example:
$ unshare -r -n ls
a.txt b.txt
A command that does require network access will fail predictably.
$ unshare -r -n curl unix.stackexchange.com
curl: (6) Could not resolve host: unix.stackexchange.com
I'm wondering whether it's possible to remove network access for the current process, potentially by writing to a magical file in /sys
or something similar.
I'd like to be able to do something like
$ /bin/sh -c 'echo 1 > /sys/unsharethis; curl unix.stackexchange.com'
An excerpt from strace
-ing unshare -r -n ls
shows the unshare
system call
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
Which suggests to me that unsharing the network access from the current process is in fact the only way of achieving unsharing (i.e. it can't be passed as an argument to spawn
or some equivalent thereof). It also suggests that unsharing from a shell script wouldn't work unless the shell had been specifically extended to expose a wrapper around unshare
.
Best Answer
This can be done, sort of, with the
gdb
debugger, and if the running process can be attached to (programs that alter their dumpable state, or are setgid etc. can't be attached to, unless from root).Some optional files can help to use gdb like debug symbols for libc6, and a few Linux related include files to get the actual values of a few symbols later (eg on Debian: (possibly)
libc6-dbg
,libc6-dev
andlinux-libc-dev
packages), but actually once the "recipe" is made, they're probably not needed anymore.First what more than
unshare()
unshare -r
is doing? Without this, the new user stays atnobody
and can't even write as its initial user:That will be used later.
On an other terminal:
[...]
Now let's call the first function:
Ok, there might be a method to have gdb know it, but I'm not a guru:
On the altered process, one can verify the
eth0
interface disappeared:There's no going back: the new user namespace can't change back to its initial namespace. If the process is running with enough privileges (eg root without lost capabilities nor SELinux) then it would be possible (using only
unshare(CLONE_NEWNET)
/setns(savedopenedfd)
).Of course it's possible to script it in a file, and alter any allowed running process, or have the shell alter itself from a gdb subprocess. Contents of
removenetwork.gdb
, valid here only to alter a process withpid:gid
==1000:1000
:UPDATE: added the (approximate) return type for the syscalls below, this should avoid some versions of gdb to complain in non-dev environments:
Example:
UPDATE: if root is not needed at all, as it appears for this Question, then there's no need to map to root at all. Simply replace occurences of
write($XX, "0 1000 1", 8)
withwrite($XX, "1000 1000 1", 11)
(for theuid:gid
==1000:1000
case). Supplementary groups are still unavoidably lost, but the uid/gid doesn't change (is mapped to itself).