jupyterhub / jupyterhub

Multi-user server for Jupyter notebooks
https://jupyterhub.readthedocs.io
Other
7.78k stars 2.01k forks source link

JupyterHub on subpath behind apache2 reverse proxy #1716

Closed tchakravarty closed 6 years ago

tchakravarty commented 6 years ago

The JupyterHub documentation shows how to reverse proxy JH on the root path (/). However, I would like to access JH at xxx.yyy.com/jupyter, that is, on the subpath jupyter.

Here is the relevant block in my 000-default.conf. Note that there is no JH config file that is being used, i.e., default configuration.

<VirtualHost *:443>
  SSLEngine on
  SSLCertificateFile /path/to/certs/MaimKey.crt
  SSLCertificateKeyFile /path/to/certs/decrypted-private-key.pem
  SSLCertificateChainFile /path/to/certs/Chain.crt

  <Proxy *>
    Allow from localhost
  </Proxy>
        <Location />
                AuthType Basic
                AuthName "Restrcted Access - Authenticate"
                AuthUserFile /etc/httpd/htpasswd.users
                Require valid-user
        </Location>

  ProxyPassMatch ^/(.+)/websocket ws://localhost:8787/$1/websocket
  ProxyPass / http://localhost:8787/
  ProxyPassReverse / http://localhost:8787/

   ServerName xxx.yyy.com

  <Location "/jupyter">
    # preserve Host header to avoid cross-origin problems
    ProxyPreserveHost on
    # proxy to JupyterHub
    ProxyPass         http://localhost:8000/
    ProxyPassReverse  http://localhost:8000/
  </Location>

  # Use RewriteEngine to handle websocket connection upgrades
  RewriteEngine On
  RewriteCond %{HTTP:Connection} Upgrade [NC]
  RewriteCond %{HTTP:Upgrade} websocket [NC]
  RewriteRule /(.*) ws://localhost:8000/$1 [P,L]
</VirtualHost>

Now when I go to xxx.yyy.com/jupyter it redirects to xxx.yyy.com/hub and I get the message:

/hub not found

Any help with this configuration would be appreciated. I am aware of similar questions here, here and here, but none of them appear to resolve this issue, specifically for apache & JupyterHub.

betatim commented 6 years ago

I think you will have to configure the base_url property of JupyterHub to match you subpath: https://github.com/jupyterhub/jupyterhub/blob/a58bea6d933b7bc0bcfe4b08dfff4f31dd2b5909/jupyterhub/app.py#L347-L352

willingc commented 6 years ago

Closing this issue since it appears to be resolved. Please feel free, if this is still an issue, to leave a comment requesting this issue be reopened. Thanks to all!

bellackn commented 6 years ago

I'd like to re-discuss this issue, as I'm having similar troubles. I want to deploy JupyterHub (in a Docker container) with Dockerspawner behind an SSL handling Apache Reverse Proxy (also in a Docker container) on the subpath /jupyter. The users shall log in via Keycloak (also containerized), which is running on the same host on the subpath /. The JupyterHub is spawning the notebook containers as "siblings".

My problem right now is that the notebook container does not bind to the JupyterHub. I can log in successfully at https://localhost/jupyter/hub/login, but I always run into a redirect loop afterwards. This is from the JupyterHub container's log:

