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 usingnpm
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:
- Docker Desktop for Mac
- MacOS 10.14
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.
This is a comment