Want to restrict the import of certain python modules like OS, sys or subprocess

I an running a jupyter environment (jupyter lab) inside a docker container
I want to restrict the imports of certain python modules like os, sys and subprocess
such that a untrusted user is unable to access of manipulate any os level files.
is there a way to achieve that?

My main goal is to restrict users from accessing os level folders and filessystem.

is this approach valid and can this be achieved?

A custom kernel would be the way to do this, since the kernel is what’s responsible for interpreting user code. but it is strongly advised to implement such restrictions at a higher level than user code execution, especially if code to be executed is Python. The point of Jupyter is arbitrary code execution and it’s quite difficult to lock that down in general, but also in Python in particular where there are many ways to access e.g. the os module (sys.modules["os"], getattr on any other module that may have imported os).

If you want to write a custom kernel with your own DSL that prohibits imports, etc. you might be able to , as long as what you want to allow users to do is extremely limited. But if you want anything like “do anything except this”, then I think it’s very unlikely that you will be successful at the kernel level.

A much stronger approach is to assume arbitrary code execution in the notebook itself, but lock down the container in which the notebook executes so that no matter what they manage to execute, you know they can’t do the kinds of things you don’t want them to outside the the container. There are lots of powerful tools for locking down containers so they can’t do anything you don’t want them to, no matter what code is executed, especially things like restricting filesystem access.

1 Like

I tried running the below script on pre-spawn-hook, its working for our usecase

import os
import django
import importlib
from django.apps import apps
import environ
import requests
def restricted_methods(method):
    raise Exception("This method is restricted!!!") 
def current_user_groups(hub_api_url, jupyterhub_api_token):
    req = requests.get(
        hub_api_url + '/hub/api/user',
        headers={'Authorization': f'token {jupyterhub_api_token}'}
    )
    users = req.json()
    return users
def is_import_restricted(item, restricted_imports):
    return item in restricted_imports
def custom_importer(name, globals=None, locals=None, fromlist=(), level=0, users=None, users_group=None, jupyterhub_config=None):
    if users.get('admin') or not fromlist:
        return importlib.__import__(name, globals, locals, fromlist, level)
    for group in users_group:
        restricted_imports = jupyterhub_config['methods_imports_to_disable'].get(group, {}).get('imports', [])
        if any(is_import_restricted(item, restricted_imports) for item in fromlist):
            restricted_items = [item for item in fromlist if is_import_restricted(item, restricted_imports)]
            raise ImportError(f"Imports {', '.join(restricted_items)} are restricted.")
    return importlib.__import__(name, globals, locals, fromlist, level)
def run_production_code():
    env = environ.Env()
    environ.Env.read_env()
    jupyterhub_config = env('JUPYTERHUB_CONFIG', default={
        'hub_api_url': 'http://127.0.0.1:8888',
        'methods_imports_to_disable': {
            'DEVOPS-USERS': {
                'imports': [],
                'methods': []
            },
            'TECH_SUPPORT-USERS': {
                'imports': ['models', 'Product', '*'],
                'methods': ['save']
            }
        }
    })
    # Set up Django environment
    os.chdir('/srv/www/project/')
    os.environ.setdefault("DJANGO_ALLOW_ASYNC_UNSAFE", 'true')
    os.environ.setdefault("DJANGO_SETTINGS_MODULE", "project.settings")
    django.setup()
    # Fetch user groups from the Hub API
    jupyterhub_api_token = os.environ.get("JUPYTERHUB_API_TOKEN")
    users = current_user_groups(jupyterhub_config['hub_api_url'], jupyterhub_api_token)
    users_group = users.get('groups', []) if len(users.get('groups', [])) > 0 else ['TECH_SUPPORT-USERS']
    #built-in __import__ function with custom importer
    __builtins__.__dict__['__import__'] = lambda name, globals=None, locals=None, fromlist=(), level=0: custom_importer(name, globals, locals, fromlist, level, users, users_group, jupyterhub_config)
    
    # Disable specified methods based on user groups
    for model in apps.get_models():
        if users.get('admin'):
            break
        for group in users_group:
            methods_to_disable = jupyterhub_config['methods_imports_to_disable'].get(group, {}).get('methods', [])
            [setattr(model, method, restricted_methods) for method in methods_to_disable if hasattr(model, method)]
if __name__ == "__main__":
    run_production_code()