From b4a2e6403cae79bdc5e6002a601a2b63b3fc58a4 Mon Sep 17 00:00:00 2001 From: Nicolas Setton Date: Wed, 23 Jan 2019 11:16:48 -0500 Subject: [PATCH] Make runs under the 'unprivileged' user Simplify the handling of timeouts. --- compile_server/app/checker.py | 19 +++++++++---- .../container_payload/Makefile.safecontainer | 3 +++ infrastructure/container_payload/run.py | 27 +++++++++++++------ 3 files changed, 36 insertions(+), 13 deletions(-) diff --git a/compile_server/app/checker.py b/compile_server/app/checker.py index ef22d17..6633dfd 100644 --- a/compile_server/app/checker.py +++ b/compile_server/app/checker.py @@ -281,13 +281,22 @@ def run_program(request): doctor_main_gpr(tempd, main) + # Push the code to the container + + try: + subprocess.check_call(["lxc", "file", "push", "--recursive", tempd, + "safecontainer/workspace/sessions/"]) + subprocess.check_call(["lxc", "exec", "safecontainer", "--", + "chmod", "-R", "a+rx", + "/workspace/sessions/{}".format + (os.path.basename(tempd))]) + except subprocess.CalledProcessError, exception: + result = {'message': "error transferring the program"} + return CrossDomainResponse(result) + # Run the command(s) to check the program commands = [ - # Copy the program over - ["lxc", "file", "push", "--recursive", tempd, - "safecontainer/workspace/sessions/"], - - # Run it + # Run the program ["lxc", "exec", "safecontainer", "--", "su", "runner", "-c", "python /workspace/run.py /workspace/sessions/{} {} {}".format( diff --git a/infrastructure/container_payload/Makefile.safecontainer b/infrastructure/container_payload/Makefile.safecontainer index f047f64..1aae8d3 100644 --- a/infrastructure/container_payload/Makefile.safecontainer +++ b/infrastructure/container_payload/Makefile.safecontainer @@ -33,6 +33,9 @@ protect: mkdir -p /home/unprivileged chown unprivileged /home/unprivileged + # Allow runner to run unprivileged + echo "runner ALL=(unprivileged) NOPASSWD:ALL" > /etc/sudoers.d/runner + # Prevent unprivileged from writing to /tmp chmod 775 /tmp chmod 775 /var/tmp diff --git a/infrastructure/container_payload/run.py b/infrastructure/container_payload/run.py index a0d9c43..a4a0e78 100644 --- a/infrastructure/container_payload/run.py +++ b/infrastructure/container_payload/run.py @@ -15,7 +15,8 @@ import traceback CONT = 'safecontainer' INTERRUPT_STRING = '' -DEBUG = True +INTERRUPT_RETURNCODE = 124 +DEBUG = False def run(command): @@ -30,27 +31,36 @@ def run(command): def safe_run(workdir, mode, main): - def c(cl): + def c(cl=[]): """Aux procedure, run the given command line and output to stdout""" try: if DEBUG: print "running: {}".format(cl) - subprocess.call(cl, cwd=workdir, stdout=sys.stdout, shell=True) + returncode = subprocess.call(cl, cwd=workdir, + stdout=sys.stdout, shell=False) + if returncode == INTERRUPT_RETURNCODE: + print INTERRUPT_STRING return True except Exception: print "ERROR when running {}".format(' '.join(cl)) traceback.print_exc() return False - c(["echo", workdir, mode, main]) + c(["echo"]) try: if mode == "run": + # In "run" mode, first build, and then launch the main if c(["gprbuild", "-q", "-P", "main"]): if main: - line = 'timeout 10s bash -c "LD_PRELOAD=/preloader.so {}" || echo {}'.format( - os.path.join(workdir, main.split('.')[0]), - INTERRUPT_STRING) - c([line]) + # We run: + # - as user 'unprivileged' that has no write access + # - under a timeout + # - with our ld preloader to prevent forks + line = ['sudo', '-u', 'unprivileged', 'timeout', '10s', + 'bash', '-c', + 'LD_PRELOAD=/preloader.so {}'.format( + os.path.join(workdir, main.split('.')[0]))] + c(line) except Exception: traceback.print_exc() @@ -70,6 +80,7 @@ if __name__ == '__main__': if len(sys.argv) > 3: main = sys.argv[3] + # This is where the compiler is installed os.environ["PATH"] = "/gnat/bin:{}".format(os.environ["PATH"]) safe_run(workdir, mode, main)