binderhub icon indicating copy to clipboard operation
binderhub copied to clipboard

Supporting arbitrary web applications without Jupyter server present

Open yuvipanda opened this issue 7 years ago • 14 comments

One of our goals is to have first class support for non-jupyter applications on Binder (such as RStudio, arbitrary command execution ala ReproServer, etc).

Currently, the only things that binderhub expects the process started inside to do are:

  1. Accept a token and enforce using that (via a cookie, header or query param) for authentication

From prior discussion, the simplest way to allow arbitrary execution seems to be to build a standalone no-deps simple application that does the following:

  1. Authentication with token + proxying (including websockets) to any other process
  2. Process supervision of the backend process, so it gets restarted as appropriate

This program should be completely statically linked, and then dropped into the image as a final step. This unfortunately probably means we can't use Python (unless cx_freeze exceeds all my expectations), and need to write this in Go or Rust.

Once this program exists, we can use it for supporting arbitrary web backends without having to impose too many requirements on the environment that is present inside the container image.

yuvipanda avatar Nov 10 '17 04:11 yuvipanda

We sortof already do this with a combination of the nbserverproxy + nbrsessionproxy for rstudio, and that mostly works when you have Jupyter already installed. However, I think to really be inclusive of non-Jupyter & non-Python users, we need to not have Jupyter present inside as a requirement.

yuvipanda avatar Nov 10 '17 04:11 yuvipanda

From the binderhub side, this would mean that we support the following:

  1. Customizing what 'cmd' is run inside the container by the API
  2. Formalizing how we pass in the token to the container as a stable API
  3. Formalizing how we expect the container process to use the token as a stable API

Currently all these three are jupyter specific.

yuvipanda avatar Nov 10 '17 04:11 yuvipanda

Supporting arbitrary web applications

I picked that instead of your less broad first line of the first comment because it is more radical ;) My question is: is this really our goal? For me this sounds like heroku and friends, which already exists. My first reaction is that binderhub is about "telling stories with data", "data narratives", and "encouraging reuse and exploration". For me this implies a notebook like interface. Something that encourages me to poke around inside and encourages the creator to not add so many layers that it is a nice and polished product which makes it harder to explore around and reuse it in a different project.

From a technical point of view a solid and robust way to launch things like RStudio and co that doesn't rely on too many "hacks" because we started with jupyter notebooks is something I welcome.

Maybe this is just a question of wording but I felt the urge to tell you :)

betatim avatar Nov 10 '17 08:11 betatim

It also doesn't have to be in go/rust because we could use a multi-container pod where the auth proxy is in a different container, right? Is there a reason the proxy must be in the container with the target application?

I'm all for using go/rust, as long as there is already a proxy that properly supports http + websockets without being told where those will be. I don't think developing and supporting our own proxy in go/rust ought to be within scope for this project, but adding simple auth to an existing one should be quite doable. So the first step, for me, is identifying the landscape of extensible proxy libraries in go and/or rust.

minrk avatar Nov 10 '17 15:11 minrk

@betatim perhaps 'arbitrary interactive web applications that are traditionally single-user'? I don't think we'd be heroku, since our model is:

  1. Autobuild some container image I
  2. Launch image I in pod P
  3. Allow authenticated access for the individual user who initiated process to pod P

While heroku's model is more like:

  1. Autobuild some container image I
  2. Launch image I in one or many pods P
  3. Allow arbitrary access to everyone for the things in pods P at a stable URL

IMO we should make sure that 'our model' does not require Jupyter to exist in the image I, and that's in scope if we want to consider RStudio and future similar projects (Eclipse Che maybe?! plain Terminado?) as first class citizens.

yuvipanda avatar Nov 10 '17 17:11 yuvipanda

@minrk after listening to you and @remram44 talk about this I'm now convinced that having a sidecar container with a proxy is the right thing to do.

i definitely do not want us to write any new code if possible, and splitting the proxy and process supervision parts lets us do that. It also means we are not actually restricted to single binary proxies, which brings nginx back into play...

yuvipanda avatar Nov 10 '17 17:11 yuvipanda

Given that we're talking about simple token auth, an nginx proxy seems like it would be a pretty good fit as a side-car, and not too complicated since it's not conferring with an external service.

We may want to consider whether activity-tracking should be part of this API, as well (either in this proxy pod, or in the user application), as we are moving away from tracking at the CHP level.

Step one is multi-container pod spec support in KubeSpawner, I think?

minrk avatar Nov 10 '17 20:11 minrk

Currently, the only things that binderhub expects the process started inside to do are:

  1. Accept a token and enforce using that (via a cookie, header or query param) for authentication

Could this be set as an environmental variable instead of passed as an arg? I assume at least the port would also have to passed in.

Setting environmental variables to dictate how the container should behave seems to the most explicit and flexible way solution, since it doesn't assume anything about the image.

What is the rationale for requiring it to launch jupyter notebook?

saulshanabrook avatar Feb 18 '19 14:02 saulshanabrook

Wouldn't it make sense for this to be part of the proxy instead?

remram44 avatar Feb 18 '19 15:02 remram44

@remram44 if you do auth at the proxy level, it leaves services exposed to attacks from localhost (inside the container), and from the network if you can bypass the proxy (if you are another user in the same network). Defense in depth, etc.

yuvipanda avatar Feb 18 '19 23:02 yuvipanda

@saulshanabrook

What is the rationale for requiring it to launch jupyter notebook?

Something needs to do the authentication from inside the container rather than at the proxy. Currently this is the Jupyter Notebook, since that is where we already have the code. However, I'd love for us to write a single binary Rust / Go thing that can do the auth + proxying instead. We can also use a sidecar approach instead, but that would only work for Kubernetes and be non-generalizable.

yuvipanda avatar Feb 18 '19 23:02 yuvipanda

http://jupyter-server-proxy.readthedocs.io/ does all the things I want it to now, but would be great to not need it in the future.

yuvipanda avatar Feb 18 '19 23:02 yuvipanda

Something needs to do the authentication from inside the container rather than at the proxy. Currently this is the Jupyter Notebook, since that is where we already have the code.

Ah I see! Sorry I was totally misunderstanding the problem here. Thanks!

saulshanabrook avatar Feb 18 '19 23:02 saulshanabrook

Does https://github.com/ideonate/jhsingle-native-proxy cover this use-case? https://discourse.jupyter.org/t/new-package-to-run-arbitrary-web-service-in-jupyterhub-jhsingle-native-proxy/3493

manics avatar Aug 24 '21 16:08 manics

I think jhsingle-native-proxy and jupyter-server-proxy together cover this use case now.

yuvipanda avatar Oct 03 '23 03:10 yuvipanda