Creation et exposition d'un service DNS publique
Qu'est-ce que le DNS ?
Le Domain Name System (DNS) est un système qui fournit des noms lisibles par l'homme pour les ordinateurs, services et autres ressources connectés à Internet. Les enregistrements de base permettent de traduire un Nom de domaine (que les humains peuvent comprendre) en une IP Address (que l'ordinateur comprend pour le routage).
DNS est utilisé tout le temps, comme en ce moment, pour accéder à ce blog, votre ordinateur fait une requête DNS pour traduire lunik.tiwabbit.fr
en une [IP] [adresse-ip-wikipedia]. Ensuite, il a demandé la page Web à l'adresse IP résolue par la requête DNS.
Vous pouvez visualiser ce comportement en ouvrant la console développeur sur votre navigateur web :
Warning
Toutes les adresses IP et les noms DNS utilisés dans cet article de blog ne sont plus utilisés par mon projet. S'il vous plaît, ignorez-les.
Faire une requête DNS
Vous pouvez effectuer une requête de base DNS en utilisant nslookup
sur votre terminal :
Vous obtiendrez quelque chose comme :
Nom : lunik.tiwabbit.fr
Adresse : 13.224.247.30
Nom : lunik.tiwabbit.fr
Adresse : 13.224.247.31
Nom : lunik.tiwabbit.fr
Adresse : 13.224.247.51
Nom : lunik.tiwabbit.fr
Adresse : 13.224.247.100
Où déployer le service ?
J'ai décidé d'héberger mon service DNS sur un fournisseur de cloud public nommé Scaleway.
Voici une vue globale de l'architecture :
J'ai décidé d'exposer deux serveurs publics DNS un dans chaque région : France (Paris 1) et Pays-Bas (Amsterdam 1).
J'ai choisi ces deux régions car elles sont les seules à proposer des instances Stardust. Les serveurs DNS ne nécessitent pas une tonne de ressources, cela me permet de réduire le coût du projet au minimum.
Les deux serveurs sont exposés avec des adresses publiques flexible
IP (une v4 et une v6 chacune) qui peuvent être résolues via deux entrées DNS pour plus de commodité.
La sécurité était ma principale préoccupation, j'ai donc mis en place un groupe de sécurité sur mes instances me permettant de filtrer le trafic entrant et sortant.
Déploiement de l'infrastructure
Maintenant que je sais quelle architecture je veux pour le service. Je dois le déployer sur le fournisseur de cloud. J'ai décidé d'utiliser Terraform pour créer toutes les ressources et les lier ensemble.
ça a été assez simple de créer ma ressource en suivant la documentation Terraform. Après 1 heure j'avais le code nécessaire. Il ne me restait plus qu'à l'appliquer.
Voici à quoi ressemblait le plan
(tronqué) :
Terraform will perform the following actions:
# scaleway_instance_ip.france["tiwabbit-dns-01"] will be created
+ resource "scaleway_instance_ip" "france" {
+ address = (known after apply)
+ id = (known after apply)
+ reverse = "dns01.tiwabbit.fr"
+ server_id = (known after apply)
+ zone = "fr-par-1"
}
# scaleway_instance_security_group.france will be created
+ resource "scaleway_instance_security_group" "france" {
+ description = "dns"
+ enable_default_security = true
+ external_rules = false
+ id = (known after apply)
+ inbound_default_policy = "drop"
+ name = "public-dns"
+ outbound_default_policy = "accept"
+ stateful = true
+ zone = "fr-par-1"
+ inbound_rule {
+ action = "accept"
+ ip = "X.X.X.X"
+ port = 22
+ protocol = "TCP"
}
+ inbound_rule {
+ action = "accept"
+ ip_range = "0.0.0.0/0"
+ port = 53
+ protocol = "UDP"
}
+ inbound_rule {
+ action = "accept"
+ ip_range = "::/0"
+ port = 53
+ protocol = "UDP"
}
}
# scaleway_instance_server.france["tiwabbit-dns-01"] will be created
+ resource "scaleway_instance_server" "france" {
+ enable_dynamic_ip = false
+ enable_ipv6 = true
+ id = (known after apply)
+ image = "fr-par-1/5c8bbf4b-10f0-4cac-863b-4561781043ff"
+ ip_id = (known after apply)
+ ipv6_address = (known after apply)
+ ipv6_gateway = (known after apply)
+ ipv6_prefix_length = (known after apply)
+ name = "tiwabbit-dns-01"
+ private_ip = (known after apply)
+ public_ip = (known after apply)
+ security_group_id = "fr-par-1/dns"
+ state = "started"
+ type = "STARDUST1-S"
+ zone = "fr-par-1"
+ root_volume {
+ size_in_gb = "10"
+ volume_id = (known after apply)
}
}
[...]
Plan: 8 to add, 0 to change, 0 to destroy.
Après avoir terminé la création, voici ce que j'avais sur la console Scaleway :
Instances :
Volumes :
[Adresses IP][adresse-ip-wikipedia] :
Groupes de sécurité :
Ici, vous pouvez voir que je n'autorise le trafic entrant que sur le port
53
qui est celui utilisé par les serveurs DNS. (La première règle avec le port 22
me permet de gérer le serveur depuis un emplacement privé en utilisant SSH)
Installation et configuration du service
Maintenant que j'ai deux nouveaux serveurs à ma disposition, je dois installer et configurer un service DNS à faire tourner dessus.
Ils tournent avec Fedora 32 avec un 5.6
Linux Kernel :
[root@tiwabbit-dns-01 ~]# screenfetch
/:-------------:\ root@tiwabbit-dns-01
:-------------------:: OS: Fedora
:-----------/shhOHbmp---:\ Kernel: x86_64 Linux 5.6.6-300.fc32.x86_64
/-----------omMMMNNNMMD ---: Uptime: 18m
:-----------sMMMMNMNMP. ---: Packages: 405
:-----------:MMMdP------- ---\ Shell: bash 5.0.11
,------------:MMMd-------- ---: Disk: 1.0G / 9.6G (11%)
:------------:MMMd------- .---: CPU: AMD EPYC 7281 16-Core @ 2.096GHz
:---- oNMMMMMMMMMNho .----: RAM: 293MiB / 969MiB
:-- .+shhhMMMmhhy++ .------/
:- -------:MMMd--------------:
:- --------/MMMd-------------;
:- ------/hMMMy------------:
:-- :dMNdhhdNMMNo------------;
:---:sdNMMMMNds:------------:
:------:://:-------------::
:---------------------://
Fait amusant les instance Scaleway Stardust fonctionne sur AMD EPYC SoC !
J'ai décidé d'utiliser le serveur Bind9 DNS car il est déjà packagé dans de nombreuses distributions, il y a beaucoup de documentation et la communauté est importante.
J'utilise Ansible pour déployer toute la pile : config linux de base, Bind9, Firewalld, Fail2Ban
Tip
J'ai déjà parlé de Firewalld et de Fail2Ban dans un autre article de blog : Sécurisation de mon point d'entré web des menaces externes
Mais pour les besoins de cet article de blog, je vais détailler les commandes bash
équivalentes qui peuvent être utilisées.
Installation et configuration de Bind9
L'installation de Bind9 est assez simple. Comme il est déjà packagé, je n'ai qu'à faire une simple commande pour l'installer :
[root@tiwabbit-dns-01 ~]# dnf install bind bind-utils
Last metadata expiration check: 0:02:41 ago on Fri 05 Nov 2021 09:49:15 AM UTC.
Dependencies resolved.
============================================================================================================================================================================================================
Package Architecture Version Repository Size
============================================================================================================================================================================================================
Installing:
bind x86_64 32:9.11.28-1.fc32 updates 2.0 M
bind-utils x86_64 32:9.11.28-1.fc32 updates 233 k
Installing dependencies:
bind-dnssec-doc noarch 32:9.11.28-1.fc32 updates 46 k
bind-libs x86_64 32:9.11.28-1.fc32 updates 90 k
bind-libs-lite x86_64 32:9.11.28-1.fc32 updates 1.1 M
bind-license noarch 32:9.11.28-1.fc32 updates 16 k
fstrm x86_64 0.5.0-2.fc32 fedora 28 k
mariadb-connector-c x86_64 3.1.12-1.fc32 updates 203 k
mariadb-connector-c-config noarch 3.1.12-1.fc32 updates 11 k
policycoreutils-python-utils noarch 3.0-2.fc32 fedora 83 k
protobuf-c x86_64 1.3.2-2.fc32 fedora 35 k
python3-bind noarch 32:9.11.28-1.fc32 updates 64 k
Installing weak dependencies:
bind-dnssec-utils x86_64 32:9.11.28-1.fc32 updates 128 k
Transaction Summary
============================================================================================================================================================================================================
Install 13 Packages
Total download size: 4.0 M
Installed size: 10 M
Is this ok [y/N]:
Une fois installé, en utilisant la documentation Bind9 j'ai mis la configuration suivante dans /etc/named.conf
:
acl "managment" {
X.X.X.X/32;
};
acl "public" {
0.0.0.0/0;
::/0;
};
options {
dump-file "/etc/named/data/cache_dump.db";
statistics-file "/etc/named/data/named_stats.txt";
memstatistics-file "/etc/named/data/named_mem_stats.txt";
secroots-file "/etc/named/data/named.secroots";
recursing-file "/etc/named/data/named.recursing";
listen-on port 53 { any; };
listen-on-v6 port 53 { any; };
allow-transfer { none; };
max-cache-size 70%;
allow-query-cache {
127.0.0.1;
localhost;
managment;
public;
};
allow-query {
127.0.0.1;
localhost;
managment;
public;
};
recursion yes;
allow-recursion {
127.0.0.1;
localhost;
managment;
public;
};
dnssec-enable yes;
dnssec-validation yes;
prefetch 4 10;
rate-limit {
ipv4-prefix-length 28;
ipv6-prefix-length 56;
responses-per-second 20;
window 5;
slip 3;
};
managed-keys-directory "/var/named/dynamic";
geoip-directory "/usr/share/GeoIP";
pid-file "/run/named/named.pid";
session-keyfile "/run/named/session.key";
hostname "dns01.tiwabbit.fr";
server-id "dns01.tiwabbit.fr";
/* https://fedoraproject.org/wiki/Changes/CryptoPolicy */
include "/etc/crypto-policies/back-ends/bind.config";
};
statistics-channels {
inet 127.0.0.1 port 8053 allow { 127.0.0.1; };
};
logging {
channel client_file {
file "/var/log/named/client.log" versions 3 size 5m;
severity dynamic;
print-time yes;
print-category yes;
print-severity yes;
};
channel cname_file {
file "/var/log/named/cname.log" versions 3 size 5m;
severity dynamic;
print-time yes;
print-category yes;
print-severity yes;
};
channel config_file {
file "/var/log/named/config.log" versions 3 size 5m;
severity dynamic;
print-time yes;
print-category yes;
print-severity yes;
};
channel database_file {
file "/var/log/named/database.log" versions 3 size 5m;
severity dynamic;
print-time yes;
print-category yes;
print-severity yes;
};
channel default_file {
file "/var/log/named/default.log" versions 3 size 5m;
severity dynamic;
print-time yes;
print-category yes;
print-severity yes;
};
channel delegation-only_file {
file "/var/log/named/delegation-only.log" versions 3 size 5m;
severity dynamic;
print-time yes;
print-category yes;
print-severity yes;
};
channel dispatch_file {
file "/var/log/named/dispatch.log" versions 3 size 5m;
severity dynamic;
print-time yes;
print-category yes;
print-severity yes;
};
channel dnssec_file {
file "/var/log/named/dnssec.log" versions 3 size 5m;
severity dynamic;
print-time yes;
print-category yes;
print-severity yes;
};
channel dnstap_file {
file "/var/log/named/dnstap.log" versions 3 size 5m;
severity dynamic;
print-time yes;
print-category yes;
print-severity yes;
};
channel edns-disabled_file {
file "/var/log/named/edns-disabled.log" versions 3 size 5m;
severity dynamic;
print-time yes;
print-category yes;
print-severity yes;
};
channel general_file {
file "/var/log/named/general.log" versions 3 size 5m;
severity dynamic;
print-time yes;
print-category yes;
print-severity yes;
};
channel lame-servers_file {
file "/var/log/named/lame-servers.log" versions 3 size 5m;
severity dynamic;
print-time yes;
print-category yes;
print-severity yes;
};
channel network_file {
file "/var/log/named/network.log" versions 3 size 5m;
severity dynamic;
print-time yes;
print-category yes;
print-severity yes;
};
channel notify_file {
file "/var/log/named/notify.log" versions 3 size 5m;
severity dynamic;
print-time yes;
print-category yes;
print-severity yes;
};
channel queries_file {
file "/var/log/named/queries.log" versions 3 size 5m;
severity dynamic;
print-time yes;
print-category yes;
print-severity yes;
};
channel query-errors_file {
file "/var/log/named/query-errors.log" versions 3 size 5m;
severity dynamic;
print-time yes;
print-category yes;
print-severity yes;
};
channel rate-limit_file {
file "/var/log/named/rate-limit.log" versions 3 size 5m;
severity dynamic;
print-time yes;
print-category yes;
print-severity yes;
};
channel resolver_file {
file "/var/log/named/resolver.log" versions 3 size 5m;
severity dynamic;
print-time yes;
print-category yes;
print-severity yes;
};
channel rpz_file {
file "/var/log/named/rpz.log" versions 3 size 5m;
severity dynamic;
print-time yes;
print-category yes;
print-severity yes;
};
channel security_file {
file "/var/log/named/security.log" versions 3 size 5m;
severity dynamic;
print-time yes;
print-category yes;
print-severity yes;
};
channel spill_file {
file "/var/log/named/spill.log" versions 3 size 5m;
severity dynamic;
print-time yes;
print-category yes;
print-severity yes;
};
channel trust-anchor-telemetry_file {
file "/var/log/named/trust-anchor-telemetry.log" versions 3 size 5m;
severity dynamic;
print-time yes;
print-category yes;
print-severity yes;
};
channel unmatched_file {
file "/var/log/named/unmatched.log" versions 3 size 5m;
severity dynamic;
print-time yes;
print-category yes;
print-severity yes;
};
channel update_file {
file "/var/log/named/update.log" versions 3 size 5m;
severity dynamic;
print-time yes;
print-category yes;
print-severity yes;
};
channel update-security_file {
file "/var/log/named/update-security.log" versions 3 size 5m;
severity dynamic;
print-time yes;
print-category yes;
print-severity yes;
};
channel xfer-in_file {
file "/var/log/named/xfer-in.log" versions 3 size 5m;
severity dynamic;
print-time yes;
print-category yes;
print-severity yes;
};
channel xfer-out_file {
file "/var/log/named/xfer-out.log" versions 3 size 5m;
severity dynamic;
print-time yes;
print-category yes;
print-severity yes;
};
category client { client_file; default_debug; };
category cname { cname_file; default_debug; };
category config { config_file; default_debug; };
category database { database_file; default_debug; };
category default { default_file; default_debug; };
category delegation-only { delegation-only_file; default_debug; };
category dispatch { dispatch_file; default_debug; };
category dnssec { dnssec_file; default_debug; };
category dnstap { dnstap_file; default_debug; };
category edns-disabled { edns-disabled_file; default_debug; };
category general { general_file; default_debug; };
category lame-servers { lame-servers_file; default_debug; };
category network { network_file; default_debug; };
category notify { notify_file; default_debug; };
category queries { queries_file; default_debug; };
category query-errors { query-errors_file; default_debug; };
category rate-limit { rate-limit_file; default_debug; };
category resolver { resolver_file; default_debug; };
category rpz { rpz_file; default_debug; };
category security { security_file; default_debug; };
category spill { spill_file; default_debug; };
category trust-anchor-telemetry { trust-anchor-telemetry_file; default_debug; };
category unmatched { unmatched_file; default_debug; };
category update { update_file; default_debug; };
category update-security { update-security_file; default_debug; };
category xfer-in { xfer-in_file; default_debug; };
category xfer-out { xfer-out_file; default_debug; };
};
Waouh c'est beaucoup !
Les lignes importantes à configurer sont :
Listes de contrôle d'accès
Les ACLs permettent de choisir le comportement du service en fonction du client IP address.
Jetons un œil à ma configuration :
Ici, je crée deux groupes ACL : public
et managment
. Dans chacun de ces blocs acl
, je peux mettre autant de CIDR que je veux.
public
contient l'IPv4 et l'IPv6 global CIDR. managment
contient une unique IP CIDR (celle utilisée pour configurer le service).
Vous pouvez réutiliser ces groupes ACL dans d'autres parties de la configuration comme dans allow-query
, allow-query-cache
, allow-recursion
.
Comportement des requêtes
J'ai choisi de n'implémenter que trois comportements de requête pour rester simple :
allow-query
définit qui est autorisé à interroger mon service DNS. Si l'IP du client n'est pas dans la liste, le serveur ne répondra pas.allow-query-cache
définit depuis quel client le serveur doit mettre en cache les réponses. Cela permet à la requête d'être résolue plus rapidement la prochaine fois.allow-recursion
définit qui peut faire des requêtes récursives. Query recursion est un mécanisme DNS qui trouve l'IP associé à une entrée DNS en faisant tout les requêtes nécessaires une par une depuis les serveurs racine. Cela permet d'être indépendant lors de la résolution des requêtes (vous n'avez pas besoin de transmettre la requête à un autre serveur public DNS comme8.8.8.8
ou1.1.1.1
)
Prefetching
Tout est dans le nom. Cette configuration permet au serveur de faire une requête DNS par lui-même en prévision d'autres requêtes. Cela permet d'être plus rapide à répondre la plupart du temps si une entrée DNS est demandée très souvent.
Chaque entrée DNS a un Time To Live (ou TTL). Vous pouvez l'obtenir en utilisant dig
:
dig lunik.tiwabbit.fr
; <<>> DiG 9.11.28-RedHat-9.11.28-1.fc32 <<>> lunik.tiwabbit.fr
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 41640
;; flags: qr rd ra; QUERY: 1, ANSWER: 4, AUTHORITY: 0, ADDITIONAL: 1
;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 1232
;; QUESTION SECTION:
;lunik.tiwabbit.fr. IN A
;; ANSWER SECTION:
lunik.tiwabbit.fr. 66 IN A 52.222.158.86
lunik.tiwabbit.fr. 66 IN A 52.222.158.38
lunik.tiwabbit.fr. 66 IN A 52.222.158.55
lunik.tiwabbit.fr. 66 IN A 52.222.158.103
;; Query time: 0 msec
;; SERVER: 10.194.3.3#53(10.194.3.3)
;; WHEN: Fri Nov 05 10:13:42 UTC 2021
;; MSG SIZE rcvd: 110
Dans la ANSWER SECTION
:
66
est le TTL de cette entrée DNS. Cela signifie que dans 66
secondes, elle n'est plus valide et le client doit faire une autre demande pour obtenir la nouvelle IP (la plupart du temps, cela ne change pas).
Dans ma configuration j'ai :
Si le serveur a mis en cache une entrée DNS avec 4
secondes ou moins de TTL restant alors il fera une requête DNS pour actualiser celle mise en cache. 10
est un paramètre facultatif qui définit l'éligibilité de l'enregistrement pour la prélecture.
Limitation de requêtes
C'est peut-être l'un des paramètres les plus importants d'un serveur public DNS. Le but de cette configuration est de limiter le nombre de réponses du serveur si un client demande plusieurs fois la même entrée DNS. Il est très utile pour atténuer les attaques par amplification DNS.
Voici la configuration que j'ai :
rate-limit {
ipv4-prefix-length 28;
ipv6-prefix-length 56;
responses-per-second 20;
window 5;
slip 3;
};
Si un client demande plus de 20
fois la même entrée DNS sur une période de 5
secondes, le serveur ignore 3
réponses avant de répondre (indéfiniment).
Pour empêcher les attaques via de large sous-réseaux, la limite de débit s'étend à un /28
sous-réseau IPv4 et à un /56
sous-réseau IPv6.
Journalisation
j'ai fait le choix d'être très verbeux avec les logs c'est pourquoi j'ai configuré tous les canaux possibles.
Les plus intéressants sont
- queries
qui journalise chaque requête résolue par le serveur :
# /var/log/named/queries.log
05-Nov-2021 10:21:43.536 queries: info: client @0x7f76073dfe50 X.X.X.X#14821 (mms-iad.sp-prod.net): query: mms-iad.sp-prod.net IN A +E(0)DV (10.70.2.235)
05-Nov-2021 10:21:43.545 queries: info: client @0x7f760793d450 X.X.X.X#47583 (psja.isd.us): query: psja.isd.us IN MX +E(0)DV (10.70.2.235)
05-Nov-2021 10:21:43.547 queries: info: client @0x7f760759b400 X.X.X.X#50020 (global.reputation.invincea.com): query: global.reputation.invincea.com IN A +E(0)DV (10.70.2.235)
05-Nov-2021 10:21:43.556 queries: info: client @0x7f76075dddc0 X.X.X.X#38383 (forconzoomnyc233mmr.zoom.us): query: forconzoomnyc233mmr.zoom.us IN A + (10.70.2.235)
05-Nov-2021 10:21:43.566 queries: info: client @0x7f76077ba820 X.X.X.X#39161 (ps-membership.us-ctkip-ps3.dell.com): query: ps-membership.us-ctkip-ps3.dell.com IN A + (10.70.2.235)
05-Nov-2021 10:21:43.618 queries: info: client @0x7f7607a9f2c0 X.X.X.X#39161 (ps-membership.usgit.dell.com): query: ps-membership.usgit.dell.com IN A + (10.70.2.235)
05-Nov-2021 10:21:43.622 queries: info: client @0x7f76077fcbc0 X.X.X.X#56424 (icn.intl-global-adns.alibabacloud.com): query: icn.intl-global-adns.alibabacloud.com IN A + (10.70.2.235)
05-Nov-2021 10:21:43.623 queries: info: client @0x7f760765f400 X.X.X.X#38383 (forcon-zoomca193-123-14-158mmr.zoom.us): query: forcon-zoomca193-123-14-158mmr.zoom.us IN A + (10.70.2.235)
05-Nov-2021 10:21:43.635 queries: info: client @0x7f75f0ce5910 X.X.X.X#56548 (sfc-idzwww.riotgames.roblox.com.ru): query: sfc-idzwww.riotgames.roblox.com.ru IN A + (10.70.2.235)
rate-limit
qui enregistre tous les événements concernant le comportement de limitation de requêtes :
# /var/log/named/rate-limit.log
05-Nov-2021 10:21:39.631 rate-limit: info: client @0x7f76077cdd80 X.X.X.X#38383 (zoomff134-224-74-182mmrforcon.zoom.us): rate limit drop NXDOMAIN response to X.X.X.X/28 for zoom.us (4ef96ce9)
05-Nov-2021 10:21:39.694 rate-limit: info: client @0x7f75f0cf3f10 X.X.X.X#38383 (bisdtx-orgforcon.zoom.us): rate limit drop NXDOMAIN response to X.X.X.X/28 for zoom.us (4ef96ce9)
05-Nov-2021 10:21:39.752 rate-limit: info: client @0x7f76077fcbc0 X.X.X.X#38383 (kirklandwa-forcon.zoom.us): rate limit slip NXDOMAIN response to X.X.X.X/28 for zoom.us (4ef96ce9)
05-Nov-2021 10:21:39.863 rate-limit: info: client @0x7f76077eab00 X.X.X.X#38383 (friendsnrcforcon.zoom.us): rate limit drop NXDOMAIN response to X.X.X.X/28 for zoom.us (4ef96ce9)
05-Nov-2021 10:21:39.942 rate-limit: info: client @0x7f76078e8610 X.X.X.X#38383 (deltatrust-org-uk-forcon.zoom.us): rate limit drop NXDOMAIN response to X.X.X.X/28 for zoom.us (4ef96ce9)
05-Nov-2021 10:21:40.857 rate-limit: info: client @0x7f7607a13120 X.X.X.X#38383 (12mmrforcon.zoom.us): rate limit slip NXDOMAIN response to X.X.X.X/28 for zoom.us (4ef96ce9)
05-Nov-2021 10:21:40.871 rate-limit: info: client @0x7f76076509a0 X.X.X.X#38383 (datamarkgisforcon.zoom.us): rate limit drop NXDOMAIN response to X.X.X.X/28 for zoom.us (4ef96ce9)
05-Nov-2021 10:21:40.891 rate-limit: info: client @0x7f7607866d60 X.X.X.X#38383 (wvsd208-forcon.zoom.us): rate limit drop NXDOMAIN response to X.X.X.X/28 for zoom.us (4ef96ce9)
05-Nov-2021 10:21:40.907 rate-limit: info: client @0x7f7607146a70 X.X.X.X#38383 (zoomva198-251-217-184mmrforcon.zoom.us): rate limit slip NXDOMAIN response to X.X.X.X/28 for zoom.us (4ef96ce9)
05-Nov-2021 10:21:40.968 rate-limit: info: client @0x7f760713b020 X.X.X.X#38383 (zoomdvs185mmr-forcon.zoom.us): rate limit drop NXDOMAIN response to X.X.X.X/28 for zoom.us (4ef96ce9)
05-Nov-2021 10:21:43.761 rate-limit: info: *stop limiting NXDOMAIN responses to X.X.X.X/28 for buffer.com (bc75c42e)
05-Nov-2021 10:21:43.761 rate-limit: info: *stop limiting error responses to X.X.X.X/28
05-Nov-2021 10:21:43.761 rate-limit: info: *stop limiting NXDOMAIN responses to X.X.X.X/28 for restintergamma.nl (97dd2767)
05-Nov-2021 10:21:43.761 rate-limit: info: *stop limiting NXDOMAIN responses to X.X.X.X/28 for alibabacloud.com (d22fa033)
05-Nov-2021 10:21:43.761 rate-limit: info: *stop limiting NXDOMAIN responses to X.X.X.X/28 for dell.com (98e605b5)
05-Nov-2021 10:21:43.761 rate-limit: info: *stop limiting error responses to X.X.X.X/28
05-Nov-2021 10:21:43.761 rate-limit: info: *stop limiting NXDOMAIN responses to X.X.X.X/28 for zoom.us (4ef96ce9)
Surveillance
Je choisis d'utiliser Datadog pour surveiller les serveurs DNS.
Installation de l'agent Datadog
L'installation est assez simple avec Ansible car ils fournissent un role galaxy. Je l'ai utilisé avec la configuration suivante :
---
datadog_api_key: "{{ vault_datadog_api_key }}"
datadog_site: "datadoghq.eu"
datadog_agent_flavor: "datadog-agent"
datadog_agent_major_version: 7
datadog_enabled: yes
datadog_bind9_integration_version: 1.0.0
datadog_config:
logs_enabled: true
datadog_checks:
bind9:
init_config:
instances:
- name: bind9
url: "http://127.0.0.1:{{ bind_statistics_port }}"
logs:
- type: file
path: /var/log/named/queries.log
service: named
source: bind9
sourcecategory: queries
- type: file
path: /var/log/named/rate-limit.log
service: named
source: bind9
sourcecategory: rate-limit
J'ai activé les intégrations Bind9 et les journaux.
Une fois déployé, je peux voir mes agents via la console web Datadog dans le panneau Infrastructure
:
Métriques et journaux Datadog
Métriques
Vu que j'ai activé l'intégration Bind9 dans la configuration de l'agent, je dois faire de même dans la console Web. Dans le panneau Integrations
, recherchez Bind9
et suivez le guide de configuration.
Maintenant, je peux voir les métriques apparaître.
Journaux
Les journaux s'affichent déjà dans l'explorateur de journaux dans le panneau Logs
:
Mais Datadog ne sait pas comment interpréter ces journaux. Pour l'instant, il ne voit qu'une grande chaîne de caractères. Si je veux analyser ces journaux, Datadog doit les parser pour moi.
Dans le pipeline de journaux intégré Datadog dans le panneau Logs
, je peux définir une liste de modèles et d'actions pour parser le fichier journal produit par le service Bind9. Sur certains logiciels plus populaires, Datadog a déjà fait le travail pour vous, mais il semble que Bind9 soit une exception.
Définition des journaux d'analyse des pipelines
J'ai d'abord créé un nouveau pipeline et utilisé des filtres prédéfinis : source:bind9
et sourcecategory:queries
.
Ces deux filtres sont déduits de la configuration de l'agent Datadog précédente :
datadog_checks:
bind9:
[...]
logs:
[...]
- type: file
path: /var/log/named/queries.log
service: named
source: bind9
sourcecategory: queries
[...]
Maintenant que je vais uniquement parser les journaux à partir de la bonne source, j'ai besoin d'un parser Grok pour parser la chaîne de journal. Le parser Grok définit des blocs dans la chaîne et les place dans des variables. Lorsque la ligne de journal est parsé, elle renvoie un objet JSON magnifiquement formaté.
Les blocs sont définis par des expressions rationnelles "simplifiées" : number
, word
, date
, ip
, ...
Datadog me permet de le faire très simplement en ayant une vue d'analyse "en direct" où vous pouvez voir en temps réel quelle partie du journal est analysée par quel bloc.
Voici donc la règle finale de parsing Grok pour les journaux requêtes
:
default %{number}-%{word}-%{number}\s+%{date("HH:mm:ss.SSS"):timestamp}\s+queries:\s+%{word:status}:\s+client\s+@%{word:client.data}\s+%{ip:client.ip}\#%{number:client.port}\s+\(%{hostname:query.hostname}\):\s+query:\s+%{hostname:query.fqdn}\s+%{word:query.location}\s+%{word:query.qcode}\s+.*\s+\(%{ip:server.ip}\).*
Avec cet exemple :
27-Oct-2021 16:53:43.594 queries: info: client @0x7fad52ee1fd0 127.0.0.1#64318 (google.fr): query: google.fr IN A +E(0) (10.69.86.243)
{
"status": "info",
"query": {
"qcode": "A",
"location": "IN",
"hostname": "google.fr",
"fqdn": "google.fr"
},
"client": {
"ip": "127.0.0.1",
"data": "0x7fad52ee1fd0",
"port": 64318
},
"timestamp": 1636131223594,
"server": {
"ip": "10.69.86.243"
}
}
Génial !
Datadog permet d'améliorer ce nouvel objet JSON avec des métadonnées supplémentaires. Dans mon cas, puisque j'ai l'addresse IP du client, je peux utiliser l'analyseur GeoIP pour trouver des métadonnées sur cette IP. Maintenant, je peux déterminer le FAI, le pays et même la ville de ce client.
Voici un exemple :
05-Nov-2021 14:07:16.442 queries: info: client @0x7f6b462b2900 X.X.X.X#38383 (zoom.us): query: zoom.us IN A + (10.70.2.235)
{
"client": {
"geoip": {
"as": {
"domain": "online.net",
"name": "ONLINE S.A.S.",
"number": "AS12876",
"route": "51.15.0.0/16",
"type": "isp"
},
"city": {
"name": "Paris"
},
"continent": {
"code": "EU",
"name": "Europe"
},
"country": {
"iso_code": "FR",
"name": "France"
},
"ipAddress" : "X.X.X.X",
"location": {
"latitude": "48.85341",
"longitude" "2.3488"
},
"subdivision": {
"name": "Île-de-France"
},
"timezone": "Europe/Paris"
}
}
}
Tableau de bord
Je peux maintenant commencer la partie amusante de l'utilisation d'un service de surveillance : Make Dashboard and Graphs !
Voici ceux que j'ai créés :
Conclusion
J'ai maintenant un serveur public DNS pleinement opérationnel. Je peux maintenant faire des configurations avancées si je veux, comme bloquer certains noms de domaine (malware, trucs illégaux, ...).
Note : Si vous souhaitez rendre votre DNS public accessible à tous, vous pouvez le publier sur public-dns.info