View previous topic :: View next topic |
Author |
Message |
bjorn-fischer n00b
Joined: 13 Nov 2023 Posts: 8 Location: Bielefeld
|
Posted: Thu Jul 25, 2024 3:06 pm Post subject: New tool for network isolation: subrosa |
|
|
Hi folks,
every now and then I have the requirement to run an application isolated from the network.
True, I could set up a container each time, bind mount all necessary file systems into the
container and all that. But that's awfully much boiler plate if I just need network separation.
So I came up with the tiny tool subrosa. You can view it on github/bjorn-fischer/subrosa.
subrosa execs() the command given as arguments in a newly set up network name space
with just a loopback device in it:
Code: |
[bf@βελλεροφων:~]$ ping -c 3 193.0.0.164
PING 193.0.0.164 (193.0.0.164) 56(84) bytes of data.
64 bytes from 193.0.0.164: icmp_seq=1 ttl=54 time=24.6 ms
64 bytes from 193.0.0.164: icmp_seq=2 ttl=54 time=25.8 ms
64 bytes from 193.0.0.164: icmp_seq=3 ttl=54 time=22.3 ms
--- 193.0.0.164 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2002ms
rtt min/avg/max/mdev = 22.312/24.240/25.782/1.442 ms
[bf@βελλεροφων:~]$ subrosa bash
[bf@βελλεροφων:~]$ ifconfig
lo: flags=73<UP,LOOPBACK,RUNNING> mtu 65536
inet 127.0.0.1 netmask 255.0.0.0
inet6 ::1 prefixlen 128 scopeid 0x10<host>
loop txqueuelen 1000 (Local Loopback)
RX packets 0 bytes 0 (0.0 B)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 0 bytes 0 (0.0 B)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
[bf@βελλεροφων:~]$ ping -c 3 193.0.0.164
ping: connect: Network is unreachable
[bf@βελλεροφων:~]$ exit
exit
[bf@βελλεροφων:~]$
|
So, network isolation is just a subshell away.
Any comments? Just use containers or eBPF? Or is it even dangerous?
Cheers
Björn |
|
Back to top |
|
|
Hu Administrator
Joined: 06 Mar 2007 Posts: 22618
|
Posted: Thu Jul 25, 2024 3:38 pm Post subject: |
|
|
On principle, I am leery of anything suid-root, even when it is fairly simple.This (and later output) assumes stderr is valid and writable. Although that is usually true, the caller could have attached descriptor 2 to a read-only source, or even closed it entirely, causing your suid program to open a file as descriptor 2.
There is no check that socket succeeded. If socket failed, the ioctl will fail with a potentially confusing error message. It would be better to explicitly goto fail if socket fails, so that the errno explaining why socket failed is still available.
fd is never used after this point, but is not closed, and was not opened as O_CLOEXEC, so I think it will leak into the called process. In conjunction with the above concern about descriptor 2 being closed before program start, you could pass this socket into a child process as descriptor 2, which could cause confusing behavior from it.
Although the semantics are a little different, I prefer using bwrap for ad-hoc containers. It uses a user namespace, so it does not need to be installed suid root. That does, unfortunately, break programs that insist on running their own suid program, since being in the user namespace, that suid program will not get a real root uid. |
|
Back to top |
|
|
pietinger Moderator
Joined: 17 Oct 2006 Posts: 5089 Location: Bavaria
|
|
Back to top |
|
|
bjorn-fischer n00b
Joined: 13 Nov 2023 Posts: 8 Location: Bielefeld
|
Posted: Fri Jul 26, 2024 7:48 am Post subject: |
|
|
Hu wrote: | On principle, I am leery of anything suid-root, even when it is fairly simple. |
That is a sensible approach. I use subrosa with capabilities where ever possible.
Hu wrote: | This (and later output) assumes stderr is valid and writable. Although that is usually true, the caller could have attached descriptor 2 to a read-only source, or even closed it entirely, causing your suid program to open a file as descriptor 2. |
How does writing to a stream which underlying descriptor is closed lead to opening a file? Should not write attempts result in EBADF?
Hu wrote: | There is no check that socket succeeded. If socket failed, the ioctl will fail with a potentially confusing error message. It would be better to explicitly goto fail if socket fails, so that the errno explaining why socket failed is still available. |
Agreed. Already fixed that.
Hu wrote: | fd is never used after this point, but is not closed, and was not opened as O_CLOEXEC, so I think it will leak into the called process. In conjunction with the above concern about descriptor 2 being closed before program start, you could pass this socket into a child process as descriptor 2, which could cause confusing behavior from it. |
Yes indeed. How could that have slipped through?
Thank you for your suggestions. |
|
Back to top |
|
|
Hu Administrator
Joined: 06 Mar 2007 Posts: 22618
|
Posted: Fri Jul 26, 2024 2:23 pm Post subject: |
|
|
bjorn-fischer wrote: | How does writing to a stream which underlying descriptor is closed lead to opening a file? Should not write attempts result in EBADF? | Yes, writing to a closed descriptor will return EBADF. However, since the kernel allocates the lowest free descriptor when opening a new file, if your caller started you with fd 0 and fd 1 open, and fd 2 closed, then your first file-opening call (open, socket, etc.) will return 2. Since stderr is 2 by default, any later attempt to write to stderr will write to fd 2 - which may be the thing your root privileged program opened, not the fd 2 inherited from your caller. I don't see a way to get your program to open a file as fd 2, but if there were such a way, you would then be writing your error messages to that file, which your root privileges let you open, even if your caller's non-root privileges could not open that file. Such a scenario would be a way for an unprivileged caller to cause your setuid-root program to write text into a file that the caller cannot modify directly. |
|
Back to top |
|
|
|
|
You cannot post new topics in this forum You cannot reply to topics in this forum You cannot edit your posts in this forum You cannot delete your posts in this forum You cannot vote in polls in this forum
|
|