All python code is run through RestrictedPython, using custom import
code that allows certain safe modules and creates wrappers around
necessary but unsafe modules. Indirect module imports are also banned (e.g.
from typing import sys as totally_not_sys).
External executables are executed via a sandbox program that limits file and network access. (see here)
closes #69 (closed)
- Implementation of wrappers for os, shutil and sys preventing filesystem writes outside of TMP_DIR.
We should also sandbox fs reads in src_unpack and in scopes outside the mod install functions to maintain the idea that we don't allow arbitrary fs reads until after src_unpack has finished (the only time network access is allowed). I'm not entirely sure how to do this though without relying on modifications to global state.
One idea worth pursuing is inspecting the stack to determine which function was called in mod.py. Stack inspection and modification should not be possible within the restricted environment, so it shouldn't be possible to fake this. We do need to be very careful when looking at the pathnames on the stack to ensure that someone doesn't write an install_mod function within the repo and fake the file within the pyclass dir (faking from a pybuild shouldn't be possible because pybuilds always use the pybuild extension).
Basically this requires writing a check_call function that does the stack inspection to determine if the call is valid for the given paths and read/write operation and raises an exception if not, and then we wrap all dangerous functions in a wrapper that calls check_call before calling the original function.
Linux sandboxing works fine. OSX sandboxing needs testing. Windows sandboxing is entirely dependent on the user (we need a wiki page for Windows installs...).
- Lots of documentation needs to be written. Particularly, what python code is actually allowed in pybuilds with this implementation.
- integration tests would be useful to ensure we don't have regressions on this issue
- Add write guard to ensure that modules are not writeable (currently, they are, and this could be a security flaw). Only objects defined in the pybuild file should be writeable. Making all attributes of modules was not viable. The solution was instead to clone modules before importing them so that changes don't affect other sources
- Block access to module attributes of modules. I think this basically means that we can't whitelist any modules outside of portmod (our whitelisted modules are minimalist import only modules) and all imports go through our own safemodules modules. This way we can ensure no unsafe attributes are available due to importing modules etc.. Solved as part of previous part. When cloning modules we only copy objects of type module if they are in the whitelist
Add TMP_DIR to omwmerge --info (needed for Windows users to set
I have a working local implementation (needs to be cleaned up, and module wrappers need to be finished). Unfortunately cloning modules to isolate pybuilds is a fairly slow operation (we don't currently cache even within the context of a particular pybuild). Hopefully this can be optimized in future, but for now it adds a considerable slowdown when loading many pybuilds. Edit: have done some optimization, but it is still much slower than before.