[I 2018-08-09 14:06:18.111 JupyterHub base:499] User logged in: demo1
[I 2018-08-09 14:06:18.112 JupyterHub log:158] 302 GET /jupyter/hub/oauth_callback?state=[secret]&session_state=[secret]&code=[secret] -> /jupyter/user/demo1/ (@::ffff:192.168.80.3) 65.41ms
14:06:18.119 - debug: [ConfigProxy] PROXY WEB /jupyter//user/demo1/ to http://jupyterhub:8081
[I 2018-08-09 14:06:18.123 JupyterHub log:158] 302 GET /jupyter/user/demo1/ -> /jupyter/hub/user/demo1/ (@::ffff:192.168.80.3) 0.78ms
14:06:18.145 - debug: [ConfigProxy] PROXY WEB /jupyter//hub/user/demo1/ to http://jupyterhub:8081
[I 2018-08-09 14:06:18.204 JupyterHub dockerspawner:452] Container 'jupyter-demo1' is gone
[I 2018-08-09 14:06:18.290 JupyterHub dockerspawner:540] Created container 'jupyter-demo1' (id: ff10fe4) from image jupyter/datascience-notebook:latest
[I 2018-08-09 14:06:18.291 JupyterHub dockerspawner:557] Starting container 'jupyter-demo1' (id: ff10fe4)
[I 2018-08-09 14:06:19.774 JupyterHub log:158] 200 GET /jupyter/hub/api (@192.168.80.6) 0.65ms
[I 2018-08-09 14:06:20.171 JupyterHub base:628] User demo1 took 2.004 seconds to start
[I 2018-08-09 14:06:20.171 JupyterHub proxy:242] Adding user demo1 to proxy /jupyter/user/demo1/ => http://192.168.80.6:8888
14:06:20.177 - info: [ConfigProxy] Adding route /jupyter/user/demo1 -> http://192.168.80.6:8888
14:06:20.179 - info: [ConfigProxy] 201 POST /api/routes/jupyter/user/demo1
[I 2018-08-09 14:06:20.186 JupyterHub log:158] 302 GET /jupyter/hub/user/demo1/ -> /jupyter/user/demo1/?redirects=1 (demo1@::ffff:192.168.80.3) 2037.98ms
14:06:20.211 - debug: [ConfigProxy] PROXY WEB /jupyter//user/demo1/?redirects=1 to http://jupyterhub:8081
[I 2018-08-09 14:06:20.220 JupyterHub log:158] 302 GET /jupyter/user/demo1/?redirects=1 -> /jupyter/hub/user/demo1/?redirects=1 (@::ffff:192.168.80.3) 2.57ms
14:06:20.245 - debug: [ConfigProxy] PROXY WEB /jupyter//hub/user/demo1/?redirects=1 to http://jupyterhub:8081
[W 2018-08-09 14:06:20.282 JupyterHub base:1061] Redirect loop detected on /jupyter/hub/user/demo1/?redirects=1

[...and so on.]

This is my Apache config:

SSLRandomSeed startup file:/dev/urandom 512
SSLRandomSeed connect file:/dev/urandom 512

SSLCipherSuite HIGH:MEDIUM:!SSLv3:!kRSA
SSLProxyCipherSuite HIGH:MEDIUM:!SSLv3:!kRSA

SSLHonorCipherOrder on

SSLProtocol all -SSLv2 -SSLv3
SSLProxyProtocol all -SSLv2 -SSLv3

SSLPassPhraseDialog  builtin

SSLSessionCache        "shmcb:/usr/local/apache2/logs/ssl_scache(512000)"
SSLSessionCacheTimeout  300

SSLUseStapling On
SSLStaplingCache "shmcb:/usr/local/apache2/logs/ssl_stapling(32768)"
SSLStaplingStandardCacheTimeout 3600
SSLStaplingErrorCacheTimeout 600

Listen 443

