Projet

Général

Profil

Wiki » Historique » Révision 22

Révision 21 (sacha, 10/12/2023 17:01) → Révision 22/35 (sacha, 10/12/2023 17:02)

{{>toc}} 

 # DNS récursif Ouvert DoH DoT & haute disponibilité 
 **45.67.81.23 - dns.aquilenet.fr** 



 On propos un DNS récursif ouvert cf https://www.aquilenet.fr/services/dns/ https://dns.aquilenet.fr/ 
 On a mis aussi un DNS ouvert sur l'IP 45.67.81.23 car facile à retenir, avec l'adresse de dns.aquilenet.fr (voir [http://dns.aquilenet.fr](http://dns.aquilenet.fr)) . Nous en avons profité pour ajouter [DoH](https://fr.wikipedia.org/wiki/DNS_over_HTTPS) et [DoT](https://fr.wikipedia.org/wiki/DNS_over_TLS) et d'avoir cette adresse en IP Virtuelle partagée entre pan1 et pan2. 
 Cette IP virtuelle est portée par Keepalived sur pan1 le primaire et pan2 le secondaire. Si le démon Keepalived est arrêté sur pan1, l'ip bascule sur pan2. Si on redémarre le démon sur pan1, ce dernier reprend l'IP: pratique pour les mises à jour, le service continue à fonctionner. 

 Pan[1|2] portent l'ip 45.67.81.23 gracieusement offerte par les copains de https://yaal.coop/fr un vlan est dédié sur ce réseau de l'hyperviseur. 

 ## Configurations communes à pan1 et pan2 

 ### Nginx 

 * /etc/nginx/sites-enabled/default 


 ~~~ 
 server { 
     listen         45.67.81.23:80 ; 
     listen         [::]:80 ; 

     location ~ /.well-known/acme-challenge { 
         allow all; 
         root /var/www/letsencrypt; 
     } 
     location / { 
         return 301 https://dns.aquilenet.fr; 
         } 

     server_name dns.aquilenet.fr; # managed by Certbot 

     location ~ /.well-known/acme-challenge { 
         allow all; 
         root /var/www/letsencrypt; 
     } 
 } 
 server { 
     listen         45.67.81.23:443 ssl http2; 
     listen         [::]:443 ssl http2; 
     root /var/www/dns.aquilenet.fr; 
     index index.htm; 

     ssl_certificate       /etc/unbound/ssl/dns.aquilenet.fr/fullchain.pem; 
     ssl_certificate_key /etc/unbound/ssl/dns.aquilenet.fr/privkey.pem; 
 location /dns-query { 
     grpc_pass grpc://127.0.0.1:443; 
         } 
 } 

 root@pan1:/etc/unbound/ssl# vi /var/www/dns.aquilenet.fr/index.htm  
 root@pan1:/etc/unbound/ssl# ip a 
 1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000 
     link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 
     inet 127.0.0.1/8 scope host lo 
        valid_lft forever preferred_lft forever 
     inet6 ::1/128 scope host noprefixroute  
        valid_lft forever preferred_lft forever 
 2: enX0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP group default qlen 1000 
     link/ether 00:16:3e:01:01:21 brd ff:ff:ff:ff:ff:ff 
     inet 45.67.81.21/24 brd 45.67.81.255 scope global enX0 
        valid_lft forever preferred_lft forever 
     inet 45.67.81.23/32 scope global enX0 
        valid_lft forever preferred_lft forever 
     inet6 fe80::216:3eff:fe01:121/64 scope link  
        valid_lft forever preferred_lft forever 
 root@pan1:/etc/unbound/ssl# vi /var/www/dns.aquilenet.fr/index.htm  
 root@pan1:/etc/unbound/ssl# vi /var/www/dns.aquilenet.fr/index.htm  
 root@pan1:/etc/unbound/ssl# vi /var/www/dns.aquilenet.fr/index.htm  
 root@pan1:/etc/unbound/ssl# vi /var/www/dns.aquilenet.fr/index.htm  
 root@pan1:/etc/unbound/ssl# cat /etc/nginx/sites-enabled/default  
 server { 
     listen         45.67.81.23:80 ; 
     listen         [::]:80 ; 

     location ~ /.well-known/acme-challenge { 
         allow all; 
         root /var/www/letsencrypt; 
     } 
     location / { 
         return 301 https://dns.aquilenet.fr; 
         } 

     server_name dns.aquilenet.fr; # managed by Certbot 

     location ~ /.well-known/acme-challenge { 
         allow all; 
         root /var/www/letsencrypt; 
     } 
 } 
 server { 
     listen         45.67.81.23:443 ssl http2; 
     listen         [::]:443 ssl http2; 
     root /var/www/dns.aquilenet.fr; 
     index index.htm; 

     ssl_certificate       /etc/unbound/ssl/dns.aquilenet.fr/fullchain.pem; 
     ssl_certificate_key /etc/unbound/ssl/dns.aquilenet.fr/privkey.pem; 
 location /dns-query { 
     grpc_pass grpc://127.0.0.1:443; 
         } 
 } 


 ~~~ 

 ### Unbound 

 ~~~ 
 server { 
     listen         45.67.81.23:80 ; 
     listen         [::]:80 ; 

     location ~ /.well-known/acme-challenge { 
         allow all; 
         root /var/www/letsencrypt; 
     } 
     location / { 
         return 301 https://dns.aquilenet.fr; 
         } 

     server_name dns.aquilenet.fr; # managed by Certbot 

     location ~ /.well-known/acme-challenge { 
         allow all; 
         root /var/www/letsencrypt; 
     } 
 } 
 server { 
     listen         45.67.81.23:443 ssl http2; 
     listen         [::]:443 ssl http2; 
     root /var/www/dns.aquilenet.fr; 
     index index.htm; 

     ssl_certificate       /etc/unbound/ssl/dns.aquilenet.fr/fullchain.pem; 
     ssl_certificate_key /etc/unbound/ssl/dns.aquilenet.fr/privkey.pem; 
 location /dns-query { 
     grpc_pass grpc://127.0.0.1:443; 
         } 
 } 

 root@pan1:/etc/unbound/ssl# cat /etc/unbound/unbound 
 cat: /etc/unbound/unbound: Aucun fichier ou dossier de ce type 
 root@pan1:/etc/unbound/ssl# cat /etc/unbound/unbound.conf 
 include-toplevel: "/etc/unbound/unbound.conf.d/*.conf" 

 server: 
     verbosity: 1  

     access-control: 0.0.0.0/0 allow  
     interface: 45.67.81.23 
 #      interface: 45.67.81.23@443 
     interface: 45.67.81.23@853 

 http-notls-downstream: yes 
     interface: 127.0.0.1@443 

     port: 53 
     tls-port: 853 
     https-port: 443 
    
     root-hints: "/usr/share/dns/root.hints" 
 
     harden-glue: yes 
     harden-large-queries: yes 
     harden-dnssec-stripped: yes 
     harden-below-nxdomain: yes 
     harden-referral-path: yes 
     harden-algo-downgrade: no # false positives with improperly configured zones 
     use-caps-for-id: no # makes lots of queries fail 
     edns-buffer-size: 1232 
     rrset-roundrobin: yes 
     cache-min-ttl: 300 
     cache-max-ttl: 86400 
     serve-expired: yes 
     harden-algo-downgrade: yes 
     harden-short-bufsize: yes 
     hide-identity: yes 
     identity: "dns.aquilenet.fr" 
     hide-version: yes 
     do-daemonize: no 
     neg-cache-size: 4m 
     qname-minimisation: yes 
     deny-any: yes 
     minimal-responses: yes 
     prefetch: yes 
     prefetch-key: yes 
     num-threads: 2  
     msg-cache-size: 50m 
     rrset-cache-size: 100m 
     so-reuseport: yes 
     so-rcvbuf: 4m 
     so-sndbuf: 4m 
     unwanted-reply-threshold: 100000 
     log-queries: no 
     log-replies: no 
     log-servfail: no 
     log-local-actions: no 
     logfile: /dev/null 

     # imposer la QNAME minimisation (RFC 7816) 
     qname-minimisation: yes 
     # même si le serveur faisant autorité ne le veut pas 
     #     après discussion, il est possible que cette option ne soit 
     #     pas recommandée dans le cadre d'un résolveur ouvert 
     qname-minimisation-strict: yes 

    # DoH 
    tls-service-key: "/etc/letsencrypt/live/dns.aquilenet.fr/privkey.pem" 
    tls-service-pem: "/etc/letsencrypt/live/dns.aquilenet.fr/cert.pem" 

 ~~~ 

 ### AppArmor 

 *    /etc/apparmor.d/usr.sbin.unbound 
 décomenter: 
   include <local/usr.sbin.unbound> 

 * /etc/apparmor.d/local/usr.sbin.unbound 
 ~~~ 
 /etc/letsencrypt/archive/** r, 
 /etc/letsencrypt/live/** r, 
 ~~~ 
 appliquer avec: `apparmor_parser -r /etc/apparmor.d/usr.sbin.unbound` 

 

 ### Iptables 

 ~~~ 
 *filter 
 :INPUT DROP 
 :FORWARD DROP 
 :OUTPUT ACCEPT 

 -A INPUT ! -i lo -s 127.0.0.0/8 -j DROP 
 -A INPUT -s 0.0.0.0 -i eth0 -j DROP 
 -A INPUT -d 0.0.0.0 -i eth0 -j DROP 
 -A INPUT -s 255.255.255.255 -i eth0 -j DROP 
 -A INPUT -d 255.255.255.255 -i eth0 -j DROP 
 -A INPUT -m conntrack --ctstate INVALID -j DROP 

 -A INPUT -p tcp -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT 
 -A INPUT -p udp ! --dport 53 -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT 
 -A INPUT -i lo -j ACCEPT 
 -A OUTPUT -o lo -j ACCEPT 

 # SSH no more 10 attempts in 3 min 
 -A INPUT -i eth0 -p tcp --dport 55555 -m recent --name SSH --update --hitcount 10 --seconds 180 -j DROP 
 -A INPUT -i eth0 -p tcp --dport 55555 -m recent --name SSH --set -j ACCEPT 

 # Web 
 -A INPUT -p tcp --dport 80 -j ACCEPT 

 # smtp 
 -A INPUT -i eth0 -p tcp --dport 25 -j ACCEPT 
 # smtps 
 -A INPUT -i eth0 -p tcp --dport 465 -j ACCEPT 
 # submission 
 -A INPUT -i eth0 -p tcp --dport 587 -j ACCEPT 

 # DNS 
 # récursif, lisible par nos abonnés seulement 
 -N dns_limit 
 -A dns_limit -s 185.233.100.0/22 -j ACCEPT 
 -A INPUT -d 45.67.81.23 -p tcp -m tcp --dport 53 -j dns_limit 
 -A INPUT -d 45.67.81.23 -p udp -m udp --dport 53 -j dns_limit 
 -A INPUT -d 45.67.81.23 -p tcp -m tcp --dport 443 -j dns_limit 
 -A INPUT -d 45.67.81.23 -p tcp -m tcp --dport 853 -j dns_limit 

 # mais aussi par le reste d'Internet, mais en rate-limit 
 -A dns_limit -m string --algo bm --hex-string "|00 00 ff 00 01|" --from 28 -m hashlimit --hashlimit-above 1/sec --hashlimit-burst 2 --hashlimit-mode srcip --hashlimit-name RL-DNS-ANY-v4 --hashlimit-srcmask 28 -m comment --comment "RATE-LIMIT DNS ANY QTYPE 1/s burst 2" -j DROP 
 -A dns_limit -m string --algo bm --hex-string "|00 00 ff 00 01|" --from 28 -m hashlimit --hashlimit-above 20/min --hashlimit-mode srcip --hashlimit-name RL-DNS-ANY-v4-LOG --hashlimit-srcmask 28 -m comment --comment "RATE-LIMIT DNS ANY QTYPE 20/min" -j LOG --log-prefix "DNS-FLOOD " 
 -A dns_limit -m hashlimit --hashlimit-above 10/sec --hashlimit-burst 100 --hashlimit-mode srcip --hashlimit-name DNS --hashlimit-srcmask 28 -m comment --comment "RATE-LIMIT DNS 10/src burst 100" -j DROP 
 -A dns_limit -j ACCEPT 

 # ICMP 
 -A INPUT -p icmp -j ACCEPT 
 -A INPUT -p icmp --icmp-type echo-request -m limit --limit 1/s -j ACCEPT 

 # NRPE 
 -A INPUT -s 185.233.100.230 -p tcp --dport 5666 -j ACCEPT 

 # Keepalived 
 -A INPUT -s 45.67.81.0/24 -p vrrp -j ACCEPT 

 COMMIT 
 ~~~ 

 ## Configurations spécifiques 

 

 ### Génération du certificat SSL sur Hades 

 ~~~ 
 certbot --apache --agree-tos --email sysop@aquilenet.fr -d dns.aquilenet.fr 
 chgrp bind /etc/letsencrypt/live/dns.aquilenet.fr/privkey.pem 
 chmod 0640 /etc/letsencrypt/live/dns.aquilenet.fr/privkey.pem 
 openssl dhparam -out /etc/bind/dhparam.pem 4096 
 ~~~ 

 Copie du certificat sur gaia 

 * /etc/letsencrypt/renewal/dns.aquilenet.fr.conf 

 ~~~ 
 + post_hook = /root/certificats.sh 
 ~~~ 

 * /root/certificats.sh 

 ~~~ 

 #!/bin/bash 
 #!/bin/sh 
 cp -RL /etc/letsencrypt/live/dns.aquilenet.fr/ /etc/unbound/ssl/ 
 systemctl restart unbound 
 scp -qrP 55555 /etc/unbound/ssl/dns.aquilenet.fr pan2.aquilenet.fr:/etc/unbound/ssl/ 
 ssh -p 55555 pan2.aquilenet.fr systemctl restart unbound rsync -aPHSA /etc/letsencrypt/archive/dns.aquilenet.fr gaia:/etc/letsencrypt/archive/ 
 ~~~ 

 ### Clée sdns (auth par épinglage de la clé / key pining) 

 * A partir de la clé 
 ~~~ 
 hades|19:21:08|:~# openssl rsa -in /etc/letsencrypt/live/dns.aquilenet.fr/privkey.pem -outform der -pubout | openssl dgst -sha256 -binary | openssl enc -base64 
 writing RSA key 
 lBk09CRIPJ+J4O9rmbvJkEiGYoH5r9rxOIQdxkYxyII= 
 ~~~ 
 * ou à partir du certificat 
 ~~~ 
 hades|19:22:21|:~# openssl x509 -in /etc/letsencrypt/live/dns.aquilenet.fr/cert.pem -pubkey -noout | openssl rsa -pubin -outform der | openssl dgst -sha256 -binary | openssl enc -base64 
 writing RSA key 
 lBk09CRIPJ+J4O9rmbvJkEiGYoH5r9rxOIQdxkYxyII= 
 ~~~ 

 ### Keepalived 

 #### Hades Master 

 * /etc/keepalived/keepalived.conf 

 ~~~ 
 global_defs { 
   notification_email { 
     sysop@aquilenet.fr 
   } 
   notification_email_from vrrp-dns@aquilenet.fr.fr 
   smtp_server localhost 
   smtp_connect_timeout 30 
 } 

 vrrp_instance DNS { 
   state MASTER  
   interface eth0 
   virtual_router_id 101  
   priority 100 
   advert_int 2 
   smtp_alert 
   unicast_src_ip 185.233.100.16  
   unicast_peer { 
     185.233.100.2 
   } 
   virtual_ipaddress { 
     45.67.81.23/32 dev eth1 scope global # VIP 
   } 
 } 
 ~~~ 

 #### Gaia Slave 

 * /etc/keepalived/keepalived.conf 

 ~~~ 
 global_defs { 
   notification_email { 
     sysop@aquilenet.fr 
   } 
   notification_email_from vrrp-dns@aquilenet.fr.fr 
   smtp_server localhost 
   smtp_connect_timeout 30 
 } 

 vrrp_instance DNS { 
   state BACKUP 
   interface eth0 
   virtual_router_id 101 
   priority 50 
   advert_int 2 
   smtp_alert 
   unicast_src_ip 185.233.100.2 
   unicast_peer { 
     185.233.100.16 
   } 
   virtual_ipaddress { 
     45.67.81.23/32 dev eth1 scope global # VIP 
   } 
 } 

 ~~~ 

 ## Validation 

 * test du service 
 ~~~ 
 dig +short dns.aquilenet.fr @45.67.81.23  
 45.67.81.23 
 dig +https +short dns.aquilenet.fr @45.67.81.23       
 45.67.81.23 
 dig +tls +short dns.aquilenet.fr @45.67.81.23       
 45.67.81.23 
 ~~~ 

 * bascule keepalived 

 sur hades: `systemctl stop keepalived` 
 sur gaia: `ip a |grep /32`doit donner "inet 45.67.81.23/32 scope global eth1" 
 http://dns.aquilenet.fr/host permet de savoir quel est le serveur qui porte l'IP virtuelle 

 ## Indexes de serveurs DNS ouverts: 

 https://github.com/curl/curl/wiki/DNS-over-HTTPS#publicly-available-servers <= fait 
 https://diyisp.org/dokuwiki/doku.php?id=technical:dnsresolver <= fait 
 https://diyisp.org/dokuwiki/doku.php?id=technical:dnsresolver <= fait 
 https://dnsprivacy.org/public_resolvers/ 

 ## Ressources 

 https://www.bortzmeyer.org/doh-bortzmeyer-fr-policy.html 
 https://www.bortzmeyer.org/7858.html 
 https://kb.isc.org/docs/aa-01386