Just how cursed is this? I intend to make something similar for use in my CMS project to support the installation of plugins using the application itself (I do plan on supporting WASM-based plugins the future).
The only cursed part of that is that you're restarting your program through cargo. Don't do that - don't assume that the Rust dev toolchain will be installed on the target system, even if it is actually installed there today. Instead, use std::env::current_exe to find the path the OS claims the running program is at, and re-exec that. Do read the caveats, though.
Other than that, this is an entirely standard method of reloading configuration by restarting the program. It has its limitations - keeping track of sockets and file handles across exec is a real challenge, for example - but it works well within them.
Of course, that does rule out rebuilding your program from source as a configuration mechanism. You aren't the first person to run into the issue that the dev tools may not be available on the target system - have a look at xcaddy for one possible approach to that.
On Linux it's safer to just run /proc/self/exe directly as opening that file will open the original executable even if it's been moved or deleted from the filesystem... assuming /proc is really procfs that is. (That's an assumption that current_exe also makes. It's not a promise the OS makes.)
If current_exe fails, the first env::args_os is usually (but not required to be) the path to the executable which the invoker supplied (could be a relative path), with all the same TOCTOU concerns on top.
Self-modifying programs are scary.
For one, this way you never know which code is actually running. E.g. if the installer modified the code and the following build failed, the code running may not match anything that’s still on the FS. That’s bad enough for debugging but from the security perspective, it’s a non-starter. Fixing that would require very careful manipulation.
Another security problem is that the whole program including all the plugins has to have write access to itself. This makes it so much easier to exploit simple bugs to RCE and take over the server.
Also, if anything in any of the plugins goes wrong it can take down the whole application, too. Including the (un)installer.
Though, much of this also applies to other kinds of in-process plugins as well. Separating the installer and the application (and sandboxing the latter real well) may be the important point.
I am well aware of these and have some ideas for potential mitigations:
- Making a backup before rebuilding, checking that the build is successful
- Implementing authorization checks as close as possible and testing thoroughly
I do agree this thing is a bit scary, my concern is that if I separate the plugin installer it will be less appealing to those who want it to just work
Probably not actually a concern, but if you're thinking of having portability in the future, Windows doesn't permit removing a running executable (at least, without some really scary permissions). You generally solve this (eg for self update) by copying the current executable to a(nother) temporary location and restarting from there, which has it's own set of headaches to make sure that dance doesn't explode into a fork bomb.
Having a second "updater" binary that can be handed off to can often simplify things, even when you're not on Windows.