Closed stefanmeili closed 4 years ago
Actually I think what I want is a 'reverse proxy'.
I have seemingly the same issue: my dashboard runs fine when accessed locally or directly using the ip address of the server, but when accessing through a reverse proxy server (nginx) the dashboard runs but the plots do never appear, only the markdown cells in the notebook
I've managed to get this to work - I found you need two proxy_pass locations in the NGINX .conf file for your project.
location /backend/ {
proxy_pass http://192.168.XXX.XXX:8866;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_read_timeout 86400;
}
location ~* /backend(api/kernels/[^/]+/(channels|iopub|shell|stdin)|terminals/websocket)/? {
proxy_pass http://192.168.XXX.XXX:8866;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_read_timeout 86400;
}
Here's the voila.json file I have in my dashboard folder
{
"Voila": {
"open_browser": false,
"port": 8866,
"base_url": "/backend/my_dashboard/",
"notebook_path": "Some Amazing Dashboard.ipynb",
"static_root": "/home/user/dashboards/my_dashboard/voila/static/"
},
"VoilaConfiguration": {
"file_whitelist": [".*\\.(html|xlsx)"]
},
"MappingKernelManager": {
"cull_idle_timeout": 1200,
"cull_interval": 120
}
}
and I also copied the contents of /home/username/miniconda3/pkgs/voila-0.1.21-py_0/share/jupyter/voila/templates/default/static to /home/user/dashboards/my_dashboard/voila/static/
This works in that I can serve the dashboard though a reverse proxy, but it doesn't allow me to restrict access to specific users. I'm still trying to work out a solution with requests that I can do entirely through my flask webpage.
@stefanmeili Thanks for sharing your solution about the nginx config.
Do you think we could add more information about this to the documentation? https://voila.readthedocs.io/en/latest/deploy.html#running-voila-on-a-private-server
This works in that I can serve the dashboard though a reverse proxy, but it doesn't allow me to restrict access to specific users. I'm still trying to work out a solution with requests that I can do entirely through my flask webpage.
Is the authentication happening on the flask server? Just thinking out loud whether a JupyterHub instance with the TLJH gallery plugin could be an alternative?
Hi @jtpio,
That looks complete. Did you just add that? I swear I read through that page 8 times. I thought the second location block was required based on https://github.com/jupyter/notebook/issues/1311, but I just tried commenting it out now and it works. It also looks like I don't need to copy the static content folder or add the "static_root" line in the voila.json, so the second half of my last comment can be safely ignored.
Yeah authentication is on the flask server. I want access to my dashboards to be user / account specific with one login / pass in one database through a common page and with a consistent look. Thanks for the suggestion, I'll take a look.
Maybe it'll just be cleaner to set up an authentication server and set up NGINX like this: https://www.ritchievink.com/blog/2019/03/17/save-some-time-embedding-jupyter-notebook-in-an-iframe-and-serve-as-a-reverse-proxy-behind-nginx/
That looks complete. Did you just add that?
It was added in https://github.com/voila-dashboards/voila/pull/357. Maybe we should improve the docs to make it more visible?
hmm.... searching google for documentation on voila points at https://voila.readthedocs.io/en/stable/deploy.html which is pretty bare bones. I think that explains why I didn't see it.
Right, we should maybe make latest
the default in the docs (the link in the README points to latest).
For those who don't have access to the reverse proxy settings, here's a way to allow requests from all origins.
{
"Voila": {
"tornado_settings": {
"allow_origin": "*"
}
}
}
The check actually happens at https://github.com/jupyter/jupyter_server/blob/master/jupyter_server/base/handlers.py#L278
A little trick for people who want to reverse proxy several Voilà applications (each one using a specific port) running at the same time. The idea is to write the port in the "base_url" option, catch it using a regular expression in NGINX configuration file, and redirect to that port.
voila --port=9999 --no-browser --Voila.base_url=/backend/my_dashboard_9999 notebook.ipynb
Then in NGINX configuration file :
location ~ ^/backend/my_dashboard_([0-9]*) {
proxy_pass http://192.168.XXX.XXX:$1;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_read_timeout 86400;
}
@stefanmeili did you ever solve your Flask authentication issue? We are trying to do the same thing.
Hi @GregSilverman,
Yes, I did get it up and running. I believe the key was this blogpost here: https://www.ritchievink.com/blog/2019/03/17/save-some-time-embedding-jupyter-notebook-in-an-iframe-and-serve-as-a-reverse-proxy-behind-nginx/
@stefanmeili , do you have your full app in a repo somewhere?
Thanks in advance!
@GregSilverman Sorry, not in a public one. Where are you getting stuck?
@stefanmeili Not stuck, was just curious. It does seem rather straightforward.
A little trick for people who want to reverse proxy several Voilà applications (each one using a specific port) running at the same time. The idea is to write the port in the "base_url" option, catch it using a regular expression in NGINX configuration file, and redirect to that port.
voila --port=9999 --no-browser --Voila.base_url=/backend/my_dashboard_9999 notebook.ipynb
Then in NGINX configuration file :
location ~ ^/backend/my_dashboard_([0-9]*) { proxy_pass http://192.168.XXX.XXX:$1; proxy_set_header X-Real-IP $remote_addr; proxy_set_header Host $host; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; proxy_read_timeout 86400; }
@brichet I'm wondering if you have anything else in the Nginx config file for this? As in, do you also need a location /backend/
directive similar to the example provided by @stefanmeili ?
I tried both of these solutions with no luck. I'm trying to put my voila on Nginx in order to restrict IP traffic to it. So far I have been able to hit the kernel (I see [Voila] Kernel started:
in the logs) but only see a spinning wheel and then a blank screen.
Was able to figure it out. I had to use 0.0.0.0
for proxy_pass
instead of the IP. I was also able to serve 2 different voila instances on the same server with the following setup.
nginx config:
location /backend1/ {
proxy_pass http://0.0.0.0:8866;
proxy_set_header Connection "upgrade";
proxy_http_version 1.1;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $host:$server_port;
proxy_buffering off;
}
location /backend2/ {
proxy_pass http://0.0.0.0:8877;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_http_version 1.1;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $host:$server_port;
proxy_buffering off;
}
voila.json
for each instance in the same directory as the notebook:
{
"VoilaConfiguration": {
"file_whitelist": [".*"]
}
}
voila command:
voila --port=<port> --no-browser --MappingKernelManager.cull_interval=60 --MappingKernelManager.cull_idle_timeout=120 --Voila.tornado_settings 'allow_origin'='*' --Voila.base_ url=/<location>/ lens_selector.ipynb
Just be sure to match your port and your base_url location to your nginx config.
@jmurray6 it should also work with proxy_pass http://127.0.0.1:8866
.
To use the IP address I think it must be bind with the --Voila.ip
option, I didn't mention it.
Now if you want to serve more than 2 applications, you can write the port in the --Voila.base_url
option and catch it using regular expression in the nginx location section.
@stefanmeili , we got it working, but are running into a strange problem. The src
parameter in the iframe is sometimes empty when it passes the para from thee view to the html template. This works without issee in a development environment, but it's sporadic in production. If I keep doing a hard reset at the browser it eventually ends up passing the src url and rendering as expected.
Not sure what is going on? No errors are thrown in nginx, the api or voila, or in the web browser console? It's just sporadically dropping the param value for the iframe src.
Sorry if this isn't an appropriate forum for this question.
I'm trying put a user authentication page between my voila dashboard and the world. I've created a webpage using flask and am trying to use requests-html as a proxy to serve the dashboard which is behind a firewall (well my router actually).
Here's what I'm trying to do in broad strokes:
when I access the /dashboard route, the voila server spins up an instance, but the content is blocked and I get a blank page with only the 'Voila: dashboard name" title.
the flask development server spits out the following to terminal:
It looks like some static content is either blocked or can't be found. Any suggestions?