Correct Content Security Policy for use with Proxy

Hi forum!

Situation

I configured a jupyterhub on a VM which worked perfectly until we took some security measures.
The setup is the following:

  • The jupyterhub service is listening on port 127.0.0.1:8000.
  • The security proxy presenting a certificate does proxy_pass from HUB.DOMAIN.TLD:443 to 127.0.0.1:8000

As soon as the securied setup was activated the Content Security Policy seemed wrong. This is what my browser is showing:
Refused to execute inline script because it violates the following Content Security Policy directive: "script-src 'self'". Either the 'unsafe-inline' keyword, a hash ('sha256-<some hash>'), or a nonce ('nonce-...') is required to enable inline execution

I tried

I am not an expert in this field, but I read myself into the topic and I understand that the default Content Security Policy(CSP) disables execution of inline javascript code, which is used extensively by Jupyter.[1]

Also CSP is used to prevent XSS. [2]

However I tried to alter the CSP settings in /opt/jupyterhub/etc/jupyterhub/jupyterhub_config.py for making it work. Took me a day messing around with c.JupyterHub.tornado_settings no success yet.

I tried is for example

c.JupyterHub.tornado_settings = {'headers': {'Content-Security-Policy': "script-src * data: blob: 'unsafe-inline' 'unsafe-eval';"}}

Result

Plugin '@jupyterlab/filebrowser-extension:share-file' failed to activate.
(anonymous) @ index.es6.js:282
Promise.catch (async)
(anonymous) @ index.es6.js:281
e.start @ index.es6.js:280
o @ index.out.js:1011
load (async)
ANye @ index.out.js:1060
t @ bootstrap:84
0 @ main.71316899e49699071e9d.js:1
t @ bootstrap:84
i @ bootstrap:45
s @ bootstrap:32
(anonymous) @ vendors~main.8a850565bae0c4974bdc.js:2
index.es6.js:283 EvalError: Refused to evaluate a string as JavaScript because 'unsafe-eval' is not an allowed source of script in the following Content Security Policy directive: "script-src 'self'".

    at new Function (<anonymous>)
    at g.R (index.js:120)
    at g.l (resolve.js:54)
    at Object.j [as resolveRef] (index.js:189)
    at Object.e [as code] (ref.js:21)
    at Object.e [as validate] (validate.js:277)
    at Object.e [as code] (properties.js:201)
    at e (validate.js:374)
    at R (index.js:88)
    at g.f (index.js:55)

I’ve read through all the issues on Github but mainly people have these problems when embedding jupyterhub into an iframe.

I’d appreciate ideas on the topic. I believe my setup is a common one. Probably I am missing something. Let’s find an answer that helps other googling this.

All the best
Philipp

[1] https://jupyter-notebook.readthedocs.io/en/stable/public_server.html
[2] https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP

Still hoping someone may be able to help!

My main problem seems to be that my CSP settings for tornado seem to be ignored.

c.JupyterHub.tornado_settings = {'headers': {'Content-Security-Policy': "script-src 'self' 'unsafe-eval' 'unsafe-inline'; object-src 'self'; style-src 'self' 'unsafe-inline';"}}

I added CSP to the NotebookApp. As I am using JupyterLab and not Jupyter Notebook probably “NotebookApp” is wrong here, right?

c.Spawner.args = ["–NotebookApp.tornado_settings={ ‘headers’:{ ‘Content-Security-Policy’: “script-src ‘self’ ‘unsafe-eval’ ‘unsafe-inline’; object-src ‘self’;style-src ‘self’ ‘unsafe-inline’;”} $

What I find miserable though is the fact that even on the login page the CSP is not the one I set:
connect-src 'self'; default-src 'self'; font-src 'self'; frame-ancestors 'none'; frame-src 'none'; img-src 'self'; script-src 'self'; style-src 'self'