jupyterhub / jupyter-rsession-proxy

Jupyter extensions for running an RStudio rsession proxy
BSD 3-Clause "New" or "Revised" License
118 stars 87 forks source link

/var/run/rstudio is not writable by non root users #118

Closed juliusvonkohout closed 2 years ago

juliusvonkohout commented 2 years ago

@ryanlovett I guess there are more elegant solutions, i can change it as you like.

If you do not want to fix it, it should be at least stated in the documentation that you have to chmod 1777 /var/run/rstudio-server/ because there is zero logging and it takes a lot of time to find the reason for this error.

[~]$ /usr/lib/rstudio-server/bin/rserver --auth-none=1 --www-frame-origin=same --www-port=12345 --www-verify-user-agent=0 --secure-cookie-key-file=/tmp/tmpjqxz6d9a --server-user=1000890000 --www-root-path={base_url}rstudio/ --database-config-file=/tmp/tmpbtj913a_/tmp8ewz1mwb2021-12-03T11:07:59.337534Z [rserver] ERROR Could not change permissions for specified 'server-data-dir' - the directory (/var/run/rstudio-server) must be writeable by all users and have the sticky bit set; LOGGED FROM: int main(int, char* const*) src/cpp/server/ServerMain.cpp:684
2021-12-03T11:07:59.337944Z [rserver] ERROR system error 1 (Operation not permitted) [path: /var/run/rstudio-server]; OCCURRED AT rstudio::core::Error rstudio::core::{anonymous}::changeFileModeImpl(const string&, mode_t) src/cpp/shared_core/FilePath.cpp:313; LOGGED FROM: int main(int, char* const*) src/cpp/server/ServerMain.cpp:685
welcome[bot] commented 2 years ago

Thanks for submitting your first pull request! You are awesome! :hugs:
If you haven't done so already, check out Jupyter's Code of Conduct. Also, please make sure you followed the pull request template, as this will help us review your contribution more quickly. welcome You can meet the other Jovyans by joining our Discourse forum. There is also a intro thread there where you can stop by and say Hi! :wave:
Welcome to the Jupyter community! :tada:

ryanlovett commented 2 years ago

Thanks @juliusvonkohout ! Yes, let's fix this. I'm guessing that putting our db config file into the data dir won't cause any issues. I'll add a comment...

juliusvonkohout commented 2 years ago

i spent hours to debug something that only happens when you have a proxy in front of rserver. I found out that in some constellations jupyter-server-proxy or rstudio mutilate hostnames with hyphens. In the end i came up with the following. I tested it with jupyterhub + proxy on openshift, jupyterlab with proxy on openshift, jupyterlab on localhost and jupyterlab inside kubeflow.

import getpass
import os
import pathlib
import shutil
import subprocess
import tempfile
from textwrap import dedent
from urllib.parse import urlparse, urlunparse, urlsplit

def rewrite_response(response, request):
    # /rstudio/ forwards with 302 to /rstudio/auth-sign-in and back to /rstudio/
    # rstudio cannot forward to hostnames that contain hyphens behind proxies.
    # Host: jupyterlab-dsws.apps.p028.otc.mcs-paas.io and 
    # X-Forwarded-Host: jupyterlab-dsws.apps.p028.otc.mcs-paas.io
    # are mutilated to "jupyterlab". The parsing just stops after the first hyphen.
    # So we just use the hostname from the original request as 302 location
    for header, value in response.headers.get_all():
        if header == "Location":
            split = urlsplit(value)
            scheme = split.scheme
            hostname = request.host
            #port = ':'+str(split.port) if split.port else ''
            path = split.path
            new_location = scheme+ '://' + hostname + path
            #with open('/tmp/rstudio_proxy_log.txt', 'a') as file:
            #    file.write('Processing response' + '\n')
            #    file.write('The orignal request: ' + str(request) + '\n')
            #    file.write('response.headers: ' + str(response.headers) + '\n')
            #    file.write('response.body: ' + str(response.body) + '\n')
            #    file.write('response.code: ' + str(response.code) + '\n')
            #    file.write('response.reason: ' + str(response.reason) + '\n')
            #    file.write('new location: ' + new_location + '\n')
            response.headers[header] = new_location

def get_rstudio_executable(program):
    other_paths = [
        os.path.join('/usr/lib/rstudio-server/bin', program),
        os.path.join('/usr/lib/rstudio/bin', program),
    ]
    if shutil.which(program):
        return program
    for op in other_paths:
        if os.path.exists(op):
            return op
    raise FileNotFoundError(f'Could not find {program} in PATH')

def get_icon_path():
    return os.path.join(
        os.path.dirname(os.path.abspath(__file__)), 'icons', 'rstudio.svg'
    )

def setup_rserver():

    def _get_env(port):
        return dict(USER=getpass.getuser())

    def _get_cmd(port):
        cookie_file = tempfile.NamedTemporaryFile()
        # use mkdtemp() so the directory and its contents do not
        # vanish when we are out of scope
        database_directory = tempfile.mkdtemp()
        database_configuration = dedent("""
            provider=sqlite
            directory={directory}
        """).format(directory=database_directory)
        database_configuration_file = tempfile.NamedTemporaryFile(
            mode='w', delete=False, dir=database_directory)
        database_configuration_file.write(database_configuration)
        database_configuration_file.close()

        cmd = [
            get_rstudio_executable('rserver'),
            '--auth-none=1',
            '--www-frame-origin=same',
            '--www-port=' + str(port),
            '--www-verify-user-agent=0',
            '--secure-cookie-key-file=' + cookie_file.name,
            '--server-user=' + getpass.getuser(),
            '--www-root-path={base_url}rstudio/',
            f'--database-config-file={database_configuration_file.name}',
            f'--server-data-dir={database_directory}',
        ]

        return cmd

    server_process = {
        'command': _get_cmd,
        'environment': _get_env,
        'rewrite_response': rewrite_response,
        'launcher_entry': {
            'title': 'RStudio',
            'icon_path': get_icon_path(),
            # path_info does not fix the damaged location header
            # 'path_info': 'rstudio/auth-sign-in'
        }
    }
    return server_process

just install https://download2.rstudio.org/server/centos8/x86_64/rstudio-server-rhel-2021.09.1-372-x86_64.rpm

ccoulombe commented 2 years ago

See #119 for the fix

ryanlovett commented 2 years ago

Closing in favor of #119.

I think https://github.com/jupyterhub/jupyter-rsession-proxy/pull/118#issuecomment-985913085 might be need to be filed as a separate issue though.