0

I'm trying to set up a secured Jupyter notebook on my Apache 2.4.7 server. If there's a better, easier, or more secure way to do it, that'd be great. I know AuthType Basic doesn't really add much, I was just playing with Apache, and am including it here for completeness. When I set up an unsecure notebook through HTTP it works, but obviously this is a bad idea.

The issue I'm having, specifically, is

Internal Server Error

But only when accessing it through my domain. Obviously it has to be secured, as I'm exposing python through it. Apache doesn't log an error for this, but the Jupyter notebook throws SSL Error on some number('127.0.0.1', some number): [SSL: WRONG_VERSION_NUMBER_NUMBER] wrong version number (_ssl.c:600)

I have Jupyter set up with SSL and can access it through the normal https://localhost:8888/tree which confirms the same set up as https://www.[mydomain].com/ipython in connection details and technical details in the security tab for the pages in both Chrome and Firefox

Jupyter seems to default to TLS 1.0, according to openssl s_client -connect localhost:8888, selecting another protocol gets SSL: WRONG_VERSION_NUMBER as expected. diff <(openssl s_client -connect www.[mydomain].com:443 </dev/null) <(openssl s_client -connect localhost:8888 </dev/null) shows no real differences. A byte difference due to size of url, pretty sure, the session-ID, Master-Key, and the TLS session ticket are the differences.

My jupyter_notebook_config.py (after cutting it down and sanitation of a sort?) looks like:

c = get_config()
c.NotebookApp.certfile = u'[/my/cert/here].pem'
c.NotebookApp.keyfile = u'[/my/cert/here].pem'
c.NotebookApp.open_browser = False 
c.NotebookApp.password = u'sha1:[much:password:sha:such:wow]'
c.NotebookApp.port = 8888
c.NotebookApp.tornado_settings = {
    'headers': {
    'Content-Security-Policy': "frame-ancestors 'https://www.[mydomain].com' 'self' "
    }
}

I set it up with the ipython instructions (which are the same as the jupyter one luckily) and a bug report that introduced the keyfile thing. I set up the cert with openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout [mycert.pem] -out [mycert.pem] as suggested in the bug report, with the key and cert appended together for simplicity. I'll get a real one signed later. Since it works locally (with a browser complaint of localhost not matching the CN www.[mydomain].com which doesn't appear when accessing through the website), I'd imagine getting the Apache server setup correctly is the only thing I'm missing.

Pulling from a couple of related but unfortunately not completely duplicating stackoverflow and serverfault (c.NotebookApp.ip = '*'/'localhost' doesn't help) questions, I got to the settings:

SSLProxyEngine on
SSLProtocol TLSv1
SSLProxyVerify none
SSLProxyCheckPeerCN off
SSLProxyCheckPeerName off
ServerName www.[mydomain].com
SSLCertificateFile [/my/cert/here].pem
<Location /ipython>
    ProxyPass        https://localhost:8888/tree
    ProxyPassReverse https://localhost:8888/tree
    ProxyAddHeaders On
    ProxyPreserveHost On
    ProxyPassReverseCookieDomain localhost www.[mydomain].com 
    RequestHeader set Origin "https://localhost:8888"
    AuthType Basic
    AuthName "Warning: Who are you?"
    AuthUserFile [/password/file/just/for/fun/].htpasswd
    Require valid-user
    Require ssl
</Location>
<Location /kernelspecs/>
    ProxyPass        ws://localhost:8888/kernelspecs/
    ProxyPassReverse ws://localhost:8888/kernelspecs/
    ProxyAddHeaders On
    ProxyPreserveHost On
    AuthType Basic
    AuthName "Warning: Who are you?"
    AuthUserFile [/password/file/just/for/fun/].htpasswd
    Require valid-user
    Require ssl
</Location>
<Location /static/>
    ProxyPass        ws://localhost:8888/static/
    ProxyPassReverse ws://localhost:8888/static/
    ProxyAddHeaders On
    ProxyPreserveHost On
    AuthType Basic
    AuthName "Warning: Who are you?"
    AuthUserFile [/password/file/just/for/fun/].htpasswd
    Require valid-user
    Require ssl
</Location>
<Location /ipython/api/kernels/>
    ProxyPass        ws://localhost:8888/ipython/api/kernels/
    ProxyPassReverse ws://localhost:8888/ipython/api/kernels/
    ProxyAddHeaders On
    ProxyPreserveHost On
    AuthType Basic
    AuthName "Warning: Who are you?"
    AuthUserFile [/password/file/just/for/fun/].htpasswd
    Require valid-user
    Require ssl
</Location>
<Location /login>
    ProxyPass        ws://localhost:8888/login
    ProxyPassReverse ws://localhost:8888/login
    ProxyAddHeaders On
    ProxyPreserveHost On
    AuthType Basic
    AuthName "Warning: Who are you?"
    AuthUserFile [/password/file/just/for/fun/].htpasswd
    Require valid-user
    Require ssl
</Location>

For now, the Jupyter notebook is run in a terminal so I can see the output as I access it variously.

Poik
  • 103
  • 1
  • 5
  • Just a random thought: IMHO the security benefit of setting up an Apache reverse proxy to use HTTPS to connect to an application running on localhost is non-existent. - Having said that ; despite your details I'm quite unclear about what and where the problem is exactly. Is it connecting to Apache that is the issue, or is the problem getting Apache to connect to https://localhost:8888"? – HBruijn Mar 21 '16 at 16:49
  • The problem is I want to access this notebook elsewhere, so security IS an issue. I could just ssh in and play with python that way, but I do have a reason for this madness. I can access it through localhost, and all that, but when accessing it through my domain, I can't. I'll edit with more details. – Poik Mar 21 '16 at 17:01
  • Honestly, the biggest reason I want to do it, is to have a static uneditable version available on my website publicly accessible for sharing (the next and probably easier step), and be able to edit it remotely and update it with findings (which requires a GPU on my server to run, since I'm working with computationally intensive code). There are other ways I could do this, but I felt like this is the easiest. That doesn't mean it's the best, obviously. – Poik Mar 21 '16 at 17:13
  • I see what you mean. I could use the native security and just open the port for the notebook files on my router. Since the static notebooks don't need security since they don't expose anything, I don't need to bother with much. I really just wanted to learn more about Apache, but it's silly to do it for no reason. I'll delete this if someone doesn't offer something later. Have Apache serve it would allow me to have all of my access controls for the domain in one place, that's the only real benefit. – Poik Mar 21 '16 at 18:26

1 Answers1

1

Since the error is coming from Jupyter and not from Apache, it appears to be a problem with the communication between Apache and Jupyter. If Jupyter's websocket server is expecting encrypted connections (which it appears to be, since it's complaining) then instead of using the ws:// scheme, you will need to use wss://

DerfK
  • 19,313
  • 2
  • 35
  • 51
  • 1
    Thanks! This indeed is the problem, now I can access the notebook, but I can't use it, since Apache doesn't allow PUT by default when it doesn't need to. The solution to my new problem, for anyone stumbling upon this in the future, doing something as dumb as I am, is in this [question](http://serverfault.com/questions/438183/how-to-enable-all-http-methods-in-an-apache-http-server). – Poik Mar 21 '16 at 20:31