Deploying Let’s Encrypt on an Amazon Linux AMI EC2 Instance

Are you experiencing migraines trying to install Let’s Encrypt on your Amazon Linux AMI EC2 Instance?


You’re not alone!

The Anticipation
Let’s Encrypt announced its public beta on a bitter cold day in December, 2015. I was attending the inaugural WordCamp US in Philadelphia, PA alongside about 1,800 peers whose personal livelihoods are built on the internet. The mood was electric. Google had been signaling that they favor HTTPS sites in search rankings for some time. “Finally, free SSL Certificates for all!” presenters throughout the morning enthusiastically exclaimed. By lunchtime dozens of developers had collected in the hallways, fervently entering commands into their terminal windows, all racing to implement this divine gift bestowed upon the world by an obscure acronym.

The Reality
As the day wore on I inquisitively approached a group of people I overheard discussing Let’s Encrypt. The consensus? Not one of them had successfully generated a valid SSL certificate for their site using the Let’s Encrypt software. A rather disheartening inception indeed! Not to be deterred, over the following months I gathered everything I could find about implementing Let’s Encrypt SSL certificate generation and management on my own AWS EC2 instance running Amazon Linux AMI and Apache. What follows is my process, and since there’s currently very little documentation of this niche implementation online if this saves just one other person some grief then the time spent will have been worth it.

Blindly follow me into the promised land!

Shameful admission: I’m adapting this documentation from step-by-step notes taken during my own install over three months ago, so please excuse any hazy details and certainly leave feedback if anything is unclear so I can make improvements!

(In)Compatibility with Amazon Linux AMI
At the time of writing the Let’s Encrypt Certbot still isn’t technically supported for deployment on the Amazon AMI flavor of Linux. It still works, though*! At this point it’s also worth it to note that Amazon itself also offers a free service, although I haven’t tried it because as I always say: “with just a little coffee and foolhardy perseverance you too can start generating and managing SSL Certificates yourself, for free!” Plus, think of the bragging rights.

* As long as you use the--debug option on all commands

So without further ado…


The Install

1. Install Python:

$ yum install python27-devel git

2. Install Let’s Encrypt by cloning the github repository into /opt/letsencrypt and running the Let’s Encrypt installer:

$ git clone https://github.com/letsencrypt/letsencrypt /opt/letsencrypt
$ /opt/letsencrypt/letsencrypt-auto --debug

2a. If you’re running Amazon Linux 2 on your EC2 follow these additional steps (thanks @andrenakkurt!) before continuing.

3. Make a configuration file (/etc/letsencrypt/config.ini) that will be used to sign all future certificates and renewals with your private key and email address:

$ echo "rsa-key-size = 4096" >> /etc/letsencrypt/config.ini
$ echo "email = ________@____.com" >> /etc/letsencrypt/config.ini

Certificate Generation

1. Request a certificate the naked domain (_______.com) and www subdomain (www._______.com), using a “secret file” generated in a directory (.well-known) in your website’s root folder (/var/www/_______)

$ /opt/letsencrypt/letsencrypt-auto certonly --debug --webroot -w /var/www/_______ -d _______.com -d www._______.com --config /etc/letsencrypt/config.ini --agree-tos 

Certificate files (cert.pem, chain.pem, fullchain.pem, and privkey.pem) are generated in an individual folder for each domain in /etc/letsencrypt/live/ (e.g. /etc/letsencrypt/live/_______.com/ )

  • cert.pem: server certificate only.
  • chain.pem: root and intermediate certificates only.
  • fullchain.pem: combination of server, root and intermediate certificates (replaces cert.pem and chain.pem).
  • privkey.pem: private key (do not share this with anyone!).

Take a note of your expiration date and other important information displayed on the confirmation screen.

2. Remove the now-empty “secret file” directory, if desired (for cleanliness)

$ rmdir /var/www/______/.well-known

Certificate Renewal — Manual

Let’s Encrypt includes a script to check and renew your certificates:

/opt/letsencrypt/letsencrypt-auto --no-bootstrap renew

Troubleshooting:

One common issue experienced on Amazon Linux AMI is receiving the following error:

Traceback (most recent call last):
File "/usr/local/bin/virtualenv", line 7, in <module>
from virtualenv import main
ImportError: No module named virtualenv

…or similarly:

/opt/letsencrypt/letsencrypt-auto: line 666: virtualenv: command not found

To resolve this, install/reinstall the python package “virtualenv” using PIP

$ sudo easy_install pip
$ sudo pip install virtualenv

Certificate Renewal — Automated

The easiest way to automate the above certificate renewal process is to add it to your cron process.

$ sudo nano /etc/crontab

Enter the following for a once-a-day check. Make sure you also refresh Apache, newly renewed certificates won’t be recognized otherwise!

# Renew SSL Certs
0 13 * * * ec2-user /opt/letsencrypt/letsencrypt-auto --no-bootstrap renew
# Refresh Server
10 13 * * * root apachectl -k restart > /dev/null 2>&1

If your certificates fail to renew automatically see “Troubleshooting” under Certificate Renewal — Manual, many times it’s the virtualenv package failing to execute during the auto-renew process as well, a reinstall of that package should fix it.

Certificate Implementation

Adding your newly generated SSL Certificates to your website just requires making some additions to your virtual host configuration files.

$ sudo nano /etc/httpd/conf.d/_____.com.conf

If you want to force SSL (a good idea) add the following to the TOP of the config file before anything else:

# Force SSL
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteCond %{HTTPS} !=on
RewriteRule ^/?(.*) https://%{SERVER_NAME}/$1 [R=301,L]
</IfModule>

Then add the port 443 (SSL) configuration below:

Listen 443
<VirtualHost *:443>
ServerAdmin ________@____.com
DocumentRoot /var/www/_______.com
ServerName _______.com
ServerAlias www._______.com
     # Logs
ErrorLog /var/www/logs/_______.com-ssl_error_log
CustomLog /var/www/logs/_______.com-ssl_access_log common
     # Certificates
SSLEngine on
SSLCertificateFile /etc/letsencrypt/live/___.com/cert.pem
SSLCertificateKeyFile /etc/letsencrypt/live/___.com/privkey.pem
SSLCertificateChainFile /etc/letsencrypt/live/___.com/chain.pem
SSLProtocol All -SSLv2 -SSLv3
SSLHonorCipherOrder on
SSLCipherSuite "EECDH+ECDSA+AESGCM EECDH+aRSA+AESGCM EECDH+ECDSA+SHA384 EECDH+ECDSA+SHA256 EECDH+aRSA+SHA384 EECDH+aRSA+SHA256 EECDH+aRSA+RC4 EECDH EDH+aRSA !RC4 !aNULL !eNULL !LOW !3DES !MD5 !EXP !PSK !SRP !DSS"
</VirtualHost>

Refresh Apache for the changes to take effect:

$ sudo apachectl graceful

Certificate Validity Audit

Here are a couple of methods to use to ensure your new certificate is configured properly and to check an existing certificate’s expiry date:

HTTP response:

$ curl -sIv https://_______.com |& grep expire

Direct file read:

$ sudo openssl x509 -noout -dates -in /etc/letsencrypt/live/_______.com/cert.pem

The following online tools are indispensable for checking and debugging invalid certificate issues:

…and that’s it!


Further Reading

My own journey would have been impossible without the sages who blazed the untamed trail before me, to which I owe my sincere gratitude: