Python, NGINX, and HTTPS

Posted on September 11, 2019

In this article I'm going to try to remember what I did to get my computer to talk to a Python app over HTTPS hosted on my Pi.

First, we create a certificate authority:

openssl genrsa -des3 -out myCA.key 2048

This creates an RSA key with des3 encryption and puts it in a myCA.key file. The key is 2048 bits long.

Next up is the actual certificate:

openssl req -x509 -new -nodes -key myCA.key -sha256 -days 1825 -out myCA.pem

You'll be prompted for a password to verify a certificate, go ahead and provide a secure one. My understanding is that the -x509 indicates a signing request, I'm not sure what the -nodes flag does, but everything else is pretty straightforward. -sha256 is the hashing algorithm (I believe there are others), and -days 1825 indicates that this key will expire in 5 years.

Now we generate a private key to sign our requests with:

openssl genrsa -out example.com.key 2048

This is almost the same as generating the authority. The difference is, this key will be used to sign the certificates we generate, the first key was just used to generate the authority's certificate.

Now we create the request:

openssl req -new -key example.com.key -out example.com.csr

Next we want to create a certificate. To get it to jive with browsers properly, we need a bit of configuration. I created a config file in a directory beside the one holding my CA files, just to keep them separate. I named the file example.com.ext and filled it with the following contents:

authorityKeyIdentifier=keyid,issuer
basicConstraints=CA:FALSE
keyUsage=digitalSignature,nonRepudiation,keyEncipherment,dataEncipherment
subjectAltName=@alt_names
[alt_names]
DNS.1 = example.com
IP.1 = <IP OF PI>

For basic constraints, we want CA:FALSE because the certificates are not self-signed. we're using an authority to do the signing. Key usage is what this key can conceivable be used for. I was pretty liberal with my usage, I think I'll try trimming it down at some point.

Often times, you will want a key to work for multiple domains or IP addresses, so I pointed the subjectAltName to a section named alt_names defined below it. In the alt_names section, we have the domain we want this token to validate, as well as an IP address. Since I'm hosting my Pi on my local network, I only have the IP field filled out.

With the configuration file complete, we can create a certificate:

openssl x509 -req -in example.com.csr -CA /path/to/myCA.pem -CAkey /path/to/myCA.key -CAcreateserial -out example.com.crt -days 1825 -sha256 -extfile example.com.ext

As I mentioned earlier, I put my CA files in a separate folder, hence the path/to file structure. For components, we gave the command our request file, our certificate authority, the authority's key, and the output certificate file. I set the lifetime to be 5 years again and gave it the same hashing algorithm as with the other requests. At the end there is our configuration file.

Now we need to configure NGINX. In /etc/nginx/sites-enabled/ I made a file called central-command. Here are its contents::

server {
       listen 443 http2 ssl;
       listen [::]:443 http ssl;
       server_name 192.168.0.31;

       ssl on;
       ssl_certificate /home/server/Data/ssl/private/central-command.crt;
       ssl_certificate_key /home/server/Data/ssl/private/central-command.key;

       location / {
                include uwsgi_params;
                uwsgi_pass unix:/tmp/security_token_service.sock;
       }
}

Now, we're using uwsgi to interface between Python and Nginx. This is faster than making NGINX translate.