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
npmand 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:
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):
Next we need to set an environmental variable equal to this location:
Then we create the root certificate and key:
This will create two files in the
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 firstname.lastname@example.org (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
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.
package.json in your React app, add the following line to the
"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:
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
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.
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!