Projet

Général

Profil

Wiki » Historique » Version 34

jonathan.certes, 29/04/2024 18:28
Modif du script de post_hook pour copier le certificat renouvelé sur pan2.

1 5 sacha
{{>toc}}
2
3 9 sacha
# DNS récursif Ouvert DoH DoT & haute disponibilité
4 15 sacha
**45.67.81.23 - dns.aquilenet.fr**
5 14 sacha
6 1 sacha
7 16 sacha
8 19 sacha
On propos un DNS récursif ouvert cf https://www.aquilenet.fr/services/dns/ https://dns.aquilenet.fr/
9 18 sacha
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.
10
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.
11 1 sacha
12 19 sacha
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.
13 6 sacha
14 18 sacha
## Configurations communes à pan1 et pan2
15 1 sacha
16 18 sacha
### Nginx
17 1 sacha
18 19 sacha
* /etc/nginx/sites-enabled/default
19 1 sacha
20
21
~~~
22
server {
23 19 sacha
    listen       45.67.81.23:80 ;
24 27 sacha
    listen       [2a0c:e300::1337]:80 ;
25 1 sacha
26 19 sacha
    location ~ /.well-known/acme-challenge {
27
        allow all;
28
        root /var/www/letsencrypt;
29
    }
30
    location / {
31
        return 301 https://dns.aquilenet.fr;
32
        }
33 1 sacha
34 19 sacha
    server_name dns.aquilenet.fr; # managed by Certbot
35 1 sacha
36 19 sacha
    location ~ /.well-known/acme-challenge {
37
        allow all;
38
        root /var/www/letsencrypt;
39
    }
40
}
41
server {
42
    listen       45.67.81.23:443 ssl http2;
43 27 sacha
    listen       [2a0c:e300::1337]:443 ssl http2;
44 19 sacha
    root /var/www/dns.aquilenet.fr;
45
    index index.htm;
46 1 sacha
47 19 sacha
    ssl_certificate     /etc/unbound/ssl/dns.aquilenet.fr/fullchain.pem;
48
    ssl_certificate_key /etc/unbound/ssl/dns.aquilenet.fr/privkey.pem;
49
location /dns-query {
50
    grpc_pass grpc://127.0.0.1:443;
51
        }
52
}
53
54 1 sacha
~~~
55
56
### Unbound
57
58
~~~
59 19 sacha
include-toplevel: "/etc/unbound/unbound.conf.d/*.conf"
60
61
server:
62
    verbosity: 1 
63
64 28 sacha
    access-control: 0.0.0.0/0 allow
65
    access-control: ::/0 allow
66 19 sacha
    interface: 45.67.81.23
67 28 sacha
#    interface: 45.67.81.23@443 # on nginx
68 19 sacha
    interface: 45.67.81.23@853
69 28 sacha
    interface: 2a0c:e300::1337
70
    interface: 2a0c:e300::1337@853
71 19 sacha
72
http-notls-downstream: yes
73
    interface: 127.0.0.1@443
74 28 sacha
    interface: ::1@443
75 19 sacha
76
    port: 53
77
    tls-port: 853
78
    https-port: 443
79
    
80
    root-hints: "/usr/share/dns/root.hints"
81 28 sacha
82
#    ip-rate-limit: 10 
83 19 sacha
    harden-glue: yes
84
    harden-large-queries: yes
85
    harden-dnssec-stripped: yes
86
    harden-below-nxdomain: yes
87
    harden-referral-path: yes
88
    harden-algo-downgrade: no # false positives with improperly configured zones
89
    use-caps-for-id: no # makes lots of queries fail
90 1 sacha
    edns-buffer-size: 1232
91
    rrset-roundrobin: yes
92 19 sacha
    cache-min-ttl: 300
93
    cache-max-ttl: 86400
94
    serve-expired: yes
95
    harden-algo-downgrade: yes
96
    harden-short-bufsize: yes
97
    hide-identity: yes
98
    identity: "dns.aquilenet.fr"
99
    hide-version: yes
100
    do-daemonize: no
101
    neg-cache-size: 4m
102
    qname-minimisation: yes
103
    deny-any: yes
104
    minimal-responses: yes
105
    prefetch: yes
106
    prefetch-key: yes
107
    num-threads: 2 
108
    msg-cache-size: 50m
109
    rrset-cache-size: 100m
110
    so-reuseport: yes
111
    so-rcvbuf: 4m
112
    so-sndbuf: 4m
113
    unwanted-reply-threshold: 100000
114
    log-queries: no
115
    log-replies: no
116
    log-servfail: no
117
    log-local-actions: no
118
    logfile: /dev/null
119
120
    # imposer la QNAME minimisation (RFC 7816)
121
    qname-minimisation: yes
122
    # même si le serveur faisant autorité ne le veut pas
123
    #   après discussion, il est possible que cette option ne soit
124
    #   pas recommandée dans le cadre d'un résolveur ouvert
125
    qname-minimisation-strict: yes
126
127
   # DoH
128 28 sacha
   tls-service-key: "/etc/unbound/ssl/dns.aquilenet.fr/privkey.pem"
129
   tls-service-pem: "/etc/unbound/ssl/dns.aquilenet.fr/cert.pem"
130 19 sacha
131 1 sacha
~~~
132
133
### AppArmor
134
135
*  /etc/apparmor.d/usr.sbin.unbound
136
décomenter:
137
  include <local/usr.sbin.unbound>
138
139
* /etc/apparmor.d/local/usr.sbin.unbound
140
~~~
141
/etc/letsencrypt/archive/** r,
142
/etc/letsencrypt/live/** r,
143
~~~
144
appliquer avec: `apparmor_parser -r /etc/apparmor.d/usr.sbin.unbound`
145
146
### Iptables
147
148 29 sacha
* /etc/iptables/rules.v4
149
150 1 sacha
~~~
151 19 sacha
*filter
152
:INPUT DROP
153
:FORWARD DROP
154
:OUTPUT ACCEPT
155
156
-A INPUT ! -i lo -s 127.0.0.0/8 -j DROP
157
-A INPUT -s 0.0.0.0 -i eth0 -j DROP
158
-A INPUT -d 0.0.0.0 -i eth0 -j DROP
159
-A INPUT -s 255.255.255.255 -i eth0 -j DROP
160
-A INPUT -d 255.255.255.255 -i eth0 -j DROP
161
-A INPUT -m conntrack --ctstate INVALID -j DROP
162
163
-A INPUT -p tcp -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
164
-A INPUT -p udp ! --dport 53 -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
165
-A INPUT -i lo -j ACCEPT
166
-A OUTPUT -o lo -j ACCEPT
167
168
# SSH no more 10 attempts in 3 min
169
-A INPUT -i eth0 -p tcp --dport 55555 -m recent --name SSH --update --hitcount 10 --seconds 180 -j DROP
170
-A INPUT -i eth0 -p tcp --dport 55555 -m recent --name SSH --set -j ACCEPT
171
172 1 sacha
# Web
173
-A INPUT -p tcp --dport 80 -j ACCEPT
174 19 sacha
175
# smtp
176
-A INPUT -i eth0 -p tcp --dport 25 -j ACCEPT
177
# smtps
178
-A INPUT -i eth0 -p tcp --dport 465 -j ACCEPT
179
# submission
180
-A INPUT -i eth0 -p tcp --dport 587 -j ACCEPT
181
182
# DNS
183
# récursif, lisible par nos abonnés seulement
184
-N dns_limit
185
-A dns_limit -s 185.233.100.0/22 -j ACCEPT
186 7 sacha
-A INPUT -d 45.67.81.23 -p tcp -m tcp --dport 53 -j dns_limit
187
-A INPUT -d 45.67.81.23 -p udp -m udp --dport 53 -j dns_limit
188
-A INPUT -d 45.67.81.23 -p tcp -m tcp --dport 443 -j dns_limit
189
-A INPUT -d 45.67.81.23 -p tcp -m tcp --dport 853 -j dns_limit
190 19 sacha
191
# mais aussi par le reste d'Internet, mais en rate-limit
192
-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
193
-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 "
194
-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
195
-A dns_limit -j ACCEPT
196
197
# ICMP
198
-A INPUT -p icmp -j ACCEPT
199
-A INPUT -p icmp --icmp-type echo-request -m limit --limit 1/s -j ACCEPT
200
201
# NRPE
202
-A INPUT -s 185.233.100.230 -p tcp --dport 5666 -j ACCEPT
203
204
# Keepalived
205
-A INPUT -s 45.67.81.0/24 -p vrrp -j ACCEPT
206 29 sacha
207
COMMIT
208
~~~
209
210
* /etc/iptables/rules.v6
211
212
~~~
213
*filter
214
:INPUT DROP 
215
:FORWARD DROP 
216
:OUTPUT ACCEPT
217
218
-A INPUT -m conntrack --ctstate INVALID -j DROP
219
220
-A INPUT -p tcp -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
221
-A INPUT -p udp ! --dport 53 -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
222
-A INPUT -i lo -j ACCEPT
223
-A OUTPUT -o lo -j ACCEPT
224
225
# SSH no more 10 attempts in 3 min
226
-A INPUT -i enX1 -p tcp --dport 55555 -m recent --name SSH --update --hitcount 10 --seconds 180 -j DROP
227
-A INPUT -i enX1 -p tcp --dport 55555 -m recent --name SSH --set -j ACCEPT
228
229
# Web
230
-A INPUT -i enX1 -p tcp --dport 80 -j ACCEPT
231
# DNS
232
# Autorité, lisible par tout le monde
233
-A INPUT -d 2a0c:e300::2 -p tcp -m tcp --dport 53 -j ACCEPT
234
-A INPUT -d 2a0c:e300::2 -p udp -m udp --dport 53 -j ACCEPT
235
236
# récursif, lisible par nos abonnés seulement
237
-N dns_limit
238
-A INPUT -d 2a0c:e300::1337 -p tcp -m tcp --dport 53 -j dns_limit
239
-A INPUT -d 2a0c:e300::1337 -p udp -m udp --dport 53 -j dns_limit
240
-A INPUT -d 2a0c:e300::1337 -p tcp -m tcp --dport 443 -j dns_limit
241
-A INPUT -d 2a0c:e300::1337 -p tcp -m tcp --dport 853 -j dns_limit
242
243
-A dns_limit -s 2a0c:e300::/29 -j ACCEPT
244
245
# mais aussi par le reste d'Internet, mais en rate-limit
246
-A dns_limit -m string --algo bm --hex-string "|00 00 ff 00 01|" --from 48 -m hashlimit --hashlimit-above 1/sec --hashlimit-burst 2 --hashlimit-mode srcip --hashlimit-name RL-DNS-ANY-v6 --hashlimit-srcmask 56 -m comment --comment "RATE-LIMIT DNS ANY QTYPE 1/s burst 2" -j DROP
247
-A dns_limit -m string --algo bm --hex-string "|00 00 ff 00 01|" --from 48 -m hashlimit --hashlimit-above 20/min --hashlimit-mode srcip --hashlimit-name RL-DNS-ANY-v6-LOG --hashlimit-srcmask 56 -m comment --comment "RATE-LIMIT DNS ANY QTYPE 20/min LOG" -j LOG --log-prefix "DNS-FLOOD "
248
-A dns_limit -m hashlimit --hashlimit-above 10/sec --hashlimit-burst 100 --hashlimit-mode srcip --hashlimit-name DNS --hashlimit-srcmask 56 -m comment --comment "RATE-LIMIT DNS 10/s burst 100" -j DROP
249
-A dns_limit -j ACCEPT
250
251
# ICMP
252
-A INPUT -p icmpv6 -j ACCEPT
253
-A INPUT -p icmpv6 --icmpv6-type echo-request -m limit --limit 1/s -j ACCEPT
254 19 sacha
255
COMMIT
256 20 sacha
~~~
257 3 sacha
258
## Configurations spécifiques
259
260 31 sacha
### Génération du certificat SSL sur pan1
261 3 sacha
262
~~~
263
certbot --apache --agree-tos --email sysop@aquilenet.fr -d dns.aquilenet.fr
264
chgrp bind /etc/letsencrypt/live/dns.aquilenet.fr/privkey.pem
265
chmod 0640 /etc/letsencrypt/live/dns.aquilenet.fr/privkey.pem
266 16 sacha
openssl dhparam -out /etc/bind/dhparam.pem 4096
267
~~~
268
269
Copie du certificat sur gaia
270
271 21 sacha
* /etc/letsencrypt/renewal/dns.aquilenet.fr.conf
272 1 sacha
273 21 sacha
~~~
274
+ post_hook = /root/certificats.sh
275
~~~
276 20 sacha
277 22 sacha
* /root/certificats.sh
278
279
~~~
280
#!/bin/sh
281
cp -RL /etc/letsencrypt/live/dns.aquilenet.fr/ /etc/unbound/ssl/
282
systemctl restart unbound
283 34 jonathan.certes
systemctl restart nginx
284
if [ $? -eq 0 ]; then
285
  # make sure that the configuration is identical on pan2:
286
  scp -qrP 55555 /etc/nginx/sites-available/default pan2.aquilenet.fr:"/etc/nginx/sites-available/"
287
288
  # copy the new certificate to pan2:
289
  ssh -p 55555 pan2.aquilenet.fr "mkdir -p /etc/letsencrypt/live/dns.aquilenet.fr/"
290
  scp -qrP 55555 /etc/letsencrypt/live/dns.aquilenet.fr/fullchain.pem /etc/letsencrypt/live/dns.aquilenet.fr/privkey.pem pan2.aquilenet.fr:"/etc/letsencrypt/live/dns.aquilenet.fr/"
291
  scp -qrP 55555 /etc/letsencrypt/options-ssl-nginx.conf /etc/letsencrypt/ssl-dhparams.pem pan2.aquilenet.fr:"/etc/letsencrypt/"
292
293
  scp -qrP 55555 /etc/unbound/ssl/dns.aquilenet.fr pan2.aquilenet.fr:/etc/unbound/ssl/
294
  ssh -p 55555 pan2.aquilenet.fr "systemctl restart unbound && systemctl restart nginx"
295
fi
296 16 sacha
~~~
297
298
### Clée sdns (auth par épinglage de la clé / key pining)
299
300
* A partir de la clé
301 23 sacha
302 16 sacha
~~~
303 31 sacha
pan1|19:21:08|:~# openssl rsa -in /etc/letsencrypt/live/dns.aquilenet.fr/privkey.pem -outform der -pubout | openssl dgst -sha256 -binary | openssl enc -base64
304 3 sacha
writing RSA key
305
lBk09CRIPJ+J4O9rmbvJkEiGYoH5r9rxOIQdxkYxyII=
306
~~~
307 23 sacha
308 3 sacha
* ou à partir du certificat
309 23 sacha
310 3 sacha
~~~
311 31 sacha
pan1|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
312 3 sacha
writing RSA key
313
lBk09CRIPJ+J4O9rmbvJkEiGYoH5r9rxOIQdxkYxyII=
314
~~~
315
316
### Keepalived
317
318 26 sacha
#### pan1 Master
319 3 sacha
320
* /etc/keepalived/keepalived.conf
321
322
~~~
323
global_defs {
324
  notification_email {
325
    sysop@aquilenet.fr
326
  }
327 24 sacha
  notification_email_from vrrp-dns@aquilenet.fr
328
  smtp_server localhost 
329 3 sacha
  smtp_connect_timeout 30
330
}
331
332
vrrp_instance DNS {
333
  state MASTER 
334 24 sacha
  interface enX0
335
  virtual_router_id 101
336
  priority 100 
337
  advert_int 5 
338 1 sacha
  smtp_alert
339 24 sacha
  unicast_src_ip 45.67.81.21 
340 1 sacha
  unicast_peer {
341 24 sacha
    45.67.81.22
342 1 sacha
  }
343 3 sacha
  virtual_ipaddress {
344 24 sacha
    45.67.81.23/32 dev enX0 scope global # VIP
345 3 sacha
  }
346 24 sacha
  virtual_ipaddress_excluded {
347
    2a0c:e300::1337/128 dev enX1
348
    }
349
350 3 sacha
}
351 24 sacha
352 3 sacha
~~~
353
354 25 sacha
#### pan2 Slave
355 3 sacha
356
* /etc/keepalived/keepalived.conf
357
358
~~~
359
global_defs {
360
  notification_email {
361
    sysop@aquilenet.fr
362
  }
363 25 sacha
  notification_email_from vrrp-dns@aquilenet.fr
364 3 sacha
  smtp_server localhost
365
  smtp_connect_timeout 30
366
}
367
368
vrrp_instance DNS {
369 25 sacha
  state SLAVE 
370
  interface enX0
371
  virtual_router_id 101 
372
  priority 50 
373
  advert_int 5 
374 1 sacha
  smtp_alert
375 25 sacha
  unicast_src_ip 45.67.81.22
376 1 sacha
  unicast_peer {
377 25 sacha
    45.67.81.21
378 4 sacha
  }
379
  virtual_ipaddress {
380 25 sacha
    45.67.81.23/32 dev enX0 scope global # VIP
381 4 sacha
  }
382 25 sacha
  virtual_ipaddress_excluded {
383
    2a0c:e300::1337/128 dev enX1
384
    }
385 13 sacha
}
386
387 8 sacha
~~~
388 4 sacha
389
## Validation
390
391 8 sacha
* test du service
392
~~~
393 1 sacha
dig +short dns.aquilenet.fr @45.67.81.23 
394 12 sacha
45.67.81.23
395 9 sacha
dig +https +short dns.aquilenet.fr @45.67.81.23      
396
45.67.81.23
397
dig +tls +short dns.aquilenet.fr @45.67.81.23      
398
45.67.81.23
399 10 sacha
~~~
400
401 9 sacha
* bascule keepalived
402 16 sacha
403 31 sacha
sur pan1: `systemctl stop keepalived`
404
sur pan2: `ip a |grep /32`doit donner "inet 45.67.81.23/32 scope global eth1"
405
https://dns.aquilenet.fr/hostname.txt permet de savoir quel est le serveur qui porte l'IP virtuelle
406 16 sacha
407
## Indexes de serveurs DNS ouverts:
408 1 sacha
409
https://github.com/curl/curl/wiki/DNS-over-HTTPS#publicly-available-servers <= fait
410
https://diyisp.org/dokuwiki/doku.php?id=technical:dnsresolver <= fait
411 33 sacha
https://european-alternatives.eu/category/public-dns
412
413 1 sacha
https://dnsprivacy.org/public_resolvers/
414
415
## Ressources
416
417
https://www.bortzmeyer.org/doh-bortzmeyer-fr-policy.html
418
https://www.bortzmeyer.org/7858.html
419
https://kb.isc.org/docs/aa-01386
420 30 sacha
https://dnscrypt.info/stamps-specifications/