jupyter-server / enterprise_gateway

A lightweight, multi-tenant, scalable and secure gateway that enables Jupyter Notebooks to share resources across distributed clusters such as Apache Spark, Kubernetes and others.
https://jupyter-enterprise-gateway.readthedocs.io/en/latest/
Other
623 stars 222 forks source link

404 Error when trying to access Gateway api endpoint from Javascript in console #906

Closed averageflamethrowerguy closed 3 years ago

averageflamethrowerguy commented 3 years ago

I think my issue is Enterprise Gateway rejecting GET requests because of its CORS policy. How can I allow cross-origin requests with the Jupyter Gateway on Kubernetes? I've read through this document, and it looks like it has the configuration I need. Unfortunately, these flags don't work with the helm commands I'm using to deploy the Gateway. For reference, I'm using this command:

helm upgrade --install --atomic --namespace enterprise-gateway --EnterpriseGatewayApp.allow_origin=* enterprise-gateway helm

which fails with "Error: unknown flag: --EnterpriseGatewayApp.allow_origin" (The second "helm" reference is to the directory I'm storing the .yaml files within)

I'm trying to programmatically control Enterprise Gateway kernels using API calls from a Javascript frontend.

I'm using a Kubernetes deployment of Enterprise Gateway via Helm as specified by this doc: https://jupyter-enterprise-gateway.readthedocs.io/en/latest/kernel-kubernetes.html I'm using nginx as my ingress provider, and I'm using AWS as my cloud service.

Below is the spec of my nginx ingress resource. Before I added the CORS configuration to the nginx file, I was getting GET request failures with the standard "Access-Control-Allow-Origin headers are not specified" error.

apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  annotations:
    kubernetes.io/ingress.class: nginx
    meta.helm.sh/release-name: enterprise-gateway
    meta.helm.sh/release-namespace: enterprise-gateway
    nginx.ingress.kubernetes.io/cors-allow-credentials: "true"
    nginx.ingress.kubernetes.io/cors-allow-methods: PUT, GET, POST, DELETE, PATCH,
      OPTIONS
    nginx.ingress.kubernetes.io/cors-allow-origin: '*'
    nginx.ingress.kubernetes.io/cors-expose-headers: '*, X-CustomResponseHeader'
    nginx.ingress.kubernetes.io/enable-cors: "true"
    nginx.ingress.kubernetes.io/force-ssl-redirect: "false"
    nginx.ingress.kubernetes.io/rewrite-target: /$1
    nginx.ingress.kubernetes.io/ssl-redirect: "false"
  creationTimestamp: "2020-11-29T18:11:24Z"
  generation: 1
  labels:
    app.kubernetes.io/managed-by: Helm
  name: enterprise-gateway-ingress
  namespace: enterprise-gateway
  resourceVersion: "41088484"
  selfLink: /apis/extensions/v1beta1/namespaces/enterprise-gateway/ingresses/enterprise-gateway-ingress
  uid: (removed for security)
spec:
  rules:
  - http:
      paths:
      - backend:
          serviceName: enterprise-gateway
          servicePort: 8888
        path: /gateway/?(.*)
status:
  loadBalancer:
    ingress:
    - hostname: (removed for security)

After doing a call now, it looks like Kernel Gateway (and not nginx) is still terminating the request for CORS reasons.

[W 2020-11-30 21:54:41.031 EnterpriseGatewayApp] Blocking Cross Origin API request for /api/kernelspecs.  Origin: http://localhost:3000, Host: removed.us-east-1.elb.amazonaws.com
[W 201130 21:54:41 web:2250] 404 GET /api/kernelspecs (192.168.246.185) 1.07ms
[W 2020-11-30 21:54:56.188 EnterpriseGatewayApp] Blocking Cross Origin API request for /api/kernelspecs.  Origin: http://localhost:3000, Host: removed.us-east-1.elb.amazonaws.com
[W 201130 21:54:56 web:2250] 404 GET /api/kernelspecs (192.168.246.185) 0.95ms
[D 2020-11-30 21:54:59.801 EnterpriseGatewayApp] Found kernel python3 in /opt/conda/share/jupyter/kernels
[D 2020-11-30 21:54:59.802 EnterpriseGatewayApp] Found kernel r_docker in /usr/local/share/jupyter/kernels
[D 2020-11-30 21:54:59.802 EnterpriseGatewayApp] Found kernel r_kubernetes in /usr/local/share/jupyter/kernels
  ... (finds all the other kernels) ...
[I 201130 21:54:59 web:2250] 200 GET /api/kernelspecs (192.168.201.93) 254.03ms
kevin-bates commented 3 years ago

Hi @averageflamethrowerguy - thanks for the issue. I find it surprising that this is an issue, but I'm really not up on the nuances that trigger the need to open up the CORS stuff (sorry, I'm not really a web developer).

Adding EG-specific command-line options to the helm command won't work. Instead, you'd need to "plumb" a value/env into the helm chart files in values.yaml and then in deployment.yaml for the env EG_ALLOW_ORIGIN.

Do you know what triggered GET /api/kernelspecs to start working on the last attempt? Is that from something that doesn't trigger CORS enforcement? (See what I mean about not knowing this stuff! :smile: )

averageflamethrowerguy commented 3 years ago

Oh, yes! I got that from querying the same route via Postman, a developer tool (which doesn't send an origin header by default, so it doesn't fail). I didn't mean to include that in the logs. Whoops!

Also: Score! Thanks so much! Here's the solution for anyone else coming along:

  1. Edit the values.yaml nginx section to include the following additional annotations (not all of them may be required). Also note that this is a wide-open CORS policy--good for development, not for security:

    nginx.ingress.kubernetes.io/cors-allow-credentials: "true"
    nginx.ingress.kubernetes.io/cors-allow-methods: PUT, GET, POST, DELETE, PATCH, OPTIONS
    nginx.ingress.kubernetes.io/cors-allow-origin: '*'
    nginx.ingress.kubernetes.io/cors-expose-headers: '*, X-CustomResponseHeader'
    nginx.ingress.kubernetes.io/enable-cors: "true"
  2. Add the following section to deployment.yaml in the containers/env section

    - name: EG_ALLOW_ORIGIN
    value: "*"