Forums

SSL support

Hi guys,

I understand that SSL support was implemented a few months back (truth?). I'm working with pyQBOE which requires an SSL connection to do its thing: specifically, I need to know the location of the .key and .crt files associated with *.pythonanywhere.com. I found the .crt files in /etc/ssl/certs, but can't seem to find the .key (is it in the locked /etc/ssl/private?).

Before I realized that PA had a wildcard certificate, I created one for my app on the domain geoffham.pythonanywhere.com. If for whatever reason I can't be given access to the .key file for the wildcard certificate, would it be possible for me to configure my nginx instance to use the .key and .crt files I created?

Any help would be much appreciated.

G

We're definitely not going to publish our SSL private key to our users. That would allow anyone to impersonate us and completely defeat the purpose of SSL.

However, we do have a process that we're testing to allow users to user SSL on their domains. It's manual until we have confirmed that it works well enough to automate. Drop us a line at support@pythonanywhere.com to tell us where your crt and key files are and we'll configure nginx to serve your domain. We haven't tested it with a x.pythonanywhere.com domain yet so it may take us a while to work out exactly how to work it into our infrastructure.

Your crt file needs to contain the entire certificate chain. See the second bullet point on this page for details.

Note The private key must not be password protected. You can tell if it's password protected by looking at the file. If it has a line like

Proc-Type: 4,ENCRYPTED

then it's encrypted and you need to decrypt it before we can use it:

openssl rsa -in server.key.encrypted -out server.key

In a slightly related aside, I recently discovered that StartCom will currently issue you a trusted SSL certificate for free - as far as I'm aware, they're the only free provider who has their root certificate included in the majority of web browsers.

You only get a class 1 certificate which only validates the domain (i.e. it only has a common name specified, not the organisation), and I believe you're limited to only a single domain per certificate (i.e. no subdomains), but it's good enough to get rid of the nasty security warnings on browsers which is what most individual users will care about and the price is right. Even if you have a commercial site, you might want to use it for your staging and testing process. They also offer certificates for organisations which have better validation, but they charge for those like most other providers.

