Tu gères plusieurs domaines et tu aimerais sauvegarder toutes les configurations de tes zones DNS ?
Je me suis retrouvé confronté au même problème et j’ai voulu faire un outil qui me permettait de sauvergarder toutes mes zones DNS d’un seul coup.
J’ai découvert l’outil dns-crawler, et je l’ai adapté pour faire une sauvegarde simple et facile à utiliser. Je prends ensuite le résultat et je le décortique avec Python, pour en faire une base de données SQLite facile à utiliser dans le futur.
Ce graphe montre le flux du programme, depuis le chargement des bibliothèques et des données jusqu’à l’enregistrement final des données traitées. Il illustre également les étapes de conversion, d’explosion de colonnes et d’extraction de données effectuées avant la préparation finale des DataFrames et leur enregistrement dans une base de données SQLite.
Créer la liste des domaines à sauvegarder
Nous avons souvent une liste de sous-domaines qui vont se répéter au travers des domaines de nos différents services sur le web. Ce sont des sous-domaines utilisés pour l’authentification des courriels ou pour la vérification de l’appartenance du domaine pour différents services d’indexation. Un exemple, c’est le sous-domaine _dmarc
pour la validation DMARC.
Pour créer la liste de tous les domaines à sauvegarder, je conseille de listes les domaines dans un fichier dns-list.txt
. Puis, on liste les préfixes dnas un fichier prefix-list.txt
. On ajoute une ligne vide au début pour le cas sans préfixe.
On peut joindre les deux listes à l’aide de la commande Unix join
:
#!/bin/zsh join -t '' -j 2 prefix-list.txt dns-list.txt | sort -u > domain-list-n.txt tr < domain-list-n.txt -d '\000' > domain-list.txt rm domain-list-n.txt
L’outil join
va inclure des caractères null dans mon fichier intermédiaire domain-list-n.txt
, que j’enlève à la deuxième ligne avec tr
.
Extraire les données
J’utilise ensuite l’outil dns-crawler, qui retourne un fichier JSON pour chaque domaine. Je les écris tous dans un fichier JSONL, qui inclus un JSON par ligne.
dns-crawler domain-list.txt > results.jsonl
Nettoyer les données
Les fichiers JSON enregistrés contiennent beaucoup de données. Elles ne sont pas toutes nécessaires pour notre sauvegarde de zone DNS. Nous allons donc utiliser Python pour nettoyer ces données et les enregistrer dans une base de données SQLite prête à l’usage.
Je vais d’abord importer Pandas et SQLAlchemy
import pandas as pd from sqlalchemy import create_engine
Je vais ensuite charger les données JSONL.
results = pd.read_json('results.jsonl', lines=True)
Données parent
Je convertis la colonne parent
en DataFrame pour Pandas. Cette conversion facilite l’accès et le traitement des données contenues dans la colonne ‘parent’, étant donné que nous avons une structure de données complexe.
parent_df = results['parent'].apply(pd.Series)
La méthode explode
est utilisée pour transformer chaque valeur de liste en une ligne distincte. Enfin, les lignes avec des valeurs manquantes sont supprimées à l’aide de la méthode dropna
.
ns_df_ = pd.DataFrame({'ns': parent_df['ns']}).explode('ns').dropna()['ns'].apply(pd.Series)
Nous allons maintenant extraire les adresses IPv4 et IPv6 du DataFrame ns_df_
. Tout d’abord, nous remplissons les valeurs manquantes dans les colonnes ‘ipv4’ et ‘ipv6’ par des chaînes vides à l’aide de la méthode fillna
.
Ensuite, nous appliquons une fonction lambda pour extraire uniquement l’adresse IP de chaque dictionnaire contenu dans la liste. Les colonnes d’origine (‘ipv4’ et ‘ipv6’) sont supprimées à la fin.
Données results
Dans cette section, nous allons extraire les données contenues dans la colonne ‘results’, qui contient la plupart des informations publiques disponibles sur le serveur de noms.
results_df = results['results'].apply(lambda x: x['DNS_LOCAL']).apply(pd.Series)
Nous appliquons une fonction lambda pour extraire le dictionnaire ‘DNS_LOCAL’ de chaque valeur, puis convertissons ces dictionnaires en un nouveau DataFrame appelé results_df
. Chaque type d’enregistrement DNS se trouve dans une colonne.
Depuis ces données, je construis un DataFrame pour chaque type d’enregistrement DNS en extrayant la colonne, distribue la liste sur plusieurs lignes avec explode
, puis en convertissant en série Pandas.
Je termine en ajoutant un préfixe, sinon je ne sais plus de quelle colonne ça provient.
base_df = results.drop(columns=['parent', 'results']).copy() ns_df = ns_df_.explode(['ipv4_', 'ipv6_']).copy() mail_df = results_df['MAIL'].explode().dropna().apply(pd.Series).add_prefix('MAIL_') web4_df = results_df['WEB4'].explode().dropna().apply(pd.Series).add_prefix('WEB4_') web4www_df = results_df['WEB4_www'].explode().dropna().apply(pd.Series).add_prefix('WEB4_www_') web6_df = results_df['WEB6'].explode().dropna().apply(pd.Series).add_prefix('WEB6_') web6www_df = results_df['WEB6_www'].explode().dropna().apply(pd.Series).add_prefix('WEB6_www_') txt_df = results_df['TXT'].explode().dropna().apply(pd.Series).add_prefix('TXT_')
Suppression des données GeoIP et ajout de la date
Je n’ai pas utilisé GeoIP, donc je vais supprimer toutes les colonnes qui ont trait à ça. GeoIP est une base de données qui permet de géolocaliser des adresses IP. C’est assez précis, allant de la ville jusqu’au quartier.
web4_df = web4_df.drop(columns=['WEB4_geoip']) web4www_df = web4www_df.drop(columns=['WEB4_www_geoip']) web6_df = web6_df.drop(columns=['WEB6_geoip']) web6www_df = web6www_df.drop(columns=['WEB6_www_geoip'])
Sauvegarde des enregistrements DNS dans une base de données SQLite
Enfin, je vais enregistrer chacun des DataFrame produit dans une base de données SQLite. Cette base de données légère prend la forme d’un fichier sur l’ordinateur et est facile à transporter et à réutiliser.
Nous utilisons la méthode to_sql
de Pandas pour chaque table. Chacune représente un type d’enregistrement DNS.
Le nom de fichier de la base de données inclus la date du jour.
date_today = pd.to_datetime('today').strftime('%Y-%m-%d') engine = create_engine(f'sqlite:///dns_results_{date_today}.db') base_df.to_sql('base_df', engine, if_exists='replace', index=True) ns_df.to_sql('ns_df', engine, if_exists='replace', index=True) mail_df.to_sql('mail_df', engine, if_exists='replace', index=True) web4_df.to_sql('web4_df', engine, if_exists='replace', index=True) web4www_df.to_sql('web4www_df', engine, if_exists='replace', index=True) web6_df.to_sql('web6_df', engine, if_exists='replace', index=True) web6www_df.to_sql('web6www_df', engine, if_exists='replace', index=True) txt_df.to_sql('txt_df', engine, if_exists='replace', index=True)
Conclusion
Ce script Python permet d’extraire des données d’un fichier JSONL généré par l’outil dns-extract
, de les traiter et de les stocker dans une base de données SQLite pour une utilisation ultérieure.
Le code complet est disponible sur mon serveur de code: https://git.jevalide.ca/partage/dns-backup-tool/src/branch/main
Il pourrait avoir évolué depuis l’écriture de cette page.
Consultation Express 🧠
La consultation express, c’est une heure où je me consacre entièrement à résoudre tes problèmes informatiques avec toi.