Let's Encrypt Server Certificate via DNS Challenge
Tags: certificate, bash, dns
Published: 2019-01-31 02:29:00

---
title: Let's Encrypt Server Certificate via DNS Challenge
published: true
description: How to obtain a Let's Encrypt certificate through a DNS Challenge.
cover_image: https://res.cloudinary.com/michaeldscherr/image/upload/v1549059099/Dev.to/Posts/Let%27s%20Encrypt%20Server%20Certificate%20DNS%20Challenge/img-banner.jpg
tags: certificate, bash, dns
---

Have you ever wondered if it was possible to obtain an `Server Certificate` before you migrate a website? I always assumed the `DNS` had to be switched _before_ the `certificate validation` could take place. I've come to find there are more options available.

## The Scenario

My company is currently in the process of moving all of our client production sites to a new server. Part of that effort is ensuring all sites be placed on `https`.

## Server Certificate Challenges

> _Let's Encrypt offers domain-validated certificates, meaning they have to check that the certificate request comes from a person who actually controls the domain. They do this by sending the client a unique token, and then making a web or DNS request to retrieve a key derived from that token._

There are two main options to obtain a `server certificate`:

- `HTTP Challenge` - Posting a specified `file` in a specified location on a web site
- `DNS Challenge` - Posting a specified `DNS record` in the domain name system

### HTTP Challenge

This is usually handled by adding a `token` inside a `.well-known` directory in your `web root`. `Certbot` can then confirm you actually control resources on the specified domain, and will sign a `certificate`.

### DNS Challenge

This approach requires you to add specific `DNS TXT` entry for each domain requested. This is useful when you haven't switched `DNS` yet, but want to issue a `certificate` in anticipation (for testing).

For more information on challenges, visit [certbot's documentation][certbot-docs-challenges].

## Setup

_We'll be discussing the `DNS Challenge` approach for the rest of the article._

In the examples below, I'll be using `Apache` &amp; `Ubuntu 16.04` following [this guide][certbot-docs-apache-ubuntu-16.04]. To find documentation for your specific web server / operating system, go to [certbot's homepage][certbot-docs].

First we need to install `certbot` along with all necessary dependencies.

```bash
# run as root

apt-get update \
    && apt-get install software-properties-common \
    && add-apt-repository -y universe \
    && add-apt-repository -y ppa:certbot/certbot \
    && apt-get update \
    && apt-get install -y python-certbot-apache
```

## Configuration

_I won't be going over wildcard domains here, but they are an option. Refer to [their documentation][certbot-docs]._

We now need to tell `certbot` which domains we would like to issue a `certificate` for. Remember to add each subdomain individually.

Since we may have multiple `vhosts` per server, we decided to use the `--manual` &amp; `certonly` flags.

```bash
# run as root

# replace with your domain
# add all relevant subdomains
certbot --manual --preferred-challenges dns certonly \
    -d yourwebsite.com \
    -d www.yourwebsite.com \          # don't forget www binding
    -d staging.yourwebsite.com \      # example subdomain
    -d staging.stage1.yourwebsite.com # example long subdomain
```

### Challenge Prompts

Once you run the command above, it will prompt you to add a `DNS TXT` record for each specified domain. It will look like this:

```
Please deploy a DNS TXT record under the name
_acme-challenge.yourwebsite.com with the following value:

[random-string-of-characters]

Before continuing, verify the record is deployed.
(This must be set up in addition to the previous challenges; do not remove,
replace, or undo the previous challenge tasks yet. Note that you might be
asked to create multiple distinct TXT records with the same name. This is
permitted by DNS standards.)

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Press Enter to Continue

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
```

Now here's the important part. You need to remove the `base url` from each `record name`, like so:

| requested name from certbot                      | actual name to add in `DNS`      |
| ------------------------------------------------ | -------------------------------- |
| `_acme-challenge.yourwebsite.com`                | `_acme-challenge`                |
| `_acme-challenge.www.yourwebsite.com`            | `_acme-challenge.www`            |
| `_acme-challenge.staging.yourwebsite.com`        | `_acme-challenge.staging`        |
| `_acme-challenge.staging.stage1.yourwebsite.com` | `_acme-challenge.staging.stage1` |

Once you add the `DNS TXT` records, and click `Continue` through each challenge prompt respectively, the validation should pass.

```bash
Press Enter to Continue
Waiting for verification...
Cleaning up challenges

IMPORTANT NOTES:
 - Congratulations! Your certificate and chain have been saved at:
   /etc/letsencrypt/live/yourwebsite.com/fullchain.pem
   Your key file has been saved at:
   /etc/letsencrypt/live/yourwebsite.com/privkey.pem
   Your cert will expire on 2019-04-24. 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
```

## Updating Your VHost

Now that we have a valid certificate, we can update our `vhost`:

```bash
# inside the 443 binding

SSLEngine On
SSLCertificateFile /etc/letsencrypt/live/yourwebsite.com/cert.pem
SSLCertificateKeyFile /etc/letsencrypt/live/yourwebsite.com/privkey.pem
SSLCertificateChainFile /etc/letsencrypt/live/yourwebsite.com/chain.pem
```

Then restart `Apache`:

```bash
sudo apachectl configtest

# if syntax ok
sudo apachectl restart
```

## Bonus: Setup Auto Renewal

We'll leverage the `crontab` of the `root` user to automatically renew certificates that will expire soon.

```bash
# run as root

# edit the crontab
crontab -e
```

Then add the following two lines at the bottom:

```bash
# every Monday at 2:30am
30 2 * * 1 /usr/bin/certbot renew --deploy-hook "service apache2 reload" >> /var/log/letsencrypt/le-renew.log
```

The `certbot` will try and renew any certificates marked for renewal once a week. We then use the `--deploy-hook` to only reload `apache` if necessary.

Special thanks to [Daniel McCarney][dev-to-cpu] for the updated `crontab` code.

{% user cpu %}

[certbot-docs]: https://certbot.eff.org/
[certbot-docs-apache-ubuntu-16.04]: https://certbot.eff.org/lets-encrypt/ubuntuxenial-apache
[certbot-docs-challenges]: https://certbot.eff.org/docs/challenges.html
[dev-to-cpu]: https://dev.to/cpu