Compare commits

..

2 commits

Author SHA1 Message Date
ea8aedd0d6 Add article on DNS challenge
All checks were successful
ci/woodpecker/push/test Pipeline was successful
2024-07-03 14:15:08 +02:00
7dd8343680 Fix deprecated option in the config 2024-07-03 14:15:08 +02:00
2 changed files with 179 additions and 6 deletions

View file

@ -15,9 +15,6 @@ dataDir = "data"
layoutDir = "layouts" layoutDir = "layouts"
publishDir = "public" publishDir = "public"
[author]
name = "Melora Hugues"
[taxonomies] [taxonomies]
category = "blog" category = "blog"
tags = "tags" tags = "tags"
@ -56,6 +53,9 @@ author = true
logoText = "Hello there!" logoText = "Hello there!"
logoHomeLink = "/" logoHomeLink = "/"
[params.author]
name = "Melora Hugues"
[[params.social]] [[params.social]]
name = "git" name = "git"
url = "https://git.faercol.me" url = "https://git.faercol.me"

View file

@ -142,12 +142,185 @@ server for that.
### Configuring the DNS server ### Configuring the DNS server
The first step is configuring the DNS server. For this, I'll just use a [bind](https://bind9.readthedocs.io/en/v9.18.27/)
server installed from my usual package manager.
```bash
# example on Debian 12
sudo apt install bind9
```
Most of the configuration happens in the `/etc/bind` directory, mostly in `/etc/bind/named.conf.local`
```text
root@dns-server: ls /etc/bind/
bind.keys db.127 db.empty named.conf named.conf.local rndc.key
db.0 db.255 db.local named.conf.default-zones named.conf.options zones.rfc1918
```
Let's declare a first zone, for `internal.example.com`. Add the following config to
`/etc/bind/named.conf.local`
```text
zone "internal.example.com." IN {
type master;
file "/var/lib/bind/internal.example.com.zone";
```
This simply declares a new zone which is described in the file `/var/lib/bind/internal.example.com.zone`
Let's now create the zone itself. A DNS zone has a base structure that you must follow
```dns
$ORIGIN .
$TTL 7200 ; 2 hours
internal.example.com IN SOA ns.internal.example.com. admin.example.com. (
2024070301 ; serial
3600 ; refresh (1 hour)
600 ; retry (10 minutes)
86400 ; expire (1 day)
600 ; minimum (10 minutes)
)
NS ns.internal.example.com.
$ORIGIN internal.example.com.
ns A 1.2.3.4
test A 192.168.1.2
```
This file declares a zone `internal.example.com` which master is `ns.internal.example.com`.
It also sets the parameters (time to live for the records, and the current serial for the
zone config).
Finally, two (2) A records are created, associating the name `ns.internal.example.com` to
the IP address `1.2.3.4`, and `test.internal.example.com` (the domain for which we want
a certificate) to a local IP address `192.168.1.2`.
A simple `systemctl restart bind9` would be enough to apply the modification, but we still
have one thing to do, which is allowing remote modifications to the zone.
### Enabling remote DNS zone modification ### Enabling remote DNS zone modification
To allow remote modification of our DNS zone, we are going to use [TSIG](https://www.ibm.com/docs/en/aix/7.3?topic=ssw_aix_73/network/bind9_tsig.htm)
which stands for **Transaction signature**. It's a way to secure server to server operations
to edit a DNS zone, and is preferred to access control based on IP addresses.
Let's start with creating a key using the command `tsig-keygen <keyname>`
```shell
➜ tsig-keygen letsencrypt
key "letsencrypt" {
algorithm hmac-sha256;
secret "oK6SqKRvGNXHyNyIEy3hijQ1pclreZw4Vn5v+Q4rTLs=";
};
```
This creates a key with the given name using the default algorithm (which is `hmac-sha256`).
The entire output of this command is actually a code block that you can add to your bind9
configuration.
Finally, using `update-policy`, allow this key to be used to update the zone.
```text
update-policy {
grant letsencrypt. zonesub txt;
};
```
{{< callout type="note" >}}
Doing so allows users to update everything in your zone using this key. In fact
you would only need to update `_acme-challenge.test.internal.example.com` as seen
in the DNS challenge description.
If you want a better restriction, then you can use the following configuration instead
```text
update-policy {
grant letsencrypt. name _acme-challenge.test.internal.example.com. txt;
};
```
{{< /callout >}}
This means your entire `named.conf.local` would become something like this
```text
key "letsencrypt" {
algorithm hmac-sha256;
secret "oK6SqKRvGNXHyNyIEy3hijQ1pclreZw4Vn5v+Q4rTLs=";
};
zone "internal.example.com." IN {
type master;
file "/var/lib/bind/internal.example.com.zone";
update-policy {
grant letsencrypt. zonesub txt;
};
};
```
{{< callout type="warning" >}}
Be **very cautious** about the `.` at the end of the zone name and the key name, they are
easy to miss, and forgetting them will cause issues that would be hard to detect.
{{< /callout >}}
With that being done, you can restart the DNS server and everything is ready server side,
the only remaining thing to do would be the DNS challenge itself.
### Performing the challenge ### Performing the challenge
## Bonus: completely hiding your private domains from outside Start by installing the certbot with the RFC2136 plugin (to perform the DNS challenge).
```shell
apt install python3-certbot-dns-rfc2136
```
It's handled using a `.ini` configuration file, let's put it in `/etc/certbot/credentials.ini`
```ini
dns_rfc2136_server = <you_dns_ip>
dns_rfc2136_port = 53
dns_rfc2136_name = letsencrypt.
dns_rfc2136_secret = oK6SqKRvGNXHyNyIEy3hijQ1pclreZw4Vn5v+Q4rTLs=
dns_rfc2136_algorithm = HMAC-SHA512
```
Finally, run the challenge using certbot (if it's the first time you're using certbot on
that machine, it might ask for an email to handle admin stuff).
```shell
root@toolbox:~# certbot certonly --dns-rfc2136 --dns-rfc2136-credentials /etc/certbot/credentials.ini -d 'test.internal.example.com'
Saving debug log to /var/log/letsencrypt/letsencrypt.log
Requesting a certificate for test.internal.example.com
Waiting 60 seconds for DNS changes to propagate
Successfully received certificate.
Certificate is saved at: /etc/letsencrypt/live/test.internal.example.com/fullchain.pem
Key is saved at: /etc/letsencrypt/live/test.internal.example.com/privkey.pem
This certificate expires on 2024-09-30.
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
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
```
And that's done, you have a certificate, and a no point in time did you need to
actually expose your application to the outside world.
Now because I like to go way too far, I can propose two (2) improvements to this
setup:
- Using ACL in addition to the TSIG key to secure operations on the DNS server
- Using a second DNS server only locally accessible for your private records, and
using the public server to only perform challenges
## Bonus 1: adding a second layer of authentication to connect to the DNS
## Bonus 2: completely hiding your private domains from outside
![A schema describing the DNS challenge workflow for the ACME protocol using a public and private DNS servers](/images/dns_article_dns_challenge_2.svg) ![A schema describing the DNS challenge workflow for the ACME protocol using a public and private DNS servers](/images/dns_article_dns_challenge_2.svg)
![alt text](image.png)