I've been trying to use hyper (http/2) to connect to the Apple Push Notifications sever, using Apple's newer token based authentication via the "python-apns" python module. It works ok on my laptop with my phone receiving the notifications ok. However on PythonAnywhere server it fails :( , and with logging enabled in my python script, hyper is failing an assertion:

"assert proto in H2_NPN_PROTOCOLS or proto == H2C_PROTOCOL AssertionError"

So it appears that hyper client can't obtain the required the H2 protocol.

From searching on google, I think this is due to the version of OpenSSL, eg. on this hyper issues forum: https://github.com/Lukasa/hyper/issues/213

"So ALPN is only available on newer OpenSSLs, as you've discovered. My bet is that Heroku by default uses Ubuntu 14.04 for its base OS, which contains OpenSSL < 1.0.2: sadly, ALPN was introduced in OpenSSL 1.0.2."

On my laptop I use version: "OpenSSL 1.0.2l 25 May 2017"

Whereas on PythonAnywhere I have: "OpenSSL 1.0.1f 6 Jan 2014"

I think the Apple APN documentation ( https://developer.apple.com/library/content/documentation/NetworkingInternet/Conceptual/RemoteNotificationsPG/CommunicatingwithAPNs.html ) confirms this:

"Your provider must support TLS 1.2 or higher when connecting to APNs."

I can try to install the latest OpenSSL libraries in my home directory, but before doing that I was wondering if PythonAnywhere might perhaps be planning to to upgrade to a newer OpenSSL. On the openSSL website ( https://www.openssl.org/source/ ) they say:

"Note: The latest stable version is the 1.1.0 series of releases. Also available is the 1.0.2 series. This is also our Long Term Support (LTS) version (support will be provided until 31st December 2019). The 0.9.8, 1.0.0 and 1.0.1 versions are now out of support and should not be used."

I realise upgrading OpenSSL might break other applications, so would take time to test.

hi there,

Thanks for bringing that to our attention. We do intend to eventually upgrade our system images to use ubuntu 16.04 (which would be OpenSSL v1.0.2), but we don't have any timeline on when that will occur yet.

Hi Conrad,

Thank you for your reply. Yes, the OpenSSL v1.0.2 in the ubuntu should solve this problem.

I downloaded and built the OpenSSL v1.1.0f libraries in my home directory, and downloaded python 3.5.3 source and compiled it with this OpenSSL 1.1.0. Then when executing my python script at the python anywhere console command line the Apple push notification now work using this updated OpenSSL.

But, I'm sure if I can use this compiled python to serve pages on PythonAnywhere?

I used the commands below in case anyone else whats to do similar in future. There's probably a better or shorter way to do this.

(1) Build the OpenSSL libraries:

( Based on: https://github.com/magnumripper/JohnTheRipper/wiki/Howto-build-non-root-when-system-does-not-have-OpenSSL. )

wget https://www.openssl.org/source/openssl-1.1.0f.tar.gz tar zxf openssl-1.1.0f.tar.gz cd openssl-1.1.0f mkdir ~/ssl

./Configure --prefix=$HOME/ssl linux-x86_64 # if your system is not linux-x86_64, you will have to pick the right one make -build_libs > mymake.log # or gmake, or whatever 'make' tool is on your system make -s test make install_sw # I like installing without using -s to 'see' what is done

(2) Set paths to the new libraries in my ~./bashrc:

OPENSSL_CFLAGS="-I$HOME/ssl -I$HOME/ssl/include" OPENSSL_LIBS="-L$HOME/ssl/lib"

export CFLAGS=$OPENSSL_CFLAGS export CPPFLAGS=$OPENSSL_CFLAGS export LDFLAGS=$OPENSSL_LIBS

export LD_LIBRARY_PATH=$HOME/ssl/lib:$LD_LIBRARY_PATH export LIBRARY_PATH=$HOME/ssl/lib:$LIBRARY_PATH

source ~/.bashrc

(3) Build Python with these new local OpenSSL libraries: ( Based on older version: http://thelazylog.com/install-python-as-local-user-on-linux/ and: https://stackoverflow.com/questions/38697181/compiling-python-2-7-12-with-non-system-openssl-on-centos-5 )

wget https://www.python.org/ftp/python/3.5.3/Python-3.5.3.tgz tar zxf Python-3.5.3.tgz cd Python-3.5.3

mkdir ~/python35 configure --prefix=$HOME/python35 make && make install

export PATH=$HOME/python35/bin:$PATH

(4) Check this python is using this latest OpenSSL python3 import ssl ssl.OPENSSL_VERSION 'OpenSSL 1.1.0f 25 May 2017' exit()

(5) Install modules pip3 install --global-option=build_ext --global-option="-I$HOME/ssl/include" --global-option="-L$HOME/ssl/lib" gobiko.apns and other modules...

(6) Check thes modules are also using this latest OpenSSL python -m OpenSSL.debug

(7) Create virtualenv, and set PythonAnywhere to use this python35 I'm not sure if it is possible to use this compiled python3.5 within the PythonAnywhere server. I tried making an virtualenv, and setting the web interface to use this:

pip3 install virtualenv virtualenvwrapper mkvirtualenv --python=/home/sbridgett/python35/bin/python sm17_ssl_110

pip3 install -U --global-option=build_ext --global-option="-I/home/sbridgett/ssl/include" --global-option="-L/home/sbridgett/ssl/lib" gobiko.apns pip3 install django

but get errors in the server log about:

initialized 50 metrics 2017-07-21 16:04:32 Traceback (most recent call last): 2017-07-21 16:04:32 File "/bin/user_wsgi_wrapper.py", line 30, in <module> 2017-07-21 16:04:32 import logging 2017-07-21 16:04:32 File "/home/sbridgett/python35/lib/python3.5/logging/init.py", line 26, in <module> 2017-07-21 16:04:32 import sys, os, time, io, traceback, warnings, weakref, collections 2017-07-21 16:04:32 File "/home/sbridgett/python35/lib/python3.5/traceback.py", line 5, in <module> 2017-07-21 16:04:32 import linecache 2017-07-21 16:04:32 File "/home/sbridgett/.virtualenvs/sm17_ssl_110/lib/python3.5/linecache.py", line 8, in <module> 2017-07-21 16:04:32 import functools 2017-07-21 16:04:32 File "/home/sbridgett/.virtualenvs/sm17_ssl_110/lib/python3.5/functools.py", line 23, in <module> 2017-07-21 16:04:32 from weakref import WeakKeyDictionary 2017-07-21 16:04:32 File "/home/sbridgett/.virtualenvs/sm17_ssl_110/lib/python3.5/weakref.py", line 12, in <module> 2017-07-21 16:04:32 from _weakref import ( 2017-07-21 16:04:32 ImportError: cannot import name '_remove_dead_weakref' 2017-07-21 16:04:32 unable to load app 0 (mountpoint='') (callable not found or import error) 2017-07-21 16:04:32 *** no app loaded. going in full dynamic mode *** 2017-07-21 16:04:32 *** uWSGI is running in multiple interpreter mode *** 2017-07-21 16:04:32 gracefully (RE)spawned uWSGI master process (pid: 14323) 2017-07-21 16:04:32 spawned uWSGI worker 1 (pid: 14324, cores: 1)

I doubt that's going to work I'm afraid -- although we can use your virtualenv, the actual python that runs your web app will be a plugin to the web server's own python, so it won't be the one you recompiled...

Hi Harry,

Thank you for explaining about how the server uses it's own python, rather than my recompiled python.

You're right and that would explain things: When I tried using this new "sm17_ssl_110" virtualenv, it didn't work because the "gobiko.apns" module that I'd compiled with openssl v1.1, complained in the logs that it couldn't find the new "libssl.so.1.1". I think the python that is already loaded by the webserver has its library path already set to use the older openssl v1.0.1, and so its ssl module would use that v1.0.1

In the end, I'm using the python "subprocess" command to run my recompiled python as a subprocess to execute a short script to send the notifications. That should hopefully work until the servers are upgraded to ubuntu 16.04 with ssl v1.0.2 which has the ALPN.

import subprocess

home =  os.path.expanduser('~')

my_python = os.path.join(home, "my_python35/bin/python3")
script = os.path.join(settings.BASE_DIR, "send_iOS_notifications.py")

cmd = [ my_python, script, notification_id ]

# Set LD_LIBRARY_PATH so my_python finds ssl v1.1:
env = dict( os.environ, LD_LIBRARY_PATH=os.path.join(home, "ssl/lib") )

p = subprocess.Popen( cmd, env=env, stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True)

stdout, stderr = p.communicate(timeout=None)
# Check if p.returncode != 0 and parse stdout, stderr for results.

amazing workaround! :)