Compare commits
1 commit
ea8aedd0d6
...
f4f2771ab3
Author | SHA1 | Date | |
---|---|---|---|
f4f2771ab3 |
2 changed files with 6 additions and 179 deletions
|
@ -15,6 +15,9 @@ dataDir = "data"
|
||||||
layoutDir = "layouts"
|
layoutDir = "layouts"
|
||||||
publishDir = "public"
|
publishDir = "public"
|
||||||
|
|
||||||
|
[author]
|
||||||
|
name = "Melora Hugues"
|
||||||
|
|
||||||
[taxonomies]
|
[taxonomies]
|
||||||
category = "blog"
|
category = "blog"
|
||||||
tags = "tags"
|
tags = "tags"
|
||||||
|
@ -53,9 +56,6 @@ 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"
|
||||||
|
|
|
@ -142,185 +142,12 @@ 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
|
||||||
|
|
||||||
Start by installing the certbot with the RFC2136 plugin (to perform the DNS challenge).
|
## Bonus: completely hiding your private domains from outside
|
||||||
|
|
||||||
```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)
|
||||||
|
|
Loading…
Reference in a new issue