在 Juptyer 前面添加 Flask 身份验证层

问题描述 投票:0回答:1

我想在 Juptyer 前面添加 Flask 身份验证层,以便我可以从任何地方进行访问。

Flask 和 Jupyter 位于同一服务器上,因此身份验证后应该通过连接。

我正在使用具有以下配置的 Juptyer 服务器

c.ServerApp.ip = '127.0.0.1'
c.ServerApp.port = 8888
c.ServerApp.open_browser = False
c.ServerApp.token = ''  # Disable token authentication
c.ServerApp.password = ''  # Disable password authentication
c.ServerApp.disable_check_xsrf = True  # Disable XSRF checks
c.ServerApp.trust_xheaders = True  # Allow reverse proxy headers
c.ServerApp.allow_remote_access = True
c.ServerApp.allow_origin = '*'

下面是我当前的 nginx 配置:

server {
    listen 443 ssl;
    server_name jupyter.mywebsite.com;

    ssl_certificate /etc/letsencrypt/live/jupyter.mywebsite.com/fullchain.pem; # Certbot
    ssl_certificate_key /etc/letsencrypt/live/jupyter.mywebsite.com/privkey.pem; # Certbot
    include /etc/letsencrypt/options-ssl-nginx.conf; # Certbot SSL options
    ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;

    location / {
        proxy_pass http://127.0.0.1:7000/;  # Flask server
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;

        # WebSocket support for Jupyter
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_read_timeout 3600s;
        proxy_send_timeout 3600s;
    }
}

server {
    listen 80;
    server_name jupyter.mywebsite.com mywebsite.com www.mywebsite.com;

    return 301 https://$host$request_uri;
}


server {
    listen 443 ssl;
    server_name mywebsite.com www.mywebsite.com;

    ssl_certificate /etc/letsencrypt/live/mywebsite.com/fullchain.pem; # Certbot
    ssl_certificate_key /etc/letsencrypt/live/mywebsite.com/privkey.pem; # Certbot
    include /etc/letsencrypt/options-ssl-nginx.conf; # Certbot SSL options
    ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;

    location / {
        proxy_pass http://127.0.0.1:7000;  # Flask app
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;

        # WebSocket support
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_read_timeout 3600s;
        proxy_send_timeout 3600s;
    }
}

这是我当前的烧瓶配置

# Proxy Jupyter Requests
@app.after_request
def adjust_csp_for_jupyter(response):
    if "/jupyter/" in request.path:
        response.headers["Content-Security-Policy"] = (
            "default-src 'self'; script-src 'self' https://cdn.jsdelivr.net 'unsafe-eval'; "
            "style-src 'self' https://cdnjs.cloudflare.com; img-src 'self' data:;"
        )
    return response


JUPYTER_BASE_URL = "http://127.0.0.1:8888"  # Internal Jupyter address

@app.route('/jupyter/<path:path>', methods=['GET', 'POST', 'PUT', 'DELETE'])
@login_required
def proxy_to_jupyter(path):
    jupyter_url = f"{JUPYTER_BASE_URL}/{path}"
    try:
        response = requests.request(
            method=request.method,
            url=jupyter_url,
            headers={key: value for key, value in request.headers.items() if key.lower() not in ['host', 'cookie']},
            data=request.get_data(),
            cookies=request.cookies,
            allow_redirects=False,  # Handle redirects manually
        )
        excluded_headers = ['content-encoding', 'content-length', 'transfer-encoding', 'connection']
        headers = [(name, value) for name, value in response.headers.items() if name.lower() not in excluded_headers]
        return Response(response.content, response.status_code, headers)
    except requests.RequestException as e:
        abort(502, f"Failed to connect to Jupyter: {str(e)}")

@app.route('/api/kernels/<path:path>', methods=['GET', 'POST', 'PUT', 'DELETE'])
@login_required
def proxy_websocket_to_jupyter(path):
    # WebSocket-specific proxying code (use `flask-sock` or similar)
    pass
flask nginx jupyter nginx-reverse-proxy
1个回答
0
投票