<VirtualHost _default_:443>
    ServerName ${SERVER_NAME}
    ServerAdmin ${SERVER_ADMIN}

    ErrorLog "/usr/local/apache2/logs/error_log"
    TransferLog "/usr/local/apache2/logs/access_log"
    CustomLog /proc/self/fd/1 \
          "%t %h %{SSL_PROTOCOL}x %{SSL_CIPHER}x \"%r\" %b"

    SSLEngine on
    SSLCertificateFile "/usr/local/apache2/certs/apache-selfsigned.crt"
    SSLCertificateKeyFile "/usr/local/apache2/certs/apache-selfsigned.key"

    <FilesMatch "\.(cgi|shtml|phtml|php)$">
        SSLOptions +StdEnvVars
    </FilesMatch>

    <Directory "/usr/local/apache2/cgi-bin">
      SSLOptions +StdEnvVars
    </Directory>

    BrowserMatch "MSIE [2-5]" \
         nokeepalive ssl-unclean-shutdown \
         downgrade-1.0 force-response-1.0

    <Location />
        ProxyPreserveHost On
        RequestHeader set X-Forwarded-Proto "https"
        RequestHeader set X-Forwarded-Port "443"
        ProxyPass http://keycloak:8080/
        ProxyPassReverse http://keycloak:8080/
    </Location>

    RewriteEngine On
    RewriteCond %{REQUEST_URI} ^/api/v1/websocket [NC,OR]
    RewriteCond %{HTTP:UPGRADE} ^WebSocket$ [NC,OR]
    RewriteCond %{HTTP:CONNECTION} ^Upgrade$ [NC]
    RewriteRule /(.*) ws://jupyterhub:8000/jupyter/%{REQUEST_URI} [P,L]

    <Location /jupyter>
        ProxyPreserveHost On
        ProxyPass http://jupyterhub:8000/jupyter/
        ProxyPassReverse http://jupyterhub:8000/jupyter/
    </Location>      

</VirtualHost>

... and here is the relevant part of my jupyterhub_config.py:

import os
from oauthenticator.generic import GenericOAuthenticator
from dockerspawner import DockerSpawner

## DEBUGGING
c.Spawner.debug = True
c.ConfigurableHTTPProxy.debug = True
#

c.JupyterHub.base_url = "/jupyter"
c.JupyterHub.port = 8000
c.JupyterHub.hub_ip = "0.0.0.0"
c.JupyterHub.hub_connect_ip = "jupyterhub"

c.JupyterHub.authenticator_class = GenericOAuthenticator
c.OAuthenticator.client_id = 'jupyterhub'
c.OAuthenticator.client_secret = str(os.environ.get('KEYCLOAK_CLIENT_SECRET'))
c.GenericOAuthenticator.token_url = str(os.environ.get('OAUTH2_TOKEN_URL'))
c.GenericOAuthenticator.userdata_url = str(os.environ.get('BASE_URL')) + '/userinfo'
c.GenericOAuthenticator.userdata_method = 'GET'
c.GenericOAuthenticator.userdata_params = {"state": "state"}
c.GenericOAuthenticator.username_key = "preferred_username"
c.GenericOAuthenticator.scope = ['openid']

c.JupyterHub.spawner_class = DockerSpawner
c.DockerSpawner.image = 'jupyter/datascience-notebook:latest'
c.DockerSpawner.network_name = "jh_keycloak_default"
c.DockerSpawner.remove_containers = True

The rest in there is only for mounting volumes and starting services, which does work. I tried setting c.JupyterHub.ip = '127.0.0.1', but then my browser could not find it anymore. I also tried to set it to the Docker container's IP, but then the redirection loop struck again.

I've been trying to solve this for several days and I'm out of ideas by now. Also, like @tchakravarty, I am aware of all the similar questions here and there, but none of the provided solutions worked for me yet.

Maybe someone has got a hint for me where to start?

ghost commented 6 years ago

Hi anyone,

I just want to share my configurations that worked for me, to launch the JupyterHub through this pattern of url: http://your_ip_domainname/jhub/

Note: I am also running a Shiny-Server Open Source on the same CentOS server;

httpd.conf:

<Proxy *>
 Allow from localhost
 </Proxy>

