Сервер Bokeh с https через nginx, gunicorn и флешку - PullRequest
0 голосов
/ 29 июня 2018

У меня проблемы с тем, чтобы сервер bokeh отображал визуализацию при использовании https. У меня есть подобный стек, где сервер bokeh работает как положено с http. Кроме того, я могу получить соединение https для статического файла bokeh.html. Однако передача тега script в мой template.html не работает с https.

У меня nginx в качестве обратного прокси-сервера перед Gunicorn, на котором запущено приложение для фляги. Приложение фляги извлекает сеанс с работающего сервера bokeh на конкретном порту. Процесс сервера bokeh и процесс gunicorn / flask контролируются супервизором.

Я просмотрел существующую документацию по боке:

https://bokeh.pydata.org/en/latest/docs/user_guide/server.html#reverse-proxying-with-nginx-and-ssl

Я также просмотрел некоторые похожие вопросы здесь на SO:

Как настроить Nginx с подачей Gunicorn и Bokeh

Боке Служите HTTPS вместо HTTP

Как включить SSL / HTTPS на боке 0.12.5?

Обеспечение безопасности содержимого сценария для отображения HTTPS (Bokeh)

Я пробовал сочетания опций дляlative_urls = True / False в приложении фляги и --allow-websocket-origin = "myIP" / ["*"] при раскрутке сервера bokeh.

В этот момент я думаю, что мне просто не хватает чего-то очевидного, и я надеюсь, что кто-то еще увидит это. Я думаю, что ключевым моментом является то, что https работает вверх и вниз по стеку, когда обслуживает статический файл bokeh.html или другой контент, но терпит неудачу при попытке встроить сервер bokeh. Я подозреваю, что мне чего-то не хватает, как заставить колбу вытащить сеанс.

Заранее спасибо!

Ниже приведены все соответствующие разделы конфигурации и файлы flask.app:

Содержимое: / etc / nginx / sites-available / flask_settings

upstream gunicorn_flask {
    server 127.0.0.1:6000 fail_timeout=0;
}

server {
    listen 443 ssl http2 default_server;
    server_name ###.###.###.###;
    include snippets/self-signed.conf;
    include snippets/ssl-params.conf;
    access_log /var/log/nginx/https_access.log;
    error_log /var/log/nginx/https_error.log;
    ssl on;
    location / {
                proxy_pass http://gunicorn_flask;
                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_buffering off;
#                proxy_set_header X-Real-IP $remote_addr;
#                proxy_set_header Host $host:$server_port;

               }
}

server {
    listen 80;
    server_name ###.###.###.###;
    return 301 https://$server_name$request_uri;
}

Содержимое: /etc/nginx/snippets/self-signed.conf

ssl_certificate /path/to/my/file.crt;
ssl_certificate_key /path/to/my/file.key;

Содержимое: /etc/nginx/snippets/ssl-params.conf

ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_prefer_server_ciphers on;
ssl_ciphers "ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-SHA:ECDHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA256:DHE-RSA-AES128-SHA256:DHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA:ECDHE-RSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:AES256-GCM-SHA384:AES128-GCM-SHA256:AES256-SHA256:AES128-SHA256:AES256-SHA:AES128-SHA:DES-CBC3-SHA:HIGH:!aNULL:!eNULL:!EXPORT:!DES:!MD5:!PSK:!RC4";
ssl_ecdh_curve secp384r1;
ssl_session_cache shared:SSL:10m;
ssl_session_tickets off;
ssl_stapling on;
ssl_stapling_verify on;
resolver 8.8.8.8 8.8.4.4 valid=300s;
resolver_timeout 5s;
add_header Strict-Transport-Security "max-age=63072000";
ssl_dhparam /etc/ssl/certs/dhparam.pem;

Содержимое: /etc/supervisor/conf.d/bokeh.conf

[program:my_bokeh_viz]
directory=/my/path/to/bokeh/viz
command=/my/path/to/bokeh/bin/bokeh serve --allow-websocket-origin=###.###.###.### --address=127.0.0.1 --port 6001 --use-xheaders /my/path/to/bokeh/viz.py
autostart=true
autorestart=true
stderr_logfile=/var/log/my_bokeh_viz/Bokeh_Flask.err.log
stdout_logfile=/var/log/my_bokeh_viz/Bokeh_Flask.out.log

Содержимое: /etc/supervisor/conf.d/gunicorn.conf

