diff options
| author | 2019-09-14 22:11:22 +0200 | |
|---|---|---|
| committer | 2019-09-14 22:11:22 +0200 | |
| commit | 4d4691bc4feffb89470625e013a70d7d64f46a2f (patch) | |
| tree | 298a514d6ccce9af6ee3ca41bcf3f35794cfe503 /snekbox.py | |
| parent | Rename .github/FUNDING.yml to .github/.github/FUNDING.yml (diff) | |
| parent | Merge pull request #41 from python-discord/fix-flake8-docstrings (diff) | |
Merge pull request #22 from python-discord/revitalisation
Revitalisation
Diffstat (limited to 'snekbox.py')
| -rw-r--r-- | snekbox.py | 140 | 
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) | 