<VirtualHost *:80>

 RewriteEngine on

 #JupyterHub
 RewriteCond %{HTTP:Connection} Upgrade [NC]
 RewriteCond %{HTTP:Upgrade} websocket [NC]
 RewriteRule /jhub/(.*) ws://127.0.0.1:8000/jhub/$1 [P,L]
 RewriteRule /jhub/(.*) http://127.0.0.1:8000/jhub/$1 [P,L]

 #Preserve Host header to avoid cross-origin problems
 ProxyPreserveHost On
 #proxy to JupyterHub
 ProxyPass /jhub/ http://127.0.0.1:8000/jhub/
 ProxyPassReverse /jhub/  http://127.0.0.1:8000/jhub/

#ServerName sappsveeam.com
 #DocumentRoot /srv/shiny-server/sample-apps/hello

  #ShinyServer
 RewriteCond %{HTTP:Upgrade} =websocket
 RewriteRule /shiny/(.*) ws://127.0.0.1:3838/$1 [P,L]

 RewriteCond %{HTTP:Upgrade} !=websocket
 RewriteRule /shiny/(.*) http://127.0.0.1:3838/$1 [P,L]

 ProxyPass /shiny/ http://127.0.0.1:3838/
 ProxyPassReverse /shiny/ http://127.0.0.1:3838/

     TimeOut 600

   </VirtualHost>

jupyterhub_config.py --The public facing URL of the whole JupyterHub application.
--This is the address on which the proxy will bind. Sets protocol, ip, base_url --c.JupyterHub.bind_url = 'http://:8000' c.JupyterHub.bind_url = 'http://127.0.0.1:8000/jhub/'

Hope it is helpful.

willingc commented 6 years ago

Thanks @DataVictorEngineer!

bellackn commented 6 years ago

Thank you @DataVictorEngineer. As it turns out, I just messed things up with Docker networking. The basic configuration also works for me.

andreas-h commented 5 years ago

This is very valuable information, @DataVictorEngineer! I believe this should be somewhere in the JupyterHub docs, probably at jupyterhub/jupyterhub/docs/source/reference/config-proxy.md. I'm not affiliated to this project, but maybe @willingc has an opinion?

willingc commented 5 years ago

@andreas-h Pull requests are very much welcome. I think the referenced page would be a good location too 👍

ghost commented 5 years ago

@andreas-h and @willingc the modifications are merged to the documentation: https://github.com/jupyterhub/jupyterhub/blob/master/docs/source/reference/config-proxy.md

Thanks for pointing out @andreas-h !

candu commented 5 years ago

Anyone have an equivalent nginx config working? Currently wrestling with this same issue, but in an nginx-based setup - tried the nginx instructions, and then attempted to:

This results in a bunch of 404s for static resources.

candu commented 5 years ago

...found that rewrite /jupyter/(.*) /jupyter/$1 break; works.

nstickney commented 4 years ago

@candu would you mind sharing your full nginx config?

aaszodi commented 4 years ago

@DataVictorEngineer : Thank you very much, you set me on the right path. My task was to proxy JupyterHub and Rstudio from http://my.domain/jhub and http://my.domain/rstudio, respectively. It turned out that JupyterHub requires the ProxyPreserveHost On directive, which, alas, screws up Rstudio. The solution was to enclose the JupyterHub-relevant proxy settings in a <Location ...> "directory", as shown below:

  <Proxy *>
    Allow from localhost
  </Proxy>
  RewriteEngine on
  ### JupyterHub
  # add trailing slash
  RedirectMatch permanent ^/jhub$ /jhub/
  RewriteCond %{HTTP:Connection} Upgrade [NC]
  RewriteCond %{HTTP:Upgrade} websocket [NC]
  RewriteRule /jhub/(.*) ws://127.0.0.1:8000/jhub/$1 [P,L]
  RewriteRule /jhub/(.*) http://127.0.0.1:8000/jhub/$1 [P,L]

  <Location /jhub/>
  # preserve Host header to avoid cross-origin problems
  # this setting screws up Rstudio, use for JupyterHub only
  ProxyPreserveHost On
  # proxy to JupyterHub, omit location parameter from ProxyPass etc
  ProxyPass  http://127.0.0.1:8000/jhub/
  ProxyPassReverse   http://127.0.0.1:8000/jhub/ 
  </Location>

  ### Rstudio Server Proxy
  # add trailing slash
  RedirectMatch permanent ^/rstudio$ /rstudio/
  # if websocket, then ws://localhost...
  RewriteCond %{HTTP:Upgrade} =websocket
  RewriteRule /rstudio/(.*)     ws://127.0.0.1:8787/$1  [P,L]
  # if not websocket, then http://localhost...
  RewriteCond %{HTTP:Upgrade} !=websocket
  RewriteRule /rstudio/(.*)     http://127.0.0.1:8787/$1 [P,L]
  # proxy to Rstudio
  ProxyPass /rstudio/ http://127.0.0.1:8787/
  ProxyPassReverse /rstudio/ http://127.0.0.1:8787/

