How to restrict user shell / terminal inside jupyterlab

I want only allow the users to execute pip install, ls and mv, rm. I tried to setup a restricted shell but on setting

rbash = "/home/jupyteruser/jupyter/users/restricted_shell.sh"
c.Spawner.default_shell = rbash
c.Spawner.shell_cmd     = [rbash]

the process hangs and no notebook is started. Inside restricted_shell.sh I have:

#!/bin/bash

cd() {
   echo "cd command is disabled."
}

export() {
   echo "export command is disabled."
}

exec /bin/rbash --norc

I also tried c.Spawner.shell_cmd = [‘/bin/bash’] but it doesnt work too (i.e. hangs the interface).

How to properly restrict users from leaving the given directory (which is by default /tmp/user01 …)? How to properly set my own shell command?

If you need user isolation, try looking into systemd Spawner. This spawner lets you to isolate users and control which paths they can have read/write access and also restrict the resources.

Thank you, I check this out next week. Any idea why c.Spawner.shell_cmd = [‘/bin/bash’] hangs the system? Commenting returns the system to behave as expected and uncommenting hangs the system again.
At least, is the call ok?

I guess you are missing -c flag. This Spawner.shell_cmd will be prepended to actual single user server command. So, it must be c.Spawner.shell_cmd = ['/bin/bash', '-c']. When you dont set -c, I guess subprocess is giving you back a pseudo shell and may be that is why it hangs?

UPDATE:

I changed now the config to this:

# from jupyterhub.spawner import SimpleLocalProcessSpawner
# c.JupyterHub.spawner_class = SimpleLocalProcessSpawner
c.JupyterHub.spawner_class = "systemd" # as specified on the git page

and get error

Bad config encountered during initialization: The 'spawner_class' trait of a JupyterHub instance expected a subclass of 'jupyterhub.spawner.Spawner', not the module <module 'systemd' from '/usr/lib/python3/dist-packages/systemd/__init__.py'>.

EDIT:
Maybe I’m at the wrong place with the rbash? What I want is to start a restricted bash if I run a terminal in the started Jupyter-Lab. I want to avoid users to navigate or do anything on the underlying server but only run pip if necessary.

Have you installed systemdspawner?

Yes, as described on the git page: pip install jupyterhub-systemdspawner

I did this for the user that starts JupyterHub, not for the users that login and use JupyterLab.

FINALLY: What would be the “official” way to restrict the terminal that the user gets in JupyterLab started via JupyterHub? Maybe this is better than searching the error at my site.

My JupyterHub config looks currently as follows (I commented out all that I tried but that is not working, i.e. if I start terminal inside JupyterLab I have always bash and access to all commands or JupyterHub cannot start JupyterLab, i.e. hangs):

# Configuration file for jupyterhub.

c = get_config()  #noqa

c.JupyterHub.extra_log_file = '/home/jupyteruser/jupyter/log/jupyterhub.log'

c.JupyterHub.bind_url = 'http://0.0.0.0:8002'

# =============================================
# restrict user terminal - nothing works !!!

# rbash = "/home/jupyteruser/jupyter/users/restricted_bash.sh"

# ========================
# restricted_bash.sh
"""
#!/bin/bash

while IFS= read -r -p "$ " line; do
    # Extract the first word of the command, which is typically the command itself
    cmd="${line%% *}"

    case $cmd in
        pip3|ls|cp|rm|exit)
            $line
            ;;
        *)
            echo "Command not allowed"
            ;;
    esac
done
"""
# ========================

# secure as much as possible
# c.ServerApp.terminado_settings = {
#     "shell_command": [rbash]
# }
# c.Spawner.extra_paths   = ['/home/jupyteruser/jupyter/users/']
# c.Spawner.default_shell = "/bin/rbash" # = rbash
# c.Spawner.shell_cmd     = [rbash]
# c.Spawner.shell_cmd     = ['/bin/rbash','-c']
# c.Spawner.environment = {
#     'SHELL': '/home/jupyteruser/jupyter/users/restricted_bash.sh'
# }
# c.ServerApp.terminado_settings = {
#     'shell_command': ['/home/jupyteruser/jupyter/users/restricted_bash.sh']
# }

import os

def set_user_environment(spawner):
    
    username        = spawner.user.name
    users_base_path = '/home/jupyteruser/jupyter/users'
    userpath        = f'{users_base_path}/{username}/bin'
    
    spawner.notebook_dir = f'{users_base_path}/{username}'
    
    spawner.environment['PATH']            = f'{userpath}:{users_base_path}/bin'
    spawner.environment['LD_LIBRARY_PATH'] = f'/home/jupyteruser/jupyter/instantclient_19_5'
    spawner.environment['SHELL']           = f'{users_base_path}/restricted_bash.sh'
    spawner.environment['DEFAULTSHELL']    = f'{users_base_path}/restricted_bash.sh'
    # spawner.environment['DEFAULTSHELL'] = "/bin/rbash" # rbash
    # spawner.default_shell = "/bin/rbash" # rbash
    # spawner.shell_cmd     = [rbash]
    # spawner.shell_cmd     = ['/bin/bash','-c',rbash]
    
    print("USER CONFIG: set_user_environment called ...")
    print("USER PATH:",userpath)

c.NotebookApp.terminals_enabled = False

c.JupyterHub.authenticator_class = 'firstuseauthenticator.FirstUseAuthenticator'

# ========================================================
# get dummy passwords from file

import json
with open("/home/jupyteruser/jupyter/passwords.txt","r") as fp: cred = json.load(fp)

c.Spawner.pre_spawn_hook = set_user_environment

c.Spawner.concurrent_spawn_limit = 32

# c.Authenticator.whitelist = {'user01', ... # replaced by file
# c.Spawner.dummy_passwords = {'user01': 'pwdUser01', ... # replaced by file

c.Authenticator.whitelist = list(cred.keys())
c.Spawner.dummy_passwords = cred
# ========================================================

from jupyterhub.spawner import SimpleLocalProcessSpawner
c.JupyterHub.spawner_class = SimpleLocalProcessSpawner
# c.JupyterHub.spawner_class = "systemd"

Your singleuser configuration needs to go into a separate file, e.g. jupyter_server_config.py (assuming you’re using a recent version of jupyterlab/notebook):
https://jupyter-server.readthedocs.io/en/latest/other/full-config.html
You’ll probably also need to use c.ServerApp. instead of c.NotebookApp.

Sorry, now I’m slightly lost. JupyterHub starts JupyterLab. What is now jupyter server?

Jupyter Server provides the backend for JupyterLab and some other frontends:
https://jupyter-server.readthedocs.io/en/latest/

Some versions of JupyterLab supported using notebook as the backend instead of jupyter-server, which is why the config file depends on what components you’re running.