Requesting a valid certificate
Requesting a valid certificate has never been so easy. Let's Encrypt offers free, easy renewable, 90 days certificates. The process acquiring these is trivial and takes about 5 minutes of your time. All you need to do, is download the client, have access to your DNS server or have a working public website.
Let's Encrypt does have limits. You can request up to 20 certificates per domain name per week. You can surpass this limit by adding up to 100 names per certificate effectively giving you 2000 subdomains. You can then split these up. You can request up to 500 domains per week (and some IP limitations.) ISP's can request exemption/leverage limits. This process takes a while. When using cloud-resources, use durable storage to facilitate re-distribution of certificates (e.g., S3+KMS and service roles in the case of AWS)
Getting the base-client without plugins
Installing certbot using OS-specific package managers
apt-get install certbot
yum install certbot
Installing the client with self-update/install feature
mkdir /opt && cd /opt
wget https://dl.eff.org/certbot-auto
chmod a+x certbot-auto
Installing the base-client and AWS Route53 plugin using pip
pip install certbot
pip install certbot-dns-route53
Request Examples
DNS-01 Example
pip install certbot
pip install certbot-dns-route53
Run from any system e.g., your server, desktop:
./certbot-auto -d some.domain -d www.some.domain --manual --preferred-challenges dns certonly
The program will ask you to add a TXT record to your DNS servers. As soon as the record is added to your DNS, hit enter to continue. Let's Encrypt will verify that the record exists and due to the handshake and contents; know it can provide you with a certificate and private key for you to re-use and implement.
Public Standalone Example
In the case of no available webserver, you can run:
sudo ./certbot-auto --standalone -d some.domain -d www.some.domain
You can add additional names by adding "-d some.domain"
TLS-01 or HTTP-01 Example (auto-renewable)
In case of nginx or apache you will need to add a location to your server configuration pointing to a writeable location. The most basic example is:
location /.well-known/acme-challenge {
default_type "text/plain";
root /var/www/letsencrypt/html;
}
/etc/letsencrypt/cli.ini
rsa-key-size = 4096
email = some@mail.net
authenticator = webroot
webroot-path = /var/www/letsencrypt/html
text = True
agree-tos = True
#renew-by-default = True #Set this to force renew
When the configuration is in place, don't forget to reload/restart the webserver. You can now tell letsencrypt to verify ownership and validity, by running
./certbot-auto certonly -d some.domain -d www.some.domain
Route53 Example
Ensure that you have functioning Boto3 credentials with access to Route53 on your system in either the IAM instance-role attached to your system, your .aws/* files or using EXPORT-values.
Instance Role - Attached Policy Example
Yaml
- Effect: Allow
Sid: "ReadAllDomainDNSInfo"
Action:
- route53:List*
- route53:Get*
Resource:
"*"
- Effect: Allow
Sid: "WriteRightsToEntireZoneForAllRR"
Action:
- route53:ChangeResourceRecordSets
Resource:
"arn:aws:route53:::hostedzone/XXXXXXXXXXXXXX"
Json
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "ReadAllDomainDNSInfo",
"Effect": "Allow",
"Action": [
"route53:List*",
"route53:Get*"
],
"Resource": "*"
},
{
"Sid": "WriteRightsToEntireZoneForAllRR",
"Effect": "Allow",
"Action": [
"route53:ChangeResourceRecordSets"
],
"Resource": "arn:aws:route53:::hostedzone/XXXXXXXXXXXXXX"
}
]
}
These policies will give your instance all/"enough" rights to change any record in the hosted zone. Unfortunately, you cannot add more granular rights to route53. Ideally you would want to only give access to a specific subdomain/txt-record. This is not possible in IAM/route53. As a security boundary I added the route53:ChangeResourceRecordSets
, Resource part to limit it to only 1 hosted zone. You could split up your zones by delegating subdomains to another hosted zone and give the machines access to those instead. The same principle for team-sub-domains applies.
Then run from any system e.g., your server, desktop:
certbot certonly -n --agree-tos --dns-route53 --email some@email.net -d some.domain -c route53.ini
I usually create a specific Route53 ini file to make sure all settings are loaded correctly.
rsa-key-size = 4096 email = some@email.net authenticator = dns-route53 text = True agree-tos = True #renew-by-default = True
The plugin will generate a TXT record for you in your Route53 Public Hosted Zone to facilitate automatic validation of the certificate request. If you don't have credentials on your machine, you can do this manually by performing the DNS-01 instructions.
Renewing certificates
Renewal of certificates is done using the client. This is not automated by default.
certbot renew
Renewing certificates does normally not add to your counter-limit.
Automated renewal using CRON-daemon
crontab -e
0 0 1 * * /opt/certbot-auto renew && service nginx reload
This works for all non-manual renewals. The client will throw warnings and errors in case of manual steps.
Default behaviour is to only renew if the certificate has less then 30 days left. Uncommenting the line "renew-by-default" changes this behaviour by renewing all certificates.
You can change the amount of days a certificate has to have left before renewing, in the per domain-conf/ini file which can be found in the /etc/letsencrypt/renewal directory.
Wildcard Certificates
Please do not use wildcard certificates. Simply generate a regularly named certificate instead so you don't re-use your private key everywhere
Wildcard certificates are the most misused certificates. They give too much access and in the case of loss, or hi-jacked other services, they give the perpetrator the ability to hi-jack connections or identify using existing infrastructure. It is, therefore, ill advised using wildcard certificates in general, especially since getting certificates per resource is such a trivial matter.
Wildcard certificates are readily available using the ACMEv2 endpoint. To create a wildcard certificate follow any of the instructions using DNS-01 (e.g. DNS-01 or route53 plugin) and add:
--server https://acme-v02.api.letsencrypt.org/directory
to the command line. In other words it will look something like this:
certbot certonly -n --agree-tos --dns-route53 --email some@email.net -d *.some.domain -c route53.ini --server https://acme-v02.api.letsencrypt.org/directory
The process will generate a new account for the new ACMEv02 api. Any previous limits etc are henceforth not available, and you will need to re-request these at this time.
Implementing the certificate
Now that you have a certificate, you can start implementing it.
Certificate Details
Let's Encrypt certificates expire after 90 days. The certificates will be stored in /etc/letsencrypt/archive/ plus a symbolic link in /etc/letsencrypt/live/ by default.
When using auto-scaling-groups and/or deploy-pipelines, store the certificate and configuration on safe and durable storage (like kms encrypted S3 plus encrypt the file) and download and update them from there, instead of requesting a new certificate every time. This due to account limits.
Nginx
To get started, you should add a few basic lines to your config file
server {
include /etc/nginx/conf.d/default_listen.conf;
server_name some.domain* www.some.domain*;
return 301 https://$host$request_uri;
}
server {
server_name some.domain* www.some.domain*;
root /var/www/cdns/html;
access_log /var/log/nginx/some.domainaccess.log;
error_log /var/log/nginx/some.domainerror.log;
ssl_certificate /etc/letsencrypt/live/some.domain/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/some.domain/privkey.pem;
include /etc/nginx/conf.d/letsencrypt.conf;
include /etc/nginx/conf.d/default_site.conf;
include /etc/nginx/conf.d/default_ssl.conf;
include /etc/nginx/conf.d/default_php7.conf;
return 301 https://some.domain$request_uri;
}
server {
server_name some.domain www.some.domain;
root /var/www/cdns/html;
access_log /var/log/nginx/some.domainaccess.log;
error_log /var/log/nginx/some.domainerror.log;
ssl_certificate /etc/letsencrypt/live/some.domain/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/some.domain/privkey.pem;
include /etc/nginx/conf.d/letsencrypt.conf;
include /etc/nginx/conf.d/default_site.conf;
include /etc/nginx/conf.d/default_ssl.conf;
include /etc/nginx/conf.d/default_php7.conf;
}
My default SSL Settings, ciphers and code: /etc/nginx/conf.d/default_ssl.conf;
# Default SSL settings
ssl_dhparam /etc/nginx/ssl/dh4k.pem;
# ssl_session_cache builtin:1000 shared:SSL:2m;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 5m;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_prefer_server_ciphers on;
# ssl_ecdh_curve secp384r1; # Choose your curve
# ssl_ecdh_curve secp521r1; # Pre- OpenSSL 1.1 can't do both
ssl_ecdh_curve secp521r1:secp384r1;
ssl_ciphers ECDHE-RSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305-D:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-ECDSA-CHACHA20-POLY1305-D:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-SHA384:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES256-SHA256:ECDHE-RSA-AES256-SHA:DHE-RSA-AES256-SHA:DHE-RSA-CAMELLIA256-SHA:ECDHE-RSA-AES256-SHA:DHE-RSA-AES256-SHA:DHE-RSA-AES256-SHA:DHE-RSA-CAMELLIA256-SHA:ECDH-RSA-AES256-SHA;
ssl_stapling on;
ssl_stapling_verify on;
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload";
resolver 8.8.8.8 8.8.4.4;
This code-snippet will give you a 100/100/100/100 on https://ssllabs.com/ssltest/; a tool with which you can measure your resiliency and settings. You should have an a A+. Having 100/100/100/100 will limit access to your site.
This example will add HSTS to your website making unencrypted access "impossible" by browsers and some cli, for this location and all other subdomains for 31536000 seconds.
Mozilla Config Generator https://mozilla.github.io/server-side-tls/ssl-config-generator/ is your friend for making ssl/tls configuration files for: Apache, Nginx, Lighttpd, HAProxy and AWS ALB (terminate only).
To generate dh4k.pem run:
openssl dhparam -out /etc/nginx/ssl/dh4k.pem 4096
Tomcat
Tomcat Configuration For more info: https://tomcat.apache.org/tomcat-7.0-doc/ssl-howto.html
Alternative Serverless approach: AWS Lambda & CloudWatch
I created a serverless deploy and implemented a slightly adjusted - forked - version, which can be found here: https://github.com/AngeliqueDawnbringer/node-acme-lambda