Most of us, the developers, and also some of small businesses are using Let's Encrypt to generate our own certificates instead of buying one.

I have been using Let's encrypt for years, and unfortunately, I was using the manual way of doing DNS challenge, meaning I have to manually renew the certificates once every three month.

I happened to create new certificates for my new website yesterday and I figured a new way to doing the certificates automatically, we don't even need to touch the DNS record, apply for both direct domain and wildcard domain. And it also automatically renew before the expiration date.

I will guide you how to do it, step by step. This guide will demo on Cloudflare, but it also applicable for other DNS providers like: Digital Ocean, DNS Simple, DNS Made Easy, Gehirn, Google, Linode, LuaDNS, NS1 NSONE, OVH, RFC2136, AWS Route 53, Sakura Cloud.

How it works

To be able to generate the certificate, we need to prove the ownership of the domain by doing "challenges", as defined by the ACME standard. There are four challenge types as describe as Let's Encrypt docs: http-01, dns-01, tls-sni-01, tls-alpn-01.

We will use dns-01 challenge since it won't effect any running webserver, and also it support to create wildcard certificate. So we can just need to create a certificate that is signed for domain.com and *.domain.com and that will cover any use cases of domain we would use.

Previously, I do it manually, that I have to login the DNS server and adjust TXT records as needed every time I want to issue new certificate.

But now, with DNS plugin, it will automates the process of completing a dns-01 challenge (DNS01) by creating, and subsequently removing, TXT records using the equivalent DNS API.

Step by step install

These steps assume you are using ubuntu server.

1.Install snapd

For ubuntu 18.04 and above, it is already installed in your system, for any other distros or version, follow the instruction here: install snapd

After install snap, ensure your version os snapd is up to date:

$ sudo snap install core; sudo snap refresh core

2.Remove certbot-auto and any certbot OS packages

$ sudo apt-get remove certbot

3.Install certbot

$ sudo snap install --classic certbot

Now, link command into the path so it can be run in the machine

$ sudo ln -s /snap/bin/certbot /usr/bin/certbot

4.Confirm plugin containment level

$ sudo snap set certbot trust-plugin-with-root=ok

5. Install the DNS plugin

$ sudo snap install certbot-dns-cloudflare

In this example, I used cloudflare, If you are not using cloudflare, please find your equivalent plugin name here: dns plugins

6.Set up the credential

We will need a cloudflare API token with Zone:DNS:Edit permission for only the domain you need certificates for.

  1. Access this link of Cloudflare or go to "Profile -> API Token"
  2. Click "Create API Token" and use "Edit Zone DNS" template
  3. Narrow down the permission on only specific domain
Setting token with DNS edit for a specific domain

4. Click "Continue to summary", confirm and grab your token

We will save this credential as a file, in order to pass through the certbot command. In my case, I will save it in ~/.secrets/cloudflare_domain.com.ini and file content as follow:

# Cloudflare API token used by Certbot
dns_cloudflare_api_token = <paste your api key here>

Grab and use the SSL certificates

Acquire the SSL certificate

I recommend to acquire a single certificate for your root domain (domain.com), and wildcard domain (*.domain.com) for all other sub domain.

Also I recommend to grab the certificate only so we can reuse and configure it whatever we want later.

To acquire a single certificate for both <domain>.com and *.<domain>.com

sudo certbot certonly \
  --dns-cloudflare \
  --dns-cloudflare-credentials ~/.secrets/cloudflare_domain.com.ini \
  --dns-cloudflare-propagation-seconds 60 \
  -d "<domain>.com" \
  -d "*.<domain>.com"

I set the propagation second to 60 to make sure the DNS record has time to effect.

You will need to enter the email address, and also agree to their Term of Service in the prompt and wait for it to process. The success output would look like this

Saving debug log to /var/log/letsencrypt/letsencrypt.log
Requesting a certificate for <redacted>.com and *.<redacted>.com
Waiting 60 seconds for DNS changes to propagate

Successfully received certificate.
Certificate is saved at: /etc/letsencrypt/live/<redacted>.com/fullchain.pem
Key is saved at:         /etc/letsencrypt/live/<redacted>.com/privkey.pem
This certificate expires on 2023-07-29.
These files will be updated when the certificate renews.
Certbot has set up a scheduled task to automatically renew this certificate in the background.

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
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
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

As you can see in the output, we have the key and certificate at /etc/letsencrypt/live/.com/privkey.pem and /etc/letsencrypt/live/.com/fullchain.pem

Alternative, if you want to generate certificate for single domain, you can run this command:

sudo certbot certonly \
  --dns-cloudflare \
  --dns-cloudflare-credentials ~/.secrets/cloudflare_domain.com.ini \
  --dns-cloudflare-propagation-seconds 60 \
  -d "<domain>.com"

Use the SSL certificates

This is how you would configure with nginx, because I signed for domain.com and *.domain.com so I can use this pair of certificate for my root domain and all sub-domain.

upstream ghost-backend {
    server 127.0.0.1:2368;
}


server {
    listen                  443 ssl http2;
    listen                  [::]:443 ssl http2;
    server_name             cms.example.com;

    # SSL
    ssl_certificate         /etc/letsencrypt/live/example.com/fullchain.pem;
    ssl_certificate_key     /etc/letsencrypt/live/example.com/privkey.pem;
    ssl_trusted_certificate /etc/letsencrypt/live/example.com/chain.pem;


    # logging
    access_log              /var/log/nginx/cms.example.com.access.log;
    error_log               /var/log/nginx/cms.example.com.error.log warn;

    # reverse proxy
    location / {
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header Host $http_host;
        proxy_pass http://ghost-backend;
    }
}


# HTTP redirect
server {
    listen      80;
    listen      [::]:80;
    server_name cms.example.com;

    location / {
        return 301 https://cms.example.com$request_uri;
    }
}

Just focus on ssl_certificate, ssl_certificate_key, ssl_trusted_certificate that is where the ssl certificates are used.

The same can be done with other web servers, if you are using Apache, HAProxy, etc you can insert the equivalent file as above.

Verify the auto renew process

When we use the dns plugin, they will update DNS record as the requirement without human interference. Certbot also configured the cron job to automatically renew the domain, now we need to make sure it is configured and working, by dry-run it.

$ sudo certbot renew --dry-run
Saving debug log to /var/log/letsencrypt/letsencrypt.log

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Processing /etc/letsencrypt/renewal/<redacted>.com.conf
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Account registered.
Simulating renewal of an existing certificate for <redacted>.com and *.<redacted>.com
Waiting 60 seconds for DNS changes to propagate

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Congratulations, all simulated renewals succeeded:
  /etc/letsencrypt/live/<redacted>.com/fullchain.pem (success)
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -