Configure HTTPs
There are two strategies we discuss here to get https. The first is for development, meaning you can make a faux https certificate to test it locally, and the second is for production.
Faux https
We are going to be using FiloSottile/mkcert for this case. You can following the instructions in the README to generate your “certificates.” You will need Go installed on your system first. If you want to install from a package manager, there are instructions in the repository README.
git clone https://github.com/FiloSottile/mkcert
cd mkcert
go build
I also needed to install:
$ sudo apt install libnss3-tools
So the certificates could be installed to my browsers.
This places mkcert
in your current working directory!
$ ./mkcert -h
Usage of mkcert:
$ mkcert -install
Install the local CA in the system trust store.
$ mkcert example.org
Generate "example.org.pem" and "example.org-key.pem".
$ mkcert example.com myapp.dev localhost 127.0.0.1 ::1
Generate "example.com+4.pem" and "example.com+4-key.pem".
$ mkcert "*.example.it"
Generate "_wildcard.example.it.pem" and "_wildcard.example.it-key.pem".
$ mkcert -uninstall
Uninstall the local CA (but do not delete it).
For more options, run "mkcert -help".
I then did:
$ ./mkcert -install
The local CA is already installed in the system trust store! 👍
The local CA is now installed in the Firefox and/or Chrome/Chromium trust store (requires browser restart)! 🦊
Let’s pretend we are generating a certificate for singularity-registry.org.
./mkcert singularity-registry.org "*.singularity-registry.org" singularity-registry.test localhost 127.0.0.1 ::1
Created a new certificate valid for the following names 📜
- "singularity-registry.org"
- "*.singularity-registry.org"
- "singularity-registry.test"
- "localhost"
- "127.0.0.1"
- "::1"
Reminder: X.509 wildcards only go one level deep, so this won't match a.b.singularity-registry.org ℹ️
The certificate is at "./singularity-registry.org+5.pem" and the key at "./singularity-registry.org+5-key.pem" ✅
It will expire on 29 August 2023 🗓
Then I moved them into the registry root, and updated my shub/settings/config.py to use https on localhost.
DOMAIN_NAME = "https://127.0.0.1"
DOMAIN_NAME_HTTP = "https://127.0.0.1"
DOMAIN_NAKED = DOMAIN_NAME_HTTP.replace("https://", "")
Finally, we need to make sure that we are using the docker compose file for https, the nginx.conf for https, and that the certificates are correctly bound.
mv docker compose.yml docker compose.yml.http
mv https/docker compose.yml .
mv nginx.conf nginx.conf http
mv https/nginx.conf.https nginx.conf
In the docker compose.yml that is newly copied, change the binds of the paths to use the files in your present working directory.
nginx:
restart: always
image: ghcr.io/singularityhub/sregistry_nginx
ports:
- "80:80"
- "443:443"
volumes:
- ./nginx.conf:/etc/nginx/conf.d/default.conf:ro
- ./uwsgi_params.par:/etc/nginx/uwsgi_params.par:ro
- ./singularity-registry.org+5-key.pem:/code/domain.key
- ./singularity-registry.org+5.pem:/code/cert.pem
# - /etc/ssl/certs:/etc/ssl/certs:ro
# - /etc/ssl/private:/etc/ssl/private:ro
We’re almost done! In the newly moved nginx.conf, make sure to comment out the dhparam.pem, the old certificate paths, and add the new ones we just created:
ssl on;
# ssl_certificate /etc/ssl/certs/chained.pem;
# ssl_certificate_key /etc/ssl/private/domain.key;
ssl_certificate /code/cert.pem;
ssl_certificate_key /code/domain.key;
ssl_session_timeout 5m;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_ciphers ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-SHA:ECDHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA;
ssl_session_cache shared:SSL:50m;
# ssl_dhparam /etc/ssl/certs/dhparam.pem;
ssl_prefer_server_ciphers on;
You can then do docker compose up -d
. As instructed by the tool, we probably
need to restart our browsers.
docker compose up -d
And amazingly (I opened Chrome freshly) my site has https!
Once you are done, you can (along with deleting the certs you created) you can uninstall mkcert and delete the certs you created:
$ ./mkcert -uninstall
...
The local CA is now uninstalled from the system trust store(s)! 👋
You can see where the root certs were located:
$ ./mkcert -CAROOT
/home/vanessa/.local/share/mkcert
And remove them:
rm -rf /home/vanessa/.local/share/mkcert/
I also deleted the mkcert directory and the original certificates, and undid the changes above to settings, the docker compose and nginx.conf files
Production https
Get a hostname
Recall that the first step to get https is to have a hostname. You can use Google
Domains, or you can also create an account on https://www.dynu.com (It’s free).
Log into your account and under the Control Panel go to DDNS Services. On the next page, click the + Add button, and fill out the Host and Top Level fields under Option 1 using whatever you like.
This will be how users access your server (e.g., sregistry.dynu.net)
. Click + Add.
- On the next page, change the IPv4 Address to the IP address for your droplet. Change the TTL to 60. Click Save.
- With a few minutes, you should be able to access your server using that hostname.
Test Nginx
In the install script, we installed nginx. Now, you merely need to start it (it might already be started).
$ sudo service nginx start
For this next step, we are still working on the host where you will run your container. What we first need to do is generate certificates, start a local web server, and ping “Let’s Encrypt” to verify that we own the server, and then sign the certificates.
SSL Certificates
We’ll use “certbot” to install and renew certificates.
Step 1. Set some variables
First we’ll set some variables that are used in later steps.
EMAIL="youremail@yourdomain.com"
DOMAIN="containers.page"
The email you set here will be used to send you renewal reminders at 20 days, 10 days, and 1 day before expiry (super helpful!)
Step 2. Install certbot
Certbot automates certificate generation and renewal. In other words, it makes it really easy to setup SSL.
sudo add-apt-repository ppa:certbot/certbot
sudo apt-get update
sudo apt-get install python-certbot-nginx
Step 3. Get certificates with certbot
Now obtain a certificate by running this command. Note that if you aren’t using a container, or you aren’t the root user, you might need to add sudo
.
certbot certonly --nginx -d "${DOMAIN}" -d "www.${DOMAIN}" --email "${EMAIL}" --agree-tos --redirect
Equivalently, if your domain doesn’t have www.
you can remove the second -d
argument.
Step 4. Stop nginx
Now we need to stop nginx because we have what we need from it!
sudo service nginx stop
Step 5. Copy certs to a new location
Now we’ll move the certs to where they’re expected later.
sudo cp /etc/letsencrypt/live/$DOMAIN/fullchain.pem /etc/ssl/certs/chained.pem
sudo cp /etc/letsencrypt/live/$DOMAIN/privkey.pem /etc/ssl/private/domain.key
sudo cp /etc/letsencrypt/ssl-dhparams.pem /etc/ssl/certs/dhparam.pem
Step 6. Renewal (and remembering to renew!)
Certificates expire after 90 days. You’ll get reminders at 20 days, 10 days, and 1 day before expiry to the email you set before. Before the cert expires, you can run this command to renew:
sudo certbot renew
Important!
Before renewing you need to stop the docker container running expfactory and start nginx outside of docker.
The commands to stop the nginx container and renew the certificates might look like this (this is for typical Ubuntu or similar).
docker compose stop nginx
sudo service nginx start
sudo certbot renew
sudo service nginx stop
docker compose start nginx
And then issue the command to start your container.
Importantly, when you start the container (that will be generated in the next steps) you will need to bind to these files on the host, and expose ports 80 and 443 too.