It would be more interesting if we just made more sandboxing powers available for traditional processes, though there is already quite a lot of sandboxing available for Linux processes...
CGI starts a process per request. fastCGI can reuse processes. Problem already found and solved 20 years ago.
catern@home ~ $ { timeout 1 sh -c 'while :; do /bin/echo foo; done'; echo; } | wc -l
1542
So at least sub-millisecond. And this is less efficient than possible because it's also writing to a pipe.Also, I quite agree that CGI is a bad way to do things, but that's what the original post is proposing. :)
You just described containers :)
Most of that is not really necessary if you are just coming from a standpoint of "I want to sandbox a process". Indeed, if you just want to sandbox a process, just run it under a dedicated non-root user account, and it won't be able to unduly interfere with the rest of the system.
This is simply not true, unless your definition of "sandboxing" only means "run it under another account" and absolutely nothing more -- but under this definition, non-root processes can 100% interfere with the rest of the system without proper control being put into place. "Intereference" can mean a lot more than "writes to my ~ dir" -- for example, monopolizing a system resource (e.g. IOPS or network bandwidth), adding code to the kernel (module autoloading) or simply using a resource at all when it shouldn't, or rogue problems like termination of a hostile/stupid set of processes (cgroups and namespaces are one of the few reliable ways to kill process groups in non-racey ways, etc).
> Most of that is not really necessary if you are just coming from a standpoint of "I want to sandbox a process".
No, a substantial amount of it is necessary if you actually want to sandbox a process in a way that's remotely isolated from the host system. Use cases like Flatpak and Docker are nice, but merely byproducts of the design that allow custom mount namespaces, etc. The original use cases for namespacing, cgroups, etc was precisely to give more fine-grained resource and isolation control over existing applications, in a hierarchical manner.
That is a common myth that is circulated, and it is a falsehood. Ironically, control groups are subject to the very race conditions that you mention.
* http://jdebp.eu./FGA/linux-control-groups-are-not-jobs.html
Contrary to your article, I think systemd's logic also exits in guaranteed finite time even with adversarial scheduling. You didn't take into account the fact that systemd is (I assume) not reaping zombies when it's doing its kill-loop. Each zombie that is left unreaped occupies a pid. Eventually, pids will be exhausted and processes inside the cgroup will no longer be able to dodge kill() by forking off new processes.
Your supervise isn't a "superior API", of course. It does a whole bunch of parsing and constructing human-readable strings at runtime, which something that the 1980s and 1990s taught better than to do. A proper directly-program-usable binary interface actually already exists, as used by Daniel J. Bernstein's original supervise program from daemontools, and has been in widespread use for two decades with a whole bunch of tools in toolsets from a variety of people that speak it. I am one of the people who has documented it in detail. See the manual page for my service-manager command.
You are at about the same level that the supervise in Daniel J. Bernstein's daemontools was in (if memory serves) 1996, before experience taught some very important lessons about the daemonization fallacy.
Not only can Daniel J. Bernstein's daemontools demonstrate some very important lessons, so can the humble ps command (which uses "status" not "stat"). The systemd people (and indeed, again, the humble ps command) can teach how to loop over all of the processes in a system without including process #1 and potentially issuing tens of thousands or even millions of open() and kill() system calls almost all of which are useless and fail. Daniel J. Bernstein's UCSPI and systemd's LISTEN_FDS mechanism can teach ways of telling programs about the inherited open file descriptors that they are to use, that will interoperate with existing tools such as fifo-listen.
And even Stack Overflow can teach the error of assuming that sizeof(bool) is 1. (-:
Sorry, what are you even talking about? I pointed out a specific mechanism by which the following assertion made by you:
>A program that forks new processes quickly enough within the cgroup can keep systemd spinning for a long time, in theory indefinitely as long as suitable "weather" prevails, as at each loop iteration there will be one more process to kill. Note that this does not have to be a fork bomb. It only has to fork enough that systemd sees at least one more new process ID in the cgroup every time that it runs its loop.
might be incorrect. (That mechanism is that pid exhaustion will happen eventually, as long as systemd doesn't collect zombies.) Why don't you read the systemd source to confirm that this bug really does exist? Or at the very least, amend your article to admit that you have not done so!
I only know for a fact that my own "supervise" does not have this bug, and since I am using kernel APIs which were originally created by the systemd developers, I would guess that systemd does not have this bug either. But you're the one stating as fact that systemd does have this vulnerability, so I think you have a bit more of the burden of proof! :)
Anyway, regarding your other comments about my supervise utility. Yes, it has a human-readable plain-text interface, so what? It's still got a feature that no other tool has: It allows daemons to fork off their own children, without any risk of bugs causing stray processes to escape supervision, without requiring privileges. I would love to learn of another tool (on Linux) that has this feature, so please tell me! djb's supervise certainly is not capable of this, nor is nosh, nor is any other daemontools derivative that I know of.
>before experience taught some very important lessons about the daemonization fallacy.
What exactly is that fallacy? :) Perhaps you just mean "daemonization is a bad idea", in which case I fully agree?
>The systemd people (and indeed, again, the humble ps command) can teach how to loop over all of the processes in a system without including process #1 and potentially issuing tens of thousands or even millions of open() and kill() system calls almost all of which are useless and fail.
Yes, my supervise utility is currently not very optimized at shutdown, so what? Starting and stopping processes is a rare operation. :) I will optimize it later, when optimization is needed...
>Daniel J. Bernstein's UCSPI and systemd's LISTEN_FDS mechanism can teach ways of telling programs about the inherited open file descriptors that they are to use, that will interoperate with existing tools such as fifo-listen.
A little condescending, don't you think? I am well aware of these tools. They hardcode logic about where to find file descriptors. I prefer the CloudABI argdata style, explicitly passing in the file descriptor number to use, so that conflicts can be avoided. I find that explicitly passing in the fd number allows for more interoperation with existing tools, not less.
People have been placing ulimits on users to limit their resource usage for decades, using cgroups to do it is not a novel concept, just more effective.
>rogue problems like termination of a hostile/stupid set of processes (cgroups and namespaces are one of the few reliable ways to kill process groups in non-racey ways, etc).
The ability for a user to leave processes around on the system has been fixed for years now, with systemd-logind which puts each user in its own cgroup.
>adding code to the kernel (module autoloading)
This is a concern both for container systems and regular user isolation, and the only reliable way to fix it for either is to disable module autoloading, or at least restrict the available modules to well-maintained ones.
>The original use cases for namespacing, cgroups, etc was precisely to give more fine-grained resource and isolation control over existing applications, in a hierarchical manner.
Yes, and they failed. The design of Linux namespaces and cgroups is awful. You can perceive this by the fact that they require privileges to use. Hopefully in a few years all this Flatpak/Docker churn will output some namespacing APIs for Linux that are actually good.
We'll just keep building layers as manufacturers try to lock the lower ones down :)
See WeChat in China for a good example on Android
But doesn't that mean we are merely reimplementing old concepts on a new level? Almost everything we do on VM level we already did on process level:
- Read-only VM images are essentially like statically linked binaries, just better encapsulated when they run.
- Tiny, fast loading VM images are essentially stripped static binaries with dead code elimination.
- Starting those by incoming network request is just like old "inetd" versions started custom handler programs on each request.
- Same with CGI, just on HTTP rather than TCP level.
- Manual maintaining the lifecycle of those VMs like FastCGI in nginx.
- Maintaining the lifecycle of such a started VM is essentially like FastCGI in Apache (or nginx + auto-spawner).
The only real difference is the improved encapsulation (controlled network access and resource consumption) of the running code, isn't it?
What might come next?
- Multiple VMs could share the same images to save memory, essentially reinventing shared libraries and dynamic linking.
- Network traffic into and out of a VM is controlled more tightly, reinventing SELinux. (Perhaps followed by noticing that these profiles are hard to maintain, rediscovering the maintenance issues of SELinux systems.)
- Providing the network traffic shape directly with the image, so these can be maintained at a single place, by the original developers. This would reinvent Pledge.
- Introducing permissions for network traffic, e.g. such that just one group of application VMs can communicate with the database VMs. Ideally with an ACL system as used for managing file permissions. This would reinvent Unix Sockets.