Ubiquitous Encryption: Let's Encrypt

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

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

Author: Angelique Dawnbringer Published: 2015-09-11 09:06:26 Keywords:
  • Ubiquitous Encryption
  • Let's Encrypt
  • Certificate
  • Renewal
Modified: 2019-06-05 16:06:04