diff --git a/README.md b/README.md index 9f62875..861ecf9 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,13 @@ Prototype server for creating interactive "try SPARK / try Ada" webpages +## Requirements + +In addition to Python, this system relies on LXC to sandbox +the run of executables. To do this, you need + - a container named "safecontainer" + - this container should have a non-admin user 'ubuntu' + ## Getting started To setup, do this: diff --git a/compile_server/app/checker.py b/compile_server/app/checker.py index f6abfcb..53fe248 100644 --- a/compile_server/app/checker.py +++ b/compile_server/app/checker.py @@ -214,11 +214,16 @@ def run_program(request): # Run the command(s) to check the program commands = [ + # Build the program ["gprbuild", "-q", "-P", "main"], - # TODO: implement a safe execute in a container - [os.path.join(tempd, main[:-4])], + # Launch the program via "safe_run", to sandbox it + ["python", + os.path.join(os.path.dirname(__file__), 'safe_run.py'), + os.path.join(tempd, main[:-4])], ] + print commands + try: p = process_handling.SeparateProcess(commands, tempd) message = "running gnatprove" diff --git a/compile_server/app/safe_run.py b/compile_server/app/safe_run.py new file mode 100644 index 0000000..6165680 --- /dev/null +++ b/compile_server/app/safe_run.py @@ -0,0 +1,60 @@ +""" This is a standalone Python script that runs its argument + safely in a container. + + At the moment it assumes that the container "safecontainer" + exists and is running. +""" + +import os +import time +import sys +import subprocess + +CONT = 'safecontainer' +DEBUG = False + + +def run(command): + if DEBUG: + print ">", " ".join(command) + output = subprocess.check_output(["lxc", "exec", CONT, "--"] + command) + if output: + output = output.rstrip() + if DEBUG: + print "<", output + return output + + +def safe_run(main): + # Make a temporary directory on the container + tmpdir = run(["mktemp", "-d"]) + + try: + run(["chown", "ubuntu", tmpdir]) + + # Push the executable to the container + subprocess.check_call(["lxc", "file", "push", main, + # This requires the dir to end with / + CONT + tmpdir + os.sep]) + + # TODO: rlimit? + + # Run it, printint output to stdout as we go along + subprocess.call(["lxc", "exec", CONT, "--", + "su", "ubuntu", "-c", + os.path.join(tmpdir, os.path.basename(main))], + stdout=sys.stdout) + except E: + print sys.exc_info() + + finally: + if tmpdir and tmpdir.startswith("/tmp"): + time.sleep(0.2) # Time for the filesystem to sync + run(["rm", "-rf", tmpdir]) + + +if __name__ == '__main__': + # Do not perform any sanity checking on args - this is not meant to + # be launched interactively + main = sys.argv[1] + safe_run(main)