所以我想通了!我有一些错误,并且过度控制 NGINX 配置:下面是准确的配置:

主要变化

  • 删除了子域配置
  • 创建了一个名为 auth-check 的自定义位置,这在下面的 jupyter 配置中被调用。这样 Flask 就可以作为交互的函数。
  • 只有一种jupyter配置
  • 然后我就有了最后进行身份验证的烧瓶应用程序。这可确保其他受保护站点的配置在加载索引之前进行。
    server {
    listen 443 ssl;
    server_name mywebsite.com www.mywebsite.com;

    ssl_certificate /etc/letsencrypt/live/mywebsite.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/mywebsite.com/privkey.pem;
    include /etc/letsencrypt/options-ssl-nginx.conf;
    ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;

    location = /auth-check {
        internal;
        proxy_pass http://127.0.0.1:7000/auth-check;
        proxy_set_header Cookie $http_cookie;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    }

    # Jupyter main routes
    location /jupyter/ {
        auth_request /auth-check;
        proxy_pass http://127.0.0.1:8888/jupyter/;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;

        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_read_timeout 3600s;
        proxy_send_timeout 3600s;
    }

    # Flask application (if separate from Jupyter)
    location / {
        proxy_pass http://127.0.0.1:7000;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;

        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_read_timeout 3600s;
        proxy_send_timeout 3600s;
    }
}

烧瓶的更改:

  • 添加验证检查方法
  • 添加 csp 标头以确保套接字的传递有效。
@app.route('/auth-check')
def auth_check():
    if current_user.is_authenticated:
        return '', 200
    return '', 401

# Adjust CSP for Jupyter pages if desired.
@app.after_request
def adjust_csp_for_jupyter(response):
    if "/jupyter/" in request.path:
        response.headers["Content-Security-Policy"] = (
          "default-src 'self'; "
          "script-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net 'unsafe-eval'; "
          "style-src 'self' 'unsafe-inline' https://cdnjs.cloudflare.com https://fonts.googleapis.com; "
          "font-src 'self' https://fonts.gstatic.com; "
          "img-src 'self' data:; "
          "connect-src 'self' wss:;"
          "connect-src 'self' wss: https://mywebsite.com;"
        )
    return response

# Internal Jupyter server base URL (no auth). Ensure you started Jupyter with --ServerApp.token=''
JUPYTER_BASE_URL = "http://127.0.0.1:8888/jupyter"

@app.route('/jupyter/<path:path>', methods=['GET', 'POST', 'PUT', 'DELETE', 'PATCH'])
@login_required
def proxy_to_jupyter(path):
    jupyter_url = f"{JUPYTER_BASE_URL}/{path}"
    try:
        # Forward the request to the Jupyter server
        response = requests.request(
            method=request.method,
            url=jupyter_url,
            headers={k: v for k, v in request.headers.items() if k.lower() not in ['host', 'cookie']},
            data=request.get_data(),
            cookies=request.cookies,
            allow_redirects=False
        )
        excluded_headers = ['content-encoding', 'content-length', 'transfer-encoding', 'connection']
        headers = [(name, value) for name, value in response.headers.items() if name.lower() not in excluded_headers]
        return Response(response.content, response.status_code, headers)
    except requests.RequestException as e:
        abort(502, f"Failed to connect to Jupyter: {str(e)}")

@app.route('/api/kernels/<path:path>', methods=['GET', 'POST', 'PUT', 'DELETE', 'PATCH'])
@login_required
def proxy_websocket_to_jupyter(path):
    # WebSocket proxying is more complex. Consider:
    # - Using a reverse proxy like Nginx for WebSocket traffic
    # - Using flask-sock or a similar package to handle upgrade requests
    # This is a placeholder.
    pass

Jupyter 的关键更改,

  • 我搬到了实验室而不是服务器,因为只有我在访问它,服务器允许更多的多用户,但我不需要它。
  • 还从配置中删除了 origin 语句。因为这样更安全。
© www.soinside.com 2019 - 2024. All rights reserved.