- Modifié
Je vous propose 2 scripts écrits en python3, l'un pour ajouter automatiquement un fingerprint chez Gandi, l'autre pour supprimer le précédent enregistrement après 24 heures. Les notifications slack permettent de recevoir les messages sur votre téléphone .. pratique pour être tenu informer.
Basés sur l'API de Gandi et sur le tuto de @Hardware
Déployer un serveur DNS autoritaire avec NSD/DNSSEC via Docker
Rotation des clés DNSSEC
Signature de la zone dns
Mise à jour du numéro de série de l'enregistrement SOA
Mise à jour automatique du fingerprint chez Gandi
Suppression après 24h du précédent fingerprint chez Gandi
Ce sont mes premiers scripts python, aussi n'hésitez pas à m'apporter vos critiques, vos conseils et vos résultats de tests pour les améliorer.
Ajout du fingerprint chez Gandi
import xmlrpc.client
import sys
import os
import subprocess
import fileinput
import requests
import json
api = xmlrpc.client.ServerProxy('https://rpc.gandi.net/xmlrpc/')
apikey = 'SSoxxxxxxxxxxxxxxx' ## cle de production gandi "https://v4.gandi.net/admin/api_key"
webhook_url = 'https://hooks.slack.com/services/TBFNA2Y5xxxxxxxxxx/F06eAOJKC1PxxxxxxxR' ## mettre votre webhook_url
# variable pour le nom de domaine
cmd = "grep name /mnt/docker/nsd/conf/nsd.conf | cut -d ':' -f2 | tr '\n' ' '"
process = subprocess.Popen(cmd, stdout=subprocess.PIPE, shell=True)
temp = process.communicate()[0]
domain = str(temp.decode())[1:-1]
# nouvelles cles
os.system('docker exec nsd keygen '+ domain)
# serial actuel de la zone
for line in open("/mnt/docker/nsd/zones/db."+ domain):
if "Serial" in line:
cmd = "grep Serial /mnt/docker/nsd/zones/db."+ domain
process = subprocess.Popen(cmd, stdout=subprocess.PIPE, shell=True)
serial = process.communicate()[0]
serial = str(serial.decode())[1:-10]
serial = serial.strip ()
serial = int(serial)
# nouveau serial
cmd = "date -d '+1 day' +'%Y%m%d%H' | tr '\n' ' '"
process = subprocess.Popen(cmd, stdout=subprocess.PIPE, shell=True)
newserial = process.communicate()[0]
newserial = int(newserial )
# modif serial dans la zone
if serial < newserial:
for line in fileinput.input("/mnt/docker/nsd/zones/db."+ domain, inplace=True):
print(line.replace(str(serial), str(newserial)), end='')
# date expiration pour la signature dnssec
cmd = "date -d '+6 months' +'%Y%m%d%H%M%S' | tr '\n' ' '"
process = subprocess.Popen(cmd, stdout=subprocess.PIPE, shell=True)
date_expire = process.communicate()[0]
date_expire = int(date_expire)
date_expire = str(date_expire)
# signature de la zone DNS
os.system('docker exec nsd nsd-checkzone '+ domain + ' /zones/db.'+ domain + ' >> zone.log')
for line in open("zone.log"):
if "ok" in line:
cmd = "grep ok zone.log"
process = subprocess.Popen(cmd, stdout=subprocess.PIPE, shell=True)
result = process.communicate()[0]
final = str(result.decode())[0:-1]
if final == 'zone' + ' ' +domain +' ''is ok':
os.system('docker exec nsd signzone '+domain+' '+date_expire)
os.remove('zone.log')
else:
slack_data = {'text': "Verifiez votre zone, il semble y avoir un erreur"}
response = requests.post(
webhook_url, data=json.dumps(slack_data),
headers={'Content-Type': 'application/json'}
)
os.remove('zone.log')
# recuperer le fingerprint
os.system('docker exec nsd ds-records '+ domain + ' >> dnskey.log')
for line in open("dnskey.log"):
if "DNSKEY" in line:
cmd = "grep DNSKEY dnskey.log | cut -d ' ' -f4 | tr '\n' ' '"
process = subprocess.Popen(cmd, stdout=subprocess.PIPE, shell=True)
fingerprint = process.communicate()[0]
condensat = str(fingerprint.decode())[0:-1]
# Envoi du fingerprint chez gandi
op = api.domain.dnssec.create(apikey, domain, {
"flags": 257,
"algorithm": 14,
"public_key": condensat})
slack_data = {'text': 'La nouvelle cle est bien enregistre chez Gandi' + ' ' +condensat}
response = requests.post(
webhook_url, data=json.dumps(slack_data),
headers={'Content-Type': 'application/json'}
)
os.remove('dnskey.log')
Suppression de l'ancien fingerprint chez gandi 24 heures après la nouvelle rotation des clés
import xmlrpc.client
import json
import requests
import time
import os
import subprocess
import fileinput
import sys
api = xmlrpc.client.ServerProxy('https://rpc.gandi.net/xmlrpc/')
webhook_url = 'https://hooks.slack.com/services/TBFxxxxxx7/Bxxxxxxx/FxxxxxxxNImR' # mettre votre webhook_url
apikey = 'xxxxxxxxxxxxxxxW8yrgGE1Qu' # cle de production gandi "https://v4.gandi.net/admin/api_key"
# nom du domaine
cmd = "grep name /mnt/docker/nsd/conf/nsd.conf | cut -d ':' -f2 | tr '\n' ' '"
process = subprocess.Popen(cmd, stdout=subprocess.PIPE, shell=True)
temp = process.communicate()[0]
domain = str(temp.decode())[1:-1]
extension=".zsk.key"
# recuperation du chemin de la cle KSK
file_path = "/mnt/docker/nsd/zones/K"+domain+extension
# Date du jour - date modification fichier par rapport a 1970
# (Nombre d'heures ecoulees depuis le dernier renouvellement des cles)
date_modif = (int(time.time()) - int(os.stat(file_path).st_mtime)) /3600
date = int(date_modif)
# on considere necessaire d'attendre 24 heures
# pour la propagadion de l'enregistrement dnssec
# avant d effacer le plus ancien
if date > 24:
# Afficher liste fingerprint de gandi
keys = api.domain.dnssec.list(apikey, domain)
fichier = open("liste.txt", "w")
fichier.write(str(keys))
fichier.close()
# Afficher les ID du(des) fingerprint(s)
cmd = "grep id liste.txt | cut --delimiter=, -f7 | cut -d ',' -f1 | cut -d ':' -f2 | cut -d ' ' -f2 | tr '\n' ' '"
process = subprocess.Popen(cmd, stdout=subprocess.PIPE, shell=True)
id = process.communicate()[0]
a = str(id.decode())[0:-1]
a = int(a)
cmd = "grep id liste.txt | cut --delimiter=, -f15 | cut -d ',' -f1 | cut -d ':' -f2 | cut -d ' ' -f2 | tr '\n' ' '"
process = subprocess.Popen(cmd, stdout=subprocess.PIPE, shell=True)
id = process.communicate()[0]
b = str(id.decode())[0:-1]
# Si le deuxieme enregistrement n'existe pas, on quitte le programme
if b == '':
slack_data = {'text': "il n'y a qu'un seul enregistrement"}
response = requests.post(
webhook_url, data=json.dumps(slack_data),
headers={'Content-Type': 'application/json'}
)
os.remove('liste.txt')
sys.exit(0)
else:
b = int(b)
# s'il y a plus d'un enregistrement on efface le plus ancien
# ( par default gandi le place en premier)
if a > 0 and b > 0:
slack_data = {'text': "Effacement en cours de l'enregistrement"}
response = requests.post(
webhook_url, data=json.dumps(slack_data),
headers={'Content-Type': 'application/json'}
)
op = api.domain.dnssec.delete(apikey, a)
os.remove('liste.txt')
else:
slack_data = {'text': "Le delais des 24 heures n'est pas expire"}
response = requests.post(
webhook_url, data=json.dumps(slack_data),
headers={'Content-Type': 'application/json'}
)
J'ai testé le lancement du script avec certbot au renouvellement des certificats en ajoutant le script de la manière suivante
--post-hook "update-mail-tlsa && python new_dnssec.py"
et ca fonctionne parfaitement, du coup les cles dnssec sont automatiquement renouvellées tous les 3 mois en meme temps que les certificats.
https://mondedie.fr/d/10326-tuto-certificat-wilcard-et-serveur-dns-autoritaire-nsd-dnssec-docker
Pour ceux qui utilisent acme (exemple)
--reloadcmd "docker-compose -f /docker/docker-compose.yml restart nginx && /usr/local/bin/update-mail-tlsa && python new_dnssec.py"
https://mondedie.fr/d/10307-certificat-classique-ou-wildcard/19
Pour le script de suppression, il suffit de le mettre en cron toutes les heures par exemple.