Skip to content

Use of Let's Encrypt to generate SSL certificates

Raymond Yee edited this page Feb 15, 2020 · 3 revisions

Overview and Option 1

Some notes as I'm working out how to use Let's Encrypt - Free SSL/TLS Certificates as a cert authority for opencontext servers.

An update on my latest work -- on branch https://github.com/rdhyee/open-context-py/tree/staging_prod_ssl (specifically https://github.com/rdhyee/open-context-py/tree/dc87af3911197350e8804dbf1fa33ab9a5f7fa89)

I have two different letsencrypt workflows that can possibly combined. When using vagrant/ansible to build staging using the opencontext_predb machine (https://github.com/rdhyee/open-context-py/blob/dc87af3911197350e8804dbf1fa33ab9a5f7fa89/sysadmin/Vagrantfile\#L105-L138 ), there are two paths -- so far I went with Option 2 -- so let me describe the first option here

Option 1 is: run nginx with the server key and certificate that I've put into the Github repo (https://github.com/rdhyee/open-context-py/tree/staging_prod_ssl/sysadmin/files/ssl-certs/opencontext_predb).  Originally, I had self-signed ssl certs that could serve as placeholders:

openssl genrsa -out server.key 2048
openssl req -new -key server.key -out server.csr -subj "/C=US/ST=California/L=San Francisco/O=The Alexandria Archive Institute/OU= /CN=*.opencontext.org/"
openssl x509 -req -days 999 -in server.csr -signkey server.key -out server.crt 

and

https://github.com/rdhyee/open-context-py/blob/staging_prod_ssl/sysadmin/build.yml#L466-L489

A self-signed SSL cert doesn't leave the browser that happy -- CSS and scripts don't load properly -- but there is encryption.  To fix the problem, we can then run certbot to generate a cert for this host (e.g., staging.opencontext.org) by running:

sudo certbot --agree-tos -m raymond.yee@gmail.com --no-eff-email --nginx -d staging.opencontext.org --no-redirect

The plus side is that should set this staging.opencontext.org ready to renew its cert in 90 days.  The possible downsides: 1) you have to run this step manually (which isn't a big burden) and 2) there is rate limit on the number of times you can renew a cert (5 times a week) (https://letsencrypt.org/docs/rate-limits/) --> which could be a problem if we're rebuilding staging.opencontext.org a lot (which is not a big problem.

Option 2 -- and one in use: creating wildcard certs on RY's mac and installing on all servers

It turns out it's possible to make wildcard certs with letsencrypt. That is, instead of using a self-signed cert, we makea wildcard cert that can be used for any opencontext.org

installing certbot on macOS

I have installed certbot on my Mac and downloaded the appropriate google credentials so that I can run certbot to create a wildcard cert.

First, to install certbot on macOS:

brew install letsencrypt

I also need the certbot-dns-google certbot plugin. How to install the plugin? I learned from ssl - How to install Certbot plugins? - DevOps Stack Exchange. Here's what happened the first time through....

$ which certbot
/usr/local/bin/certbot

$ head /usr/local/bin/certbot 
 #!/usr/local/Cellar/certbot/0.37.1_1/libexec/bin/python3.7

Then:

/usr/local/Cellar/certbot/0.37.1_1/libexec/bin/python3.7 -m pip install certbot-dns-google

(2020.02.14) It turns out that since I last ran certbot on my Mac, the homebrew version of Python was upgraded -- so now I have to reinstall the plugin.

$ head `which certbot`
#!/bin/bash
PYTHONPATH="/usr/local/Cellar/certbot/1.1.0/libexec/lib/python3.8/site-packages:/usr/local/Cellar/certbot/1.1.0/libexec/vendor/lib/python3.8/site-packages" exec "/usr/local/Cellar/certbot/1.1.0/libexec/bin/certbot" "$@"
$ head /usr/local/Cellar/certbot/1.1.0/libexec/bin/certbot

#!/usr/local/opt/python@3.8/bin/python3.8

I'm going to put certbot-dns-google into /usr/local/Cellar/certbot/1.1.0/libexec/vendor/lib/python3.8/site-packages/

PYTHONPATH="/usr/local/Cellar/certbot/1.1.0/libexec/lib/python3.8/site-packages:/usr/local/Cellar/certbot/1.1.0/libexec/vendor/lib/python3.8/site-packages" /usr/local/opt/python@3.8/bin/python3.8 -m pip install --target /usr/local/Cellar/certbot/1.1.0/libexec/vendor/lib/python3.8/site-packages/ certbot-dns-google

generating the certs

sudo certbot certonly \
  --dns-google \
  --dns-google-credentials /Volumes/ryvault1/opencontext/keys/opencontext-py-gce.json \
  --dns-google-propagation-seconds 120 \
  --agree-tos -m raymond.yee@gmail.com --no-eff-email \
  -d "*.opencontext.org" -d "opencontext.org"

When I ran certbot on 2019.10.04 (after having a cert already in place that was not near renewal time): I get a message like:

Saving debug log to /var/log/letsencrypt/letsencrypt.log
Plugins selected: Authenticator dns-google, Installer None
Cert not yet due for renewal

You have an existing certificate that has exactly the same domains or certificate name you requested and isn't close to expiry.
(ref: /etc/letsencrypt/renewal/opencontext.org.conf)

What would you like to do?
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1: Keep the existing certificate for now
2: Renew & replace the cert (limit ~5 per 7 days)
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Select the appropriate number [1-2] then [enter] (press 'c' to cancel): 2
Renewing an existing certificate

IMPORTANT NOTES:
 - Congratulations! Your certificate and chain have been saved at:
   /etc/letsencrypt/live/opencontext.org/fullchain.pem
   Your key file has been saved at:
   /etc/letsencrypt/live/opencontext.org/privkey.pem
   Your cert will expire on 2020-01-02. To obtain a new or tweaked
   version of this certificate in the future, simply run certbot
   again. To non-interactively renew *all* of your certificates, run
   "certbot renew"
 - If you like Certbot, please consider supporting our work by:

   Donating to ISRG / Let's Encrypt:   https://letsencrypt.org/donate
   Donating to EFF:                    https://eff.org/donate-le

Copying new certs to running servers

Let me copy the new cert to staging and restart nginx

Borrowing a trick from scp from stdin – /dev/blog -- on my Mac, I ran [oc-staging is a configuration in my .ssh config file] to copy the new certs to the staging server which is already running:

 # the certs are not visible to my login account -- hence use of sudo -- and the need to type in password

sudo cat /etc/letsencrypt/live/opencontext.org/fullchain.pem | ssh oc-staging "sudo cat >/opt/ocweb/certs/server.crt"


sudo cat /etc/letsencrypt/live/opencontext.org/privkey.pem | ssh oc-staging "sudo cat >/opt/ocweb/certs/server.key"

ssh oc-staging sudo /etc/init.d/nginx restart

Saving encrypted versions of new certs to github repo

Some incantations to encrypt the certs using ansible-vault. I made use of an encrypted partition on my Mac (/Volumes/ryvault1) so that I never expose the keys as non-root accessible (a bit of overkill perhaps):

sudo cp /etc/letsencrypt/live/opencontext.org/fullchain.pem /Volumes/ryvault1/tmp/server.crt

sudo cp /etc/letsencrypt/live/opencontext.org/privkey.pem /Volumes/ryvault1/tmp/server.key

sudo chown raymondyee /Volumes/ryvault1/tmp/server.crt
sudo chown raymondyee /Volumes/ryvault1/tmp/server.key

ansible-vault encrypt --encrypt-vault-id=oc /Volumes/ryvault1/tmp/server.crt

ansible-vault encrypt --encrypt-vault-id=oc /Volumes/ryvault1/tmp/server.key

mv /Volumes/ryvault1/tmp/server.crt ~/C/src/open-context-py/sysadmin/files/ssl-certs/opencontext_predb/server.crt

mv /Volumes/ryvault1/tmp/server.key ~/C/src/open-context-py/sysadmin/files/ssl-certs/opencontext_predb/server.key