Software Development

HTTPS For Local Development With React, Django, uWSGI & Docker

Lizard on red

I’m building an MVP for a SaaS application that will later be deployed to the cloud (AWS in this case). By default, most local development setups serve (insecure) HTTP connections, but production applications are (or should be) deployed using HTTPS. I wanted to understand and mitigate any issues with HTTPS before deployment, so I decided to set up my local development environment to also use HTTPS instead of HTTP.

The application consists of the following:

  • React front end created using create-react-app, run locally using npm and deployed in production from an S3 bucket.
  • Django Rest Framework back end, served by uWSGI, running locally in a docker container using docker-compose and deployed in production in ECS.

My local development setup is as follows:

Certificate Creation

I’m not going to get into the details of TLS/SSL Certificates, Certificate Authorities, etc. but let’s just say it can be challenging to create a self-signed certificate that your browser will trust.

Luckily there is a great tool written by Filippo Valsorda that will do this for you- mkcert, available at https://github.com/FiloSottile/mkcert– download and install it to proceed.

First we need to create a directory to store our certificates in (you can use the default if you want, I prefer something in my dev directory):

mkdir /Users/christopher/Desktop/Dev/workspace/ssl

Next we need to set an environmental variable equal to this location:

export CAROOT='/Users/christopher/Desktop/Dev/workspace/ssl'

Then we create the root certificate and key:

mkcert -install

This will create two files in the CAROOT directory: rootCA.pem and rootCA-key.pem. It will also add the root certificate into the Mac’s keychain- you can verify this by running the /Applications/Utilities/Keychain Access application and searching for a certificate named mkcert username@hostname.local (substitute your username and hostname/machinename).

Finally we create the certificates:

mkcert localhost 127.0.0.1 ::1

This will create two more files in the CAROOT directory: localhost+2.pem and localhost+2-key.pem, these are the certificate and the private key, respectively.

Front End Setup

Two small changes are all that is necessary to get the front end working.

First, in package.json in your React app, add the following line to the scripts section:

"prestart": "rm ./node_modules/webpack-dev-server/ssl/server.pem && cat /Users/christopher/Desktop/Dev/workspace/ssl/localhost+2-key.pem /Users/christopher/Desktop/Dev/workspace/ssl/localhost+2.pem > ./node_modules/webpack-dev-server/ssl/server.pem",

What this does is replace the default certificate included with webpack-dev-server with the newly created (and trusted) local certificate.

webpack-dev-server expects a single PEM formatted file, which includes both the certificate and the private key, so we simple cat the new key and certificate together to create this single file.

Second, also in package.json, update the start line in the scripts section to read:

"start": "HTTPS=true PORT=3443 react-scripts start",

You can change the port to whatever you want to use.

That’s it- start your dev server, navigate to https://localhost:3443/ and you’re done!

Adding CORS Support To Django

If your back end server is Django, you also need to allow CORS (Cross-Origin Resource Sharing). There is a package that allows this to be done quite easily- django-cors-headers. Add it to your pip requirements file or install it manually using pip --install django-cors-headers.

Then update your Django settings file as follows:

INSTALLED_APPS = [
  ...
  'corsheaders',
  ...
]

MIDDLEWARE = [
  'corsheaders.middleware.CorsMiddleware',
  ...
]

CORS_ALLOW_CREDENTIALS = True
CORS_ORIGIN_ALLOW_ALL = False
CORS_ORIGIN_WHITELIST = (
  'localhost:3443',
)

from corsheaders.defaults import default_headers

CORS_ALLOW_HEADERS = default_headers + (
  'access-control-allow-credentials',
  'access-control-allow-origin',
  'access-control-expose-headers',
)

Back End Setup

The backend requires several changes:

First, create an ssl directory within the build context of your Docker app. In my case this would be:

mkdir /Users/christopher/Desktop/Dev/workspace/backend/ssl

Next, copy the certificates created in the first section into this directory:

cp /Users/christopher/Desktop/Dev/ssl/localhost* /Users/christopher/Desktop/Dev/workspace/backend/ssl/

Add the following to the Dockerfile for your back end service:

RUN mkdir /project/ssl

Integrate the following into your Docker compose file (I use the YAML format and my back end service is called web):

services:
  web:
    ports:
      - "8443:8443"
    volumes:
      - ./ssl:/project/ssl

Update uwsgi.ini to serve https and to use these certificates:

https2 = addr==0,cert=/project/ssl/localhost+2.pem,key=/project/ssl/localhost+2-key.pem,spdy=1
shared-socket = 0.0.0.0:8443

Re-build your project and bring it up and you should be done:

docker-compose build --no-cache ; docker-compose up

You will need to adjust the above steps to according to the name of your back end service, the port you use and your project directory layout.

Conclusion

I can think of a few potential problems here- both the front end and back end are using the same certificates, although I’m not sure if that matters in this case. I’d also like to automate the copying of the certificates for the back end into the Docker build process- if I tackle that I’ll post an update.

Any comments, suggestions or corrections welcome!


Photo by Pierre Bamin on Unsplash because I like lizards.

1 Comment

Leave a Reply

Your email address will not be published.