aboutsummaryrefslogtreecommitdiffstats
path: root/snekbox.py
diff options
context:
space:
mode:
Diffstat (limited to 'snekbox.py')
-rw-r--r--snekbox.py140
1 files changed, 0 insertions, 140 deletions
diff --git a/snekbox.py b/snekbox.py
deleted file mode 100644
index 7491672..0000000
--- a/snekbox.py
+++ /dev/null
@@ -1,140 +0,0 @@
-import json
-import multiprocessing
-import os
-import subprocess
-import sys
-
-from rmq import Rmq
-
-
-class Snekbox:
- """Core snekbox functionality, providing safe execution of Python code."""
-
- def __init__(self,
- nsjail_binary='nsjail',
- python_binary=os.path.dirname(sys.executable) + os.sep + 'python3.6'):
- self.nsjail_binary = nsjail_binary
- self.python_binary = python_binary
- self._nsjail_workaround()
-
- env = {
- 'PATH': (
- '/snekbox/.venv/bin:/usr/local/bin:/usr/local/'
- 'sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin'
- ),
- 'LANG': 'en_US.UTF-8',
- 'PYTHON_VERSION': '3.6.5',
- 'PYTHON_PIP_VERSION': '10.0.1',
- 'PYTHONDONTWRITEBYTECODE': '1',
- }
-
- def _nsjail_workaround(self):
- dirs = ['/sys/fs/cgroup/pids/NSJAIL', '/sys/fs/cgroup/memory/NSJAIL']
- for d in dirs:
- if not os.path.exists(d):
- os.makedirs(d)
-
- def python3(self, cmd):
- """
- Execute Python 3 code in a isolated environment.
-
- The value of ``cmd`` is passed using '-c' to a Python
- interpreter that is started in a ``nsjail``, isolating it
- from the rest of the system.
-
- Returns the output of executing the command (stdout) if
- successful, or a error message if the execution failed.
- """
- args = [self.nsjail_binary, '-Mo',
- '--rlimit_as', '700',
- '--chroot', '/',
- '-E', 'LANG=en_US.UTF-8',
- '-R/usr', '-R/lib', '-R/lib64',
- '--user', 'nobody',
- '--group', 'nogroup',
- '--time_limit', '2',
- '--disable_proc',
- '--iface_no_lo',
- '--cgroup_pids_max=1',
- '--cgroup_mem_max=52428800',
- '--quiet', '--',
- self.python_binary, '-ISq', '-c', cmd]
- try:
- proc = subprocess.Popen(args,
- stdin=subprocess.PIPE,
- stdout=subprocess.PIPE,
- stderr=subprocess.PIPE,
- env=self.env,
- universal_newlines=True)
- except ValueError:
- return 'ValueError: embedded null byte'
-
- stdout, stderr = proc.communicate()
- if proc.returncode == 0:
- output = stdout
-
- elif proc.returncode == 1:
- try:
- filtered = []
- for line in stderr.split('\n'):
- if not line.startswith('['):
- filtered.append(line)
- output = '\n'.join(filtered)
- except IndexError:
- output = ''
-
- elif proc.returncode == 109:
- return 'timed out or memory limit exceeded'
-
- elif proc.returncode == 255:
- return 'permission denied (root required)'
-
- elif proc.returncode:
- return f'unknown error, code: {proc.returncode}'
-
- else:
- return 'unknown error, no error code'
-
- return output
-
- def execute(self, body):
- """
- Handles execution of a raw JSON-formatted RMQ message, contained in ``body``.
-
- The message metadata, including the Python code to be executed, is
- extracted from the message body. The code is then executed in the
- isolated environment, and the results of the execution published
- to RMQ. Once published, the system exits, since the snekboxes
- are created and disposed of per-execution.
- """
- msg = body.decode('utf-8')
- result = ''
- snek_msg = json.loads(msg)
- snekid = snek_msg['snekid']
- snekcode = snek_msg['message'].strip()
-
- result = self.python3(snekcode)
-
- rmq.publish(result,
- queue=snekid,
- routingkey=snekid,
- exchange=snekid)
- exit(0)
-
- def message_handler(self, ch, method, properties, body, thread_ws=None):
- """Spawns a daemon process that handles RMQ messages."""
- p = multiprocessing.Process(target=self.execute, args=(body,))
- p.daemon = True
- p.start()
-
- ch.basic_ack(delivery_tag=method.delivery_tag)
-
-
-if __name__ == '__main__':
- try:
- rmq = Rmq()
- snkbx = Snekbox()
- rmq.consume(callback=snkbx.message_handler)
- except KeyboardInterrupt:
- print('Exited')
- exit(0)