This cost me 2 hours of my life, but it works now.

candu commented 4 years ago

for @nstickney and anyone else who's curious, here's the full nginx config:

map $http_upgrade $connection_upgrade {
    default upgrade;
    ''      close;
}

server {
        # ports, server name, SSL certs, etc. go here

        location /jupyter/ {
                rewrite /jupyter/(.*) /jupyter/$1 break;
                proxy_pass http://localhost:8086;
                proxy_set_header X-Real-IP $remote_addr;
                proxy_set_header Host $host;
                proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

                # websocket headers
                proxy_set_header Upgrade $http_upgrade;
                proxy_set_header Connection $connection_upgrade;
        }
}
chathuriw commented 3 years ago

Anyone able to do this with Jupyterhub version 1.3. I have jupyterhub deployed in kubernetes and want to reverse proxy through apache.

Apache config RewriteEngine On RewriteCond %{HTTP:Connection} Upgrade [NC] RewriteCond %{HTTP:Upgrade} websocket [NC] RewriteRule /jupyter/(.) ws://.elb.amazonaws.com/jupyter/$1 [P,L] RewriteRule /jupyter/(.) http://.elb.amazonaws.com/jupyter/$1 [P,L] ProxyPass /jupyter/ http://..elb.amazonaws.com/jupyter/ ProxyPassReverse /jupyter/ http:/.elb.amazonaws.com/jupyter/

Jupyterhub config.yaml

hub: cookieSecret: "e83c19ff895bf91adea7bf74b05120e9d18cbcd2e32c733436129e2d8b2b7cf2" service: loadBalancerIP: 10.0.1.226 extraConfig: first-config: |# some code c.JupyterHub.allow_named_servers = True c.JupyterHub.bind_url = 'http://.elb.amazonaws.com/jupyter/'

For some reason it always redirects to /hub/jupyterhub/...

Logs from hub pod

[I 2021-01-27 22:08:54.552 JupyterHub log:181] 302 GET /jupyter/user/mnygk3djnnqw4/lab/?token=[secret] -> /hub/jupyter/user/mnygk3djnnqw4/lab/?token=[secret] (@::ffff:100.126.0.0) 0.94ms [I 2021-01-27 22:08:54.606 JupyterHub log:181] 302 GET /jupyter/user/mnygk3djnnqw4/lab/?token=[secret] -> /hub/jupyter/user/mnygk3djnnqw4/lab/?token=[secret] (@::ffff:100.126.0.0) 0.83ms [I 2021-01-27 22:08:54.698 JupyterHub log:181] 302 GET /jupyter/user/mnygk3djnnqw4/lab/?token=[secret] -> /hub/jupyter/user/mnygk3djnnqw4/lab/?token=[secret] (@::ffff:100.126.0.0) 0.89ms [I 2021-01-27 22:08:54.743 JupyterHub log:181] 302 GET /jupyter/user/mnygk3djnnqw4/lab/?token=[secret] -> /hub/jupyter/user/mnygk3djnnqw4/lab/?token=[secret] (@::ffff:100.126.0.0) 0.77ms