I work on a setup with JupyterHub and keycloak. The setup is placed locally inside a VM with vagrant and inside the VM docker is used. In the hosts file of my host machine, I have added jhub
and keycloak
as alternative names for 127.0.0.1
. vagrant automatically forwards the ports 8443 and 8000 inside the VM.
First, I open the JupyterHub webpage in my browser on the host machine of the VM. On the JupyterHub landing page (before login), I click on the button that forwards me to the keycloak login mask. After the successful login, I get forwarded back to the JupyterHub. But here I run into a “500 : Internal Server Error”. This can be seen in the logs:
jhub_1 | [I 2021-01-19 17:44:00.545 JupyterHub log:181] 302 GET /hub/oauth_login?next=%2Fhub%2F -> https://keycloak:8443/auth/realms/jhub/protocol/openid-connect/auth?response_type=code&redirect_uri=https%3A%2F%2Fjhub%3A8000%2Fhub%2Foauth_callback&client_id=jhub-1&state=[secret]&scope=openid (@::ffff:10.0.2.2) 1.21ms
jhub_1 | [E 2021-01-19 17:44:00.703 JupyterHub web:1789] Uncaught exception GET /hub/oauth_callback?state=eyJzdGF0ZV9pZCI6ICJjNDgwMDAxMDg5NTQ0NmZlYjY3MmVkMDNkMTA3ZTY0YSIsICJuZXh0X3VybCI6ICIvaHViLyJ9&session_state=3817a8f6-9d0d-4495-8ce5-ca4c9abc192e&code=40819154-5408-4c67-b5be-d0032045e791.3817a8f6-9d0d-4495-8ce5-ca4c9abc192e.581c2301-1739-44bb-b398-2889b13ddd26 (::ffff:10.0.2.2)
jhub_1 | HTTPServerRequest(protocol='https', host='jhub:8000', method='GET', uri='/hub/oauth_callback?state=eyJzdGF0ZV9pZCI6ICJjNDgwMDAxMDg5NTQ0NmZlYjY3MmVkMDNkMTA3ZTY0YSIsICJuZXh0X3VybCI6ICIvaHViLyJ9&session_state=3817a8f6-9d0d-4495-8ce5-ca4c9abc192e&code=40819154-5408-4c67-b5be-d0032045e791.3817a8f6-9d0d-4495-8ce5-ca4c9abc192e.581c2301-1739-44bb-b398-2889b13ddd26', version='HTTP/1.1', remote_ip='::ffff:10.0.2.2')
jhub_1 | Traceback (most recent call last):
jhub_1 | File "/usr/local/lib/python3.8/dist-packages/tornado/web.py", line 1704, in _execute
jhub_1 | result = await result
jhub_1 | File "/usr/local/lib/python3.8/dist-packages/oauthenticator/oauth2.py", line 224, in get
jhub_1 | user = await self.login_user()
jhub_1 | File "/usr/local/lib/python3.8/dist-packages/jupyterhub/handlers/base.py", line 749, in login_user
jhub_1 | authenticated = await self.authenticate(data)
jhub_1 | File "/usr/local/lib/python3.8/dist-packages/jupyterhub/auth.py", line 462, in get_authenticated_user
jhub_1 | authenticated = await maybe_future(self.authenticate(handler, data))
jhub_1 | File "/usr/local/lib/python3.8/dist-packages/oauthenticator/generic.py", line 155, in authenticate
jhub_1 | token_resp_json = await self._get_token(http_client, headers, params)
jhub_1 | File "/usr/local/lib/python3.8/dist-packages/oauthenticator/generic.py", line 97, in _get_token
jhub_1 | resp = await http_client.fetch(req)
jhub_1 | tornado.curl_httpclient.CurlError: HTTP 599: server certificate verification failed. CAfile: none CRLfile: none
jhub_1 |
jhub_1 | [E 2021-01-19 17:44:00.703 JupyterHub web:1789] Uncaught exception GET /hub/oauth_callback?state=eyJzdGF0ZV9pZCI6ICJjNDgwMDAxMDg5NTQ0NmZlYjY3MmVkMDNkMTA3ZTY0YSIsICJuZXh0X3VybCI6ICIvaHViLyJ9&session_state=3817a8f6-9d0d-4495-8ce5-ca4c9abc192e&code=40819154-5408-4c67-b5be-d0032045e791.3817a8f6-9d0d-4495-8ce5-ca4c9abc192e.581c2301-1739-44bb-b398-2889b13ddd26 (::ffff:10.0.2.2)
jhub_1 | HTTPServerRequest(protocol='https', host='jhub:8000', method='GET', uri='/hub/oauth_callback?state=eyJzdGF0ZV9pZCI6ICJjNDgwMDAxMDg5NTQ0NmZlYjY3MmVkMDNkMTA3ZTY0YSIsICJuZXh0X3VybCI6ICIvaHViLyJ9&session_state=3817a8f6-9d0d-4495-8ce5-ca4c9abc192e&code=40819154-5408-4c67-b5be-d0032045e791.3817a8f6-9d0d-4495-8ce5-ca4c9abc192e.581c2301-1739-44bb-b398-2889b13ddd26', version='HTTP/1.1', remote_ip='::ffff:10.0.2.2')
jhub_1 | Traceback (most recent call last):
jhub_1 | File "/usr/local/lib/python3.8/dist-packages/tornado/web.py", line 1704, in _execute
jhub_1 | result = await result
jhub_1 | File "/usr/local/lib/python3.8/dist-packages/oauthenticator/oauth2.py", line 224, in get
jhub_1 | user = await self.login_user()
jhub_1 | File "/usr/local/lib/python3.8/dist-packages/jupyterhub/handlers/base.py", line 749, in login_user
jhub_1 | authenticated = await self.authenticate(data)
jhub_1 | File "/usr/local/lib/python3.8/dist-packages/jupyterhub/auth.py", line 462, in get_authenticated_user
jhub_1 | authenticated = await maybe_future(self.authenticate(handler, data))
jhub_1 | File "/usr/local/lib/python3.8/dist-packages/oauthenticator/generic.py", line 155, in authenticate
jhub_1 | token_resp_json = await self._get_token(http_client, headers, params)
jhub_1 | File "/usr/local/lib/python3.8/dist-packages/oauthenticator/generic.py", line 97, in _get_token
jhub_1 | resp = await http_client.fetch(req)
jhub_1 | tornado.curl_httpclient.CurlError: HTTP 599: server certificate verification failed. CAfile: none CRLfile: none
jhub_1 |
jhub_1 | [E 2021-01-19 17:44:00.712 JupyterHub log:173] {
jhub_1 | "X-Forwarded-Host": "jhub:8000",
jhub_1 | "X-Forwarded-Proto": "https",
jhub_1 | "X-Forwarded-Port": "8000",
jhub_1 | "X-Forwarded-For": "::ffff:10.0.2.2",
jhub_1 | "Cookie": "oauthenticator-state=[secret]",
jhub_1 | "Accept-Language": "de-DE,de;q=0.9,en-US;q=0.8,en;q=0.7",
jhub_1 | "Accept-Encoding": "gzip, deflate, br",
jhub_1 | "Sec-Fetch-Dest": "document",
jhub_1 | "Sec-Fetch-User": "?1",
jhub_1 | "Sec-Fetch-Mode": "navigate",
jhub_1 | "Sec-Fetch-Site": "cross-site",
jhub_1 | "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9",
jhub_1 | "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.141 Safari/537.36",
jhub_1 | "Upgrade-Insecure-Requests": "1",
jhub_1 | "Connection": "close",
jhub_1 | "Host": "jhub:8000"
jhub_1 | }
jhub_1 | [E 2021-01-19 17:44:00.712 JupyterHub log:181] 500 GET /hub/oauth_callback?state=[secret]&session_state=[secret]&code=[secret] (@::ffff:10.0.2.2) 70.45ms
I don’t understand why I run into a tornado.curl_httpclient.CurlError: HTTP 599: server certificate verification failed. CAfile: none CRLfile: none
.
I do not understand why the verification fails and why both files are none.
Setup
jupyterhub
FROM jupyterhub/jupyterhub
RUN apt-get upgrade -y
ADD jupyterhub_config.py /srv/jupyterhub/jupyterhub_config.py
RUN pip install jupyter oauthenticator
RUN openssl req -x509 -newkey rsa:4096 -sha256 -days 3650 -nodes \
-keyout /srv/jupyterhub/jhubssl.key \
-out /srv/jupyterhub/jhubssl.crt \
-subj "/C=DE/ST=XXX/L=XXX/O=XXX/OU=XXXX/CN=XXXXX" \
-addext "subjectAltName=DNS:localhost,DNS:jhub,DNS:jupyterhub,IP:127.0.0.1" \
&& cp /srv/jupyterhub/jhubssl.crt /usr/local/share/ca-certificates/ \
&& chmod 644 /usr/local/share/ca-certificates/jhubssl.crt \
&& dpkg-reconfigure ca-certificates \
&& update-ca-certificates --fresh
What we can see here is that a fresh certificate is created and added. With the last lines, the certificate should also be installed on the JupyterHub so that accessing its own webpages with the self-signed certificate should not be a problem. When I am inside the JupyterHub docker container, I have also executed this: openssl s_client -showcerts -connect jhub:8000
and its output starts like this:
SSL handshake has read 2429 bytes and written 376 bytes
Verification: OK
---
New, TLSv1.2, Cipher is ECDHE-RSA-AES128-GCM-SHA256
Server public key is 4096 bit
Secure Renegotiation IS supported
Compression: NONE
Expansion: NONE
No ALPN negotiated
SSL-Session:
So generally a process inside the JupyterHub docker container can consume resources when accessing itself as https://jhub:8000.
docker-compose
version: "3.3"
services:
keycloak:
environment:
- KEYCLOAK_USER=admin
- KEYCLOAK_PASSWORD=secret
- KEYCLOAK_IMPORT=/tmp/jhub-realm.json
image: jboss/keycloak:latest
ports:
- 8443:8443
volumes:
- ./keycloak/config/jhub-realm.json:/tmp/jhub-realm.json
jhub:
environment:
- OAUTH_TLS_VERIFY=0
- OAUTH2_AUTHORIZE_URL=https://keycloak:8443/auth/realms/jhub/protocol/openid-connect/auth
- OAUTH2_TOKEN_URL=https://keycloak:8443/auth/realms/jhub/protocol/openid-connect/token
- OAUTH_CLIENT_ID=jhub-1
- OAUTH_CLIENT_SECRET=*****************
- OAUTH_CALLBACK_URL=https://jhub:8000/hub/oauth_callback
- OAUTH2_USERDATA_URL=https://keycloak:8443/auth/realms/jhub/protocol/openid-connect/userinfo
build: ./jupyterhub/jupyterhub-server
hostname: jhub
ports:
- "8000:8000"
Does somebody have an idea why I run into the CurlError? This is only supposed to be a local setup, for the production system of course proper certificates are used. Therefore, here I only want a solution that somehow works. Since it only runs on localhost, I am open to solutions that would be insecure in a production environment. Thank you very much!