DNS-01 challenge mit dehydrated und bind9
Es gibt viele Wege zu einem gültigen SSL/-TLS-Zertifikat für die eigene Server-Landschaft. In zahlreichen Anleitungen beschrieben ist die Möglichkeit, eine HTTP-01 challenge auf dem jeweiligen Webserver durchzuführen. Dieses Verfahren ist gut und solide. Spätestens wenn Zertifikate für Server benötigt werden, die nicht über das Internet erreichbar sind/sein sollen, scheitert diese Vorgehensweise jedoch. Zudem können Wildcard-Zertifikate nur über die DNS-01 Challenge austgestellt werden.
Diese Anleitung beschreibt eine Einrichtung eines eigenen DNS-Servers mit bind9, der Einrichtung des ACME-Clients dehydrated und dem anschließenden automatischen Erzeugen von Let’s-Encrypt-Zertifikaten via DNS-01 challenge. Diese werden im Anschluss via ansible auf die Zielserver verteilt
Let’s Encrypt unterstützt (derzeit aktiv) drei verschiedene Challenge-Arten. Bei der nachfolgend beschriebenen DNS-01 challenge erfolgt der Beweis der Domänen-Inhaberschaft über einen besonderen DNS-Eintrag, der bei der Challenge im DNS-System hinterlegt werden muss. Nur der Eigener der Domän kann dies tun (zumindest sollte das so sein ;-)). Ist der Eintrag erfolgt, prüft Let’s encrpyt (= CA: Certificate Authority), ob der Eintrag an Ort und Stelle ist. Ist das der Fall, wird das Zertifikat ausgestellt.
Das Vorgehen kann mit einem ACME-Client (certbot, dehydrated, etc.) manuell durchgeführt, aber eben auch automatisiert werden. Der Ablauf von der DNS-01 challegene bis zur Verteilung der Zertifikate (wie ich sie hier beschreibe) wird durch folgende Abbildung verdeutlicht:
- Im Vorfeld werden Zonen vom DNS-Registrar an eigenen DNS-Server delegiert.
- Auf dem DNS-Server ist ein ACME-Client installiert der die DNS-Challegene initiiert.
- Let’s Encrypt gibt Token vor, prüft ob dieser im DNS hinterlegt wurde und stellt Zertifikate aus.
- Die Zertifikate werden dann an die jeweilgen Stellen via ansible verteilt.
Bei der Erstellung dieses Beitrags, habe ich die nachfolgenden Codezeilen auf einem Server durchgeführt, der unter der IP-Adresse 128.140.114.17 lief. Dieser wurde nur kurzfristig durch einen Cloud-Provider bereit gestellt. Wer die Anleitung durcharbeitet, muss die Adresse des eigenen DNS-Servers verwenden.
Die Installation wird auf einem Debian-Server (zum Zeitpunkt dieses Beitrags Debian 12) durchgeführt. Nach der Basisinstallation sollte eine Minimalabsicherung erfolgen:
- SSH aktivieren
- [evtl. einen alternativen Port für SSH verwenden]
- Public-Key-Auth einrichten
- fail2ban installieren, so dass fehlerhafte Logins zu einem BAN führen
- Firewall installieren und nur Port für SSH und Port 53 freigeben.
Könnte in der Kürze wie folgt aussehen:
SSH-Key von meinem Client hochladen:
ssh-copy-id -i ~/.ssh/<mein-ssh-key>.pub <user>@<server>
Auf dem Server ufw
und fail2ban
installieren, SSH-Port umziehen (Mach ich so, müsst Ihr nicht) und passende UFW-Regeln erstellen:
apt update
apt install ufw fail2ban
sed -i 's/#Port 22/Port 42042/' /etc/ssh/sshd_config
systemctl restart sshd.service
ufw allow 42042
ufw allow 53
ufw enable
Vorsicht bei der Port-Änderung von SSH und der anschließenden Einrichtung der Firewall. Man kann sich ganz schön aussperren, wenn man hier nur abtippt.
Bei Debian 12 muss bzgl. fail2ban noch an einer Stelle nachgearbeitet werden. Nach der Installation läuft der Service nicht korrekt. Die Anpassung erfolgt in der /etc/fail2ban/jail.conf
. Ich löse das Problem in dem ich die Zeile backend = %(sshd_backend)s
durch backend = systemd
tausche und dann fail2ban neu starte:
apt install python3-systemd
sed -i 's/backend = %(sshd_backend)s/backend = systemd/' /etc/fail2ban/jail.conf
systemctl restart fail2ban
Siehe hierzu auch: https://github.com/fail2ban/fail2ban/issues/3292
Nun beginnt die eigentliche Einrichtung und wir beginnen mit der Installation eines DNS-Servers.
apt install bind9 bind9-dnsutils
Die Konfigurationsdateien liegen unter /etc/bind/
. Die Hauptkonfiugrationsdatei /etc/bind/named.conf
lassen wir in Ruhe. Diese Datei beinhaltet nur die Includes auf die anderen Dateien die uns wichtig sind.
Die eigentliche Konfiguration (außerhalb der Zonendefinitionen) erfolgt in der /etc/bind/named.conf.options
. Im Beispiel hier habe ich folgende Konfiguration hinzugefügt:
options {
directory "/var/cache/bind";
listen-on port 53 { any; };
allow-query { any; };
allow-transfer {"none";};
allow-recursion {"none";};
recursion no;
rate-limit {
responses-per-second 5;
window 5;
};
// Weiterer Inhalt gekürzt
Damit darf weltweit jedes Gerät diesen DNS-Server verwenden. Allerdings ist eine rekursive Anfrage nicht erlaubt. Somit kann der DNS-Server nur für die eigenen (delegierten) Domains verwendet werden. Zusätzlich sorgt das rate-limit
dafür, dass der Server nur eine gewisse Anzahl an DNS-Anfragen pro Zeiteinheit zulässt. Somit wird ein Missbrauch vermieden wie z. B. hier beschrieben: BSI-Hinweise zu offenen DNS-Resolvern.
In der /etc/bind/named.conf.local
werden die einzelnen Zonen definiert:
include "/etc/bind/nsupdate.key";
zone "lab.toheine.net" IN {
type master;
allow-update { key "nsupdate-key"; };
file "db.lab.toheine.net";
notify yes;
};
zone "_acme-challenge.toheine.net" IN {
type master;
allow-update { key "nsupdate-key"; };
file "db._acme-challenge.toheine.net";
notify yes;
};
Im Beispiel wurden zwei Zonen definiert. Die erste Zone ist für die Subdomain lab.toheine.net
komplett zuständig. Diese Form verwende ich, wenn ich neben der DNS-01 challenge auch weitere Records erstellen will. Die zweite Zone _acme-challenge.toheine.net
wird später ausschließlich für die Zertifikats-Erstellung verwendet. Alle anderen Records die unter der toheine.net
Domain laufen, werden direkt bei meinem Registrar eingegeben.
Innerhalb der Zone verraten die Zuweisungen für file
, wo die einzelnen Records tatsächlich liegen und allow-update
ermöglicht das dynamische Eintragen von Resource Records, sofern der korrekte Key verwendet wird. Über dieses Verfahren trägt der ACME-Client ein Token als TXT-Record ein. Den Key brauchen wir erst später … aber weil wir gerade dabei sind, erzeugen wir diesen gleich und verpassen der Datei die minimalen Rechte:
tsig-keygen -a sha512 nsupdate-key > /etc/bind/nsupdate.key
chmod 600 /etc/bind/nsupdate.key
chown bind: /etc/bind/nsupdate.key
Wenn wir bind9 konfiguriert haben, werden die einzelnen Zonen-Dateien unter /var/cache/bind/
erwartet. Für jede Zone erstellen wir eine eigene Datei, die wir im Anschluss mit Inhalten füllen:
Die Zonendatei /var/cache/bind/db.lab.toheine.net
hat nachfolgenden exemplarischen Aufbau:
$TTL 300 ; 5 minutes
@ IN SOA dns.lab.toheine.net. mail.toheine.net. (
2024041701 ; serial
86400 ; refresh (1 day)
7200 ; retry (2 hours)
604800 ; expire (1 week)
172800 ; minimum (2 days)
)
@ NS dns
dns A 128.140.114.17 ; produktive IP meines DNS-Servers
www A 192.0.2.1 ; Test-Net-IP
test CNAME www ; Alias auf www
cloud CNAME www ; Alias auf www
Das Kommentar-Zeichen der Zonendatei ist das Semikolon. Gerade wenn man neu in der DNS-Welt unterwegs ist, hilft das für die Zuordnung der vielen Werte oder DNS-Typen. Wichtig ist der SOA
-Eintrag (Start of Authority), der einige Parameter enthält, die für die Eröffnung der Zone notwendig sind. Der Aufbau der Datei ist auf Wikipedia gut erklärt.
Die weiteren Einträge dienen erstmal nur dazu, um später zu sehen, ob der DNS-Server tatsächlich die Anfragen korrekt auflöst. Hier können beliebige, gültige Einträge vorgenommen werden. Analog dazu erstelle ich eine leere Zonen-Datei unter /var/cache/bind/db._acme-challenge.toheine.net
die allerdings neben dem SOA
- und NS
-Eintrag keine weiteren Records enthält:
$TTL 300 ; 5 minutes
@ IN SOA dns.lab.toheine.net. mail.toheine.net. (
2024041701 ; serial
86400 ; refresh (1 day)
7200 ; retry (2 hours)
604800 ; expire (1 week)
172800 ; minimum (2 days)
)
@ NS dns.lab.toheine.net.
Nun sind wir fertig und wir können bind9 neu starten, damit die geänderten Konfigurationen neu eingelesen werden. Vorab ist es allerdings absolut empfehlenswert, die Konfiguration mit folgendem Befehl zu überprüfen:
named-checkconf -z
Die Ausgabe sollte wie folgt aussehen:
zone lab.toheine.net/IN: loaded serial 2024041701
zone _acme-challenge.toheine.net/IN: loaded serial 2024041701
zone localhost/IN: loaded serial 2
zone 127.in-addr.arpa/IN: loaded serial 1
zone 0.in-addr.arpa/IN: loaded serial 1
zone 255.in-addr.arpa/IN: loaded serial 1
Wenn hier Fehler enthalten sind, müssen diese vor einem Nameserver-Neustart bereinigt werden. Ansonsten wird bind9 nicht starten. Ist alles korrekt, kann der named-service neu gestartet werden:
systemctl restart named.service
Damit ist die DNS-Konfiguration fertig. Um von einem lokalen Client zu testen, ob der DNS-Server seine Arbeit korrekt erledigt, können wir mit dem Kommando dig @128.140.114.17 test.lab.toheine.net
einen DNS-Lookup durchführen. Wichtig ist zum jetzigen Zeitpunkt, dass wir den DNS-Server (hinter dem @
-Zeichen) angeben. Die Ausgabe sollte wie folgt aussehen:
; <<>> DiG 9.18.24-1-Debian <<>> @128.140.114.17 test.lab.toheine.net
; (1 server found)
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 45885
;; flags: qr aa rd; QUERY: 1, ANSWER: 2, AUTHORITY: 0, ADDITIONAL: 1
;; WARNING: recursion requested but not available
;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 1232
; COOKIE: def4386d4f9e1e0f010000006620039ac691052a8e61c70f (good)
;; QUESTION SECTION:
;test.lab.toheine.net. IN A
;; ANSWER SECTION:
test.lab.toheine.net. 300 IN CNAME www.lab.toheine.net.
www.lab.toheine.net. 300 IN A 192.0.2.1
;; Query time: 4 msec
;; SERVER: 128.140.114.17#53(128.140.114.17) (UDP)
;; WHEN: Wed Apr 17 19:15:06 CEST 2024
;; MSG SIZE rcvd: 111
Im letzten Schritt der DNS-Konfiguration, müssen wir bei unserem DNS-Registrar noch die Zonen-Delegation hinterlegen. Hier sind die Einträge je nach Anbieter recht ähnlich und kann wie folgt aussehen:
- IPv4- und IPv6- Eintrag, um den eingerichteten DNS-Server mit Namen aufzulösen
- Zonen-Delegation ausschließlich für die DNS-01 challenge.
- Zonen-Delegation für eine komplette Suddomain (DNS-01 challengenge und sonstige Records)
Sobald man eigene Konfigurations-Dateien auf einem eigenen Server hat, kann man grundsätzlich mit Hilfe von Skripten (bash-Skripte, python, etc.) die Dateien anpassen. Für das dynamische Einspielen von DNS-Records sind aber solche Skripte nicht wirklich geeignet, da u. a. der DNS-Server nach einem Resource-Eintrag seine Konfiguration jedes Mal neu laden müsste.
Für dynamische DNS-Updates ist das Tool nsupdate
vorgesehen, das wir im Rahmen von bind9-dnsutils
oben installiert haben. Unser ACME-Client soll später über dieses Tool die DNS-Einträge vornehmen. Sofern man nsupdate
ohne Angabe von Optionen oder Argumenten aufruft, befindet man sich in einem interaktiven nsupdate-Modus von wo aus verschiedene nsupdate-Befehle übergeben werden können. Mit help
sieht man die Möglichkeiten vor Ort und mit quit
beendet man den interaktiven Modus wieder.
Sehr komfortabel ist die Verwendung einer Textdatei um die Funktion von nsupdate
zu prüfen. Diese enthält die Befehlsfolge, die wir bei Aufruf von nsupdate
übergeben. Im Beispiel schreibe ich folgenden Inhalt in eine ~/nsupdate.txt
-Datei:
server 127.0.0.1
zone lab.lfb-linux.de
update add botschaft.lfb-linux.de. 300 in TXT "HeyYoda"
show
send
Sinn und Zweck ist das Testen des dynamischen Eintrags. Später brauchen wir die Datei nicht mehr. Das übernimmt dann der ACME-Client. Daher ist der RRecord (Resource Record) im Beispiel eben, mit HeyYoda relativ sinnfrei. Wenn wir den Eintrag vornehmen wollen, rufen wir nsupdate
mit folgendem Befehl auf:
nsupdate -k /etc/bind/nsupdate.key -v nsupdate.txt
Es erfolgt folgende Ausgabe:
Outgoing update query:
;; ->>HEADER<<- opcode: UPDATE, status: NOERROR, id: 0
;; flags:; ZONE: 0, PREREQ: 0, UPDATE: 0, ADDITIONAL: 0
;; ZONE SECTION:
;lab.toheine.net. IN SOA
;; UPDATE SECTION:
botschaft.lab.toheine.net. 300 IN TXT "HeyYoda"
Um zu prüfen, ob der dynamische Eintrag funktioniert hat, führen wir auf dem Client ein DNS-Lookup durch:
dig -t TXT botschaft.lab.toheine.net
Die Ausgabe sollte dann (gekürzt wie folgt aussehen):
;; ANSWER SECTION:
botschaft.lab.toheine.net. 300 IN TXT "HeyYoda"
;; AUTHORITY SECTION:
lab.toheine.net. 300 IN NS dns.lab.toheine.net.
;; ADDITIONAL SECTION:
dns.lab.toheine.net. 300 IN A 128.140.114.17
Die Installation vondehydrated
auf dem DNS-Server kann man diskutieren. Die DNS-01 Challenge und damit verbundenen dynamischen Updates können auch von jedem anderen Host aus angestoßen werden, sofern dieser über den oben erzeugtennsupdate-key
verfügt.
Nun kommen wir zur Einrichtung von dehydrated, das wir via apt install dehydrated
installieren. Die Konfiguration erfolgt im Verzeichnis /etc/dehydrated
. Eine gute Doku mit Beispielen findet sich bei Debian unter /usr/share/doc/dehydrated/
.
Die Hauptkonfigurationsdatei ist die /etc/dehydrated/config
, die folgenden Aufbau besitzt:
CONFIG_D=/etc/dehydrated/conf.d
BASEDIR=/etc/dehydrated
DOMAINS_TXT="${BASEDIR}/domains.txt"
CERTDIR="${BASEDIR}/certs"
CHALLENGETYPE="dns-01"
CA="https://acme-staging-v02.api.letsencrypt.org/directory"
#CA="letsencrypt"
HOOK="${BASEDIR}/hook.sh"
AUTO_CLEANUP="yes"
CONTACT_EMAIL=mail@toheine.net
Vieles erklärt sich von selbst. Bei Ersteinrichtung von dehydrated, nehme ich vorab die Staging-CA, die verhindert, dass ich bei Konfigurationsfehlern gegen das Rate Limit von Let’s encrypt laufe (siehe dazu: staging-environment). Produktiv verwendet wird später die korrekte CA (CA="letsencrypt"
).
Die einzelnen Domains, für die ein Zertifikat ausgestellt werden sollen, liegen in einer eigenen Datei unter /etc/dehydrated/domains.txt
:
lab.toheine.net *.lab.toheine.net > lab.toheine.net
toheine.net > toheine.net
Jede Domain, die hier pro Zeile eingetragen wird, enthält ein eigenes Zertifikat. Möchte man ein Zertifikat für mehrere Domains haben, werden diese Leerzeichen-getrennt in eine Zeile geschrieben. Hinter dem >
-Zeichen ist dann der für dehydrated hinterlegte Alias eingetragen. Unter diesem Namen werden die Zertifikate dann unter /etc/dehydrated/certs
abgelegt.
Im Anschluss brauchen wir das Hook-Skript, das während der Challenge aufgerufen wird und den dynamischen Eintrag erzeugt. Darin enthalten:
#!/usr/bin/env bash
#
# Example how to deploy a DNS challenge using nsupdate
# https://github.com/dehydrated-io/dehydrated/wiki/example-dns-01-nsupdate-script
#
set -e
set -u
set -o pipefail
NSUPDATE="nsupdate -k /etc/dehydrated/nsupdate.key"
DNSSERVER="127.0.0.1"
TTL=300
case "$1" in
"deploy_challenge")
printf "server %s\nupdate add _acme-challenge.%s. %d in TXT \"%s\"\nsend\n" "${DNSSERVER}" "${2}" "${TTL}" "${4}" | $NSUPDATE
;;
"clean_challenge")
printf "server %s\nupdate delete _acme-challenge.%s. %d in TXT \"%s\"\nsend\n" "${DNSSERVER}" "${2}" "${TTL}" "${4}" | $NSUPDATE
;;
"deploy_cert")
# optional:
# /path/to/deploy_cert.sh "$@"
;;
"unchanged_cert")
# do nothing for now
;;
"startup_hook")
# do nothing for now
;;
"exit_hook")
# do nothing for now
;;
esac
exit 0
Dieses Skript muss zur Ausführung berechtigt sein chmod +x hook.sh
. Zuletzt kopieren wir noch den nsupdate.key an Ort und Stelle:
cp ../bind/nsupdate.key .
Im Anschluss an die Einrichtung von dehydrated
, muss man sich (pro CA) einmal regisitrieren …
dehydrated --register --accept-terms
… und kann im Anschluss dehydrated manuell ausführen:
dehydrated -c
Hat alles geklappt sollte die Ausgabe wie folgt aussehen:
# INFO: Using main config file /etc/dehydrated/config
+ Creating chain cache directory /etc/dehydrated/chains
Processing lab.toheine.net with alternative names: *.lab.toheine.net
+ Creating new directory /etc/dehydrated/certs/lab.toheine.net ...
+ Signing domains...
+ Generating private key...
+ Generating signing request...
+ Requesting new certificate order from CA...
+ Received 2 authorizations URLs from the CA
+ Handling authorization for lab.toheine.net
+ Handling authorization for lab.toheine.net
+ 2 pending challenge(s)
+ Deploying challenge tokens...
+ Responding to challenge for lab.toheine.net authorization...
+ Challenge is valid!
+ Responding to challenge for lab.toheine.net authorization...
+ Challenge is valid!
+ Cleaning challenge tokens...
+ Requesting certificate...
+ Order is processing...
+ Checking certificate...
+ Done!
+ Creating fullchain.pem...
+ Done!
Processing toheine.net
+ Creating new directory /etc/dehydrated/certs/toheine.net ...
+ Signing domains...
+ Generating private key...
+ Generating signing request...
+ Requesting new certificate order from CA...
+ Received 1 authorizations URLs from the CA
+ Handling authorization for toheine.net
+ 1 pending challenge(s)
+ Deploying challenge tokens...
+ Responding to challenge for toheine.net authorization...
+ Challenge is valid!
+ Cleaning challenge tokens...
+ Requesting certificate...
+ Order is processing...
+ Checking certificate...
+ Done!
+ Creating fullchain.pem...
+ Done!
+ Running automatic cleanup
Die notwendigen Zertifikate liegen nun in unter /etc/dehydrated/certs
.
Haben wir mit der Staging-CA begonnen, muss in der/etc/dehydrated/config
noch die korrekte CA hinterlegt werden und wir müssen uns für diese ebenfalls einmalig mitdehydrated --register --accept-terms
regisitrieren.
Damit die Zertifikate in regelmäßigen Abständen neu gezogen werden, richten wir cron
mit dem Aufruf crontab -e
ein und hinterlegen folgende Zeile:
25 10 * * 1 /usr/bin/dehydrated -c >> /var/log/dehydrated.log 2>&1
Die Challenge wird nun 1x pro Woche initiiert. Sofern sich das Zertifikat dem Verfallsdatum nährt, wird ein neues ausgestellt. Ob ich das loggen muss, mir eine Mail zu sende oder die Ausgabe nach /dev/null
schiebe, ist Geschmackssache.
Nun werden die notwendigen Zertifikate automatisch und regelmäßig erstellt. Die Verteilung läuft via ansible. Klar … auch das kann man automatisieren. Dieses letzte Stück manuelle Ausführung behalte ich mir jedoch vor, da ich dann auch wirklich weiß, das die Zertifikate an Ort und Stelle sind und die notwendigen Dienste diese auch tatsächlich geladen haben.
Ich stelle hier nur kurz die entsprechende Rolle und ein exemplarisches Playbook vor. Wer mit ansible arbeitet, wird vorab einen ansible-Host einrichten, von wo aus die Playbooks auf die zu orchestrierenden Server losgelassen werden: ansible installieren, ansible.cfg anlegen, gewisse Ordnerstruktur erstellen. Einfache Beispiele dazu sind hier zu finden: https://codeberg.org/toheine/linadv_infra.
Wir benötigen auf dem ansible-host eine eigene Rolle update-certs. Diese liegt unter {{ roles_path }}/update-certs/tasks/main.yml
und hat in meinem Fall folgenden Aufbau:
---
- name: Copy fullchain.pem to local
ansible.builtin.fetch:
src: /etc/dehydrated/certs/{{ certname }}/fullchain.pem
dest: /tmp/{{ certname }}/fullchain.pem
flat: yes
mode: 0644
run_once: true
delegate_to: "{{ acmehost }}"
- name: Copy privkey.pem to local
ansible.builtin.fetch:
src: /etc/dehydrated/certs/{{ certname }}/privkey.pem
dest: /tmp/{{ certname }}/privkey.pem
flat: yes
mode: 0640
run_once: true
delegate_to: "{{ acmehost }}"
- name: Create /etc/ssl/{{ certname }} directory
ansible.builtin.file:
path: /etc/ssl/{{ certname }}
state: directory
recurse: yes
- name: Copy {{ certname }}-wildcard-certificates
ansible.builtin.copy:
src: /tmp/{{ certname }}/fullchain.pem
dest: /etc/ssl/{{ certname }}/fullchain.pem
mode: 0644
register: crt
- name: Copy {{ certname }}-wildcard-key
ansible.builtin.copy:
src: /tmp/{{ certname }}/privkey.pem
dest: /etc/ssl/{{ certname }}/privkey.pem
mode: 0600
- name: check if restart-services.sh exists
ansible.builtin.stat:
path: /usr/local/sbin/restart-services.sh
register: resfile
- name: restart services
command: /usr/local/sbin/restart-services.sh
when: resfile.stat.exists and crt.changed
Der letzte Task aus der Rolle, führt ein Bashskript auf dem Zielsystem aus, das alle Services neu startet, die das Zertifikat verwenden. Das kann exemplarisch wie folgt aussehen:
#!/bin/bash
systemctl restart nginx.service
Pro Zielsystem muss die entsprechende Datei einmalig unter /usr/local/sbin/
restart-services.sh` abgelegt werden.
Zu der Rolle gibt es pro Zertifikat ein eigenes Playbook, das dann
- die Gruppe der Ziel-Hosts,
- den Zeritifkats-Namen (alias der dehydrated-domains.txt),
- den Namen des acme-hosts auf dem die Zertifikate ausgestellt werden
- und den Rollenaufruf enthält.
Am Beispiel von lab.toheine.net sieht das playbook update-certs-lab-toheine.yml
wie folgt aus:
- name: update SSL/TLS certs
hosts: certs_lab_toheine
become: yes
vars:
certname: lab.toheine.net
acmehost: heidns
roles:
- update-certs
Das dazugehörige Inventory enthält eine Zuordnung zu einer Gruppe certs_lab_toheine
, der ein oder mehrere Hosts zugeordnet werden. In meinem Fall sind das zwei Host mit dem Namen web01 und web02. Ist alles an Ort und Stelle, kann das Playbook via ansible-playbook update-certs-toheine-net.yml
ausgeführt werden. In meinem Fall erfolgt folgende Ausgabe:
PLAY [update SSL/TLS certs] *******************************************************
TASK [Gathering Facts] ************************************************************
ok: [web02]
ok: [web01]
TASK [update-certs : Copy fullchain.pem to local] *********************************
ok: [web01 -> heidns(128.140.114.17)]
TASK [update-certs : Copy privkey.pem to local] ***********************************
ok: [web01 -> heidns(128.140.114.17)]
TASK [update-certs : Create /etc/ssl/lab.toheine.net directory] *******************
changed: [web02]
changed: [web01]
TASK [update-certs : Copy lab.toheine.net-wildcard-certificates] ******************
changed: [web02]
changed: [web01]
TASK [update-certs : Copy lab.toheine.net-wildcard-key] ***************************
changed: [web02]
changed: [web01]
TASK [update-certs : check if restart-services.sh exists] *************************
ok: [web01]
ok: [web02]
TASK [update-certs : restart services] ********************************************
skipping: [web01]
skipping: [web02]
PLAY RECAP ************************************************************************
web01 : ok=7 changed=3 unreachable=0 failed=0 skipped=1
web02 : ok=5 changed=3 unreachable=0 failed=0 skipped=1
Damit sind die SSL-Zertifikate an eingestellten Ort und Stelle. Der jeweilige Service (Apache, nginx, Postifx, etc.) muss in der Folge eben noch dieses Zertifikat nutzen.
Dieses Setting umzusetzen, ist nicht in fünf Minuten erledigt. Darüber hinaus gibt es zahlreiche andere Wege zu einem gültigen Zertifikat. Wer allerdings sowieso einen DNS-Server betreibt und eine große Farm an vielen Servern vorhält, kann über die DNS-01 Challenge saubere (Wildcard-)-Zertifikate für alle Services ausstellen.
- https://letsencrypt.org/de/docs/
- https://github.com/fail2ban/fail2ban/issues/3292#issuecomment-1678844644
- https://www.cymru.com/Documents/secure-bind-template.html
- https://www.cisa.gov/news-events/alerts/2013/03/29/dns-amplification-attacks
- https://de.wikipedia.org/wiki/SOA_Resource_Record
- https://dehydrated.io/
- https://github.com/dehydrated-io/dehydrated
- https://www.rfc-editor.org/rfc/rfc2136.html
- https://www.rtfm-sarl.ch/articles/using-nsupdate.html