[program:gunicorn_bokeh]
directory=/my/path/to/flask/app
command=/my/path/to/anaconda3/bin/gunicorn my_flask_app:app --bind 127.0.0.1:6000 --pythonpath /my/path/to/flask/app
autostart=true
autorestart=true
stderr_logfile=/var/log/gunicorn_bokeh/Bokeh_Flask.err.log
stdout_logfile=/var/log/gunicorn_bokeh/Bokeh_Flask.out.log

Содержимое: /path/to/my/flask/app.py

--- Relevant section of flask app.py
@app.route('/web/path/to/bokeh/viz', methods = ['GET'])
@login_required
def show_bokeh_viz():

  host_url = 'http://127.0.0.1:6001/my_bokeh_viz'
  my_session = pull_session(url = host_url)
  client_url = 'http://###.###.###.###/web/path/to/bokeh/viz'
  my_script = server_session(model = None,
                             session_id = my_session.id,
                             url = client_url,
                             relative_urls = False)
  return render_template("embed.html", script = my_script, template = "Flask")

@app.route('/testing', methods = ['GET'])
@login_required
def show_test():
  return render_template("stocks.html")

Содержимое: /path/to/my/flask/app/templates/embed.html

<!doctype html>
<html>
  <body>
    {{ script|safe }}
  </body>
</html>

Источник страницы из отображаемого результата:

<!doctype html>
<html>
  <body>

<script
    src="https://###.###.###.###/web/path/to/my/bokeh/viz/autoload.js?bokeh-autoload-element=a4c3a28c-8bab-4e54-8ba8-8423914078aa&bokeh-app-path=/web/path/to/my/bokeh/viz&bokeh-absolute-url=https://###.###.###.###/web/path/to/my/bokeh/viz&bokeh-session-id=2GYRA1MPiBBX0qzlIB5ZEMyWQkV9AD1Br1KdgtZoew5P"
id="a4c3a28c-8bab-4e54-8ba8-8423914078aa"
    data-bokeh-model-id=""
    data-bokeh-doc-id=""
></script>
  </body>
</html> 

1 Ответ

0 голосов
/ 03 июля 2018

Я думаю, есть пара битов. Создавая сеанс боке, вы на самом деле просто создаете скрипт, который браузер вызывает для выполнения боке. В браузере он попытается вызвать все, что вы ему дадите, и нам нужно, чтобы он позвонил по номеру https://example.com/....., а nginx отправил трафик bokeh на сервер bokeh, а не на сервер gunicorn.

Лучшее решение, которое я нашел, состояло в том, чтобы префиксировать все вызовы bokeh server_session / server_document чем-то таким, чтобы nginx мог отправлять их на сервер bokeh, а не на колбу, а затем удалять префикс с помощью nginx. Я попытался использовать тег bokeh serve --prefix, но у меня возникли проблемы с загрузкой статических ресурсов.

@app.route('/web/path/to/bokeh/viz', methods = ['GET'])
@login_required
def show_bokeh_viz():
  my_session = pull_session(url = http://localhost:6001/my_bokeh_viz)

  # use actual url here
  host_url = 'https://example.com/bokeh_stuff/my_bokeh_viz'
  my_script = server_session(model = None,
                             session_id = my_session.id,
                             url = url,
                             relative_urls = False)
  return render_template("embed.html", script = my_script, template = "Flask")

соответствующий бит nginx:

# All bokeh plots are prefixed with some unique route, in this case 'bokeh_stuff'
# Here we will collect these, strip off the prefix, and send the result to the bokeh server
# Sends https://example.com/bokeh_stuff/sliders ⇒ http://localhost:6001/sliders
location /bokeh_stuff  {
    rewrite  ^/bokeh_stuff/(.*)  /$1 break;
    proxy_pass http://127.0.0.1:6001;
    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;
}

# All traffic other than static and bokeh_stuff goes to the gunicorn server
location / {
    # forward application requests to the gunicorn server
    proxy_pass http://localhost:6000;
    proxy_redirect off;
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}

Затем в подачу боке нужно добавить --allow-websocket-origin=example.com

Чтобы проверить, что происходит, взгляните на консольные и сетевые запросы, а также на вывод сервера bokeh (журналы супервизора) и посмотрите, что он вам дает. Вы также можете перейти на example.com/bokeh_stuff и получить страницу индекса, даже если что-то не так с вашими функциями маршрута.

...