Ever wanted to run a section of your code in a "more restricted environment"?, maybe a la container... for instance, ever wanted to run a method, but not allow it to access the internet, or to not be able to write data, or to limit the amount of cpu a single python method can take?... well I have... so I made concurrentd just for that
In coding as in screenplay writing, the logic is show not tell. So here are a couple of practical examples
let's assume you want to execute a method, but want that method to run as a different user, using only 20% of CPU, with no network access, and having the entire filesystem as read only (except for tmp), then this code will get you there
import time
from concurrentd.transient import TransientUnitPoolExecutor
def busy_cpu(timeout):
# doing redistricted stuff...
t0 = time.time()
while time.time() - t0 < timeout:
2**64 -1
if __name__ == "__main__":
with TransientUnitPoolExecutor(ns_options={
"PrivateTmp": True, # mounts /tmp unique to the process
"PrivateNetwork": True, # disable network
"CPUQuota": 0.2, # only use 20% of CPU
"ProtectSystem": "strict", # mount filesystem as read only
"User": "nobody" # runs code a nobody
}) as pool:
print('bootstrap unit', pool.unit.Id.decode())
pool.submit(busy_cpu, timeout=5)
pool.submit(busy_cpu, timeout=10)
pool.submit(busy_cpu, timeout=15)
with this, restricted_method will be executed by the user nobody (User="nobody") on an environment that can't use more than 20% of CPU (CPUQuota=0.2), with no network (PrivateNetwork=True executes the code with only the loopback network interface), with root mounted as read only (ProtectSystem=strict) and a private view /tmp (PrivateTmp=True)that will get clean as soon as the executor has finish
very simple we use systemd, namespaces and cgroups to execute your code... TransientUnitPoolExecutor
works the same as
concurrent.futures.ProcessPoolExecutor, with the only difference that when you start TransientUnitPoolExecutor, it wil create
a systemd transient unit with the ns_options you pass. Then every time you submit work to the pool executor, it will work
as a regular subprocess, except that after been fork, the code is moved into the namespaces of the transient unit, the cgroup
and the user and group of the process will be set to the user and group of the transient unit.
you can specify most options in systemd.directives.
- Run as root
- Systemd