Innanzitutto, so che questa è una vecchia domanda ma ...
Ho gestito il mio server DNS autorevole, non ricorsivo per decenni, ma non sono mai stato vittima di alcun attacco DDoS basato su DNS - fino ad ora, quando sono passato a un nuovo ISP. Migliaia di query DNS contraffatte hanno inondato i miei registri e mi sono davvero infastidito, non tanto per l'impatto sul mio server, piuttosto per il fatto che ingombra i miei registri e la scomoda sensazione di essere abusato. Sembra che l'attaccante cerchi di usare il mio DNS in un " attacco autorevole di Name Server ".
Quindi ho pensato che, anche se ho limitato le query ricorsive alla mia rete interna (negandone tutte le altre), preferisco spendere i miei cicli della CPU sulla corrispondenza delle stringhe in iptables piuttosto che inviare risposte negative agli indirizzi IP falsificati (meno disordine nei miei registri, meno traffico di rete e un livello di soddisfazione più alto del mio).
Ho iniziato facendo come tutti gli altri sembrano fare , scopri quali nomi di dominio vengono interrogati e ho creato una corrispondenza di stringa su quel dominio con un DROP di destinazione. Ma presto mi sono reso conto che avrei finito con un'enorme quantità di regole, ognuna delle quali consumava cicli di CPU. Quindi che si fa? Dal momento che non eseguo un server dei nomi ricorsivo, ho pensato che avrei potuto fare la corrispondenza sulle zone effettive per cui sono autorevole e rilasciare tutto il resto.
La mia politica di default in iptables è ACCETTA, se la tua politica è DROP probabilmente dovrai apportare alcune modifiche se desideri utilizzare la seguente soluzione.
Mantengo la configurazione della mia zona in un file separato (/etc/bind/named.conf.local), usiamo questo come esempio:
zone "1.168.192.in-addr.arpa" { // Private
type master;
allow-query { 192.168.1.0/24; 127.0.0.1; };
allow-transfer { 127.0.0.1; };
file "/etc/bind/db.192.168.1";
};
zone "home.example.net" { // Private
type master;
allow-query { 192.168.1.0/24; 127.0.0.1; };
allow-transfer { 127.0.0.1; };
file "/etc/bind/pri/db.home.example.net";
};
zone "example.net" {
type master;
file "/etc/bind/pri/db.example.net";
allow-transfer { 127.0.0.1; 8.8.8.8; };
};
zone "example.com" {
type slave;
masters { 8.8.8.8; };
file "sec.example.com";
allow-transfer { 127.0.0.1; };
notify no;
};
zone "subdomain.of.example.nu" {
type slave;
masters { 8.8.8.8; };
file "sec.subdomain.of.example.nu";
allow-transfer { 127.0.0.1; };
notify no;
};
Nota il commento "// privato" sulle mie prime due zone, ne faccio uso nel seguente script per escluderle dall'elenco delle zone valide.
#!/usr/bin/perl
# zone2iptables - Richard Lithvall, april 2014
#
# Since we want to match not only example.net, but also (for example)
# www.example.net we need to set a reasonable maximum value for a domain
# name in our zones - 100 character should be more that enough for most people
# and 255 is the absolute maximum allowed in rfc1034.
# Set it to 0 (zero) if you would like the script to fetch each zone (axfr)
# to get the actual max value.
$maxLengthOfQueryName=255;
$externalInterface="eth1";
print "# first time you run this, you will get error on the 3 first commands.\n";
print "# It's here to make it safe/possible to periodically run this script.\n";
print "/sbin/iptables -D INPUT -i $externalInterface -p udp --dport 53 -j DNSvalidate\n";
print "/sbin/iptables -F DNSvalidate\n";
print "/sbin/iptables -X DNSvalidate\n";
print "#\n";
print "# now, create the chain (again)\n";
print "/sbin/iptables -N DNSvalidate\n";
print "# and populate it with your zones\n";
while(<>){
if(/^zone\s+"(.+)"\s+\{$/){
$zone=$1;
if($maxLengthOfQueryName){
$max=$maxLengthOfQueryName;
} else {
open(DIG,"dig -t axfr +nocmd +nostats $zone |");
$max=0;
while(<DIG>){
if(/^(.+?)\.\s/){
$max=(length($1)>$max)?length($1):$max;
}
}
close(DIG);
}
printf("iptables -A DNSvalidate -m string --from 40 --to %d --hex-string \"",($max+42));
foreach $subdomain (split('\.',$zone)){
printf("|%02X|%s",length($subdomain),$subdomain);
}
print("|00|\" --algo bm -j RETURN -m comment --comment \"$zone\"\n");
}
}
print "# and end the new chain with a drop\n";
print "/sbin/iptables -A DNSvalidate -j DROP\n";
print "# And, at last, make the new chain active (on UDP/53)\n";
print "/sbin/iptables -A INPUT -i $externalInterface -p udp --dport 53 -j DNSvalidate\n";
Eseguire lo script sopra con il file di configurazione della zona come argomento.
root:~/tmp/# ./zone2iptables.pl /etc/bind/named.conf.local
# first time you run this, you will get error on the 3 first commands.
# It's here to make it safe/possible to periodically run this script.
/sbin/iptables -D INPUT -i eth1 -p udp --dport 53 -j DNSvalidate
/sbin/iptables -F DNSvalidate
/sbin/iptables -X DNSvalidate
#
# now, create the chain (again)
/sbin/iptables -N DNSvalidate
# and populate it with your zones
iptables -A DNSvalidate -m string --from 40 --to 297 --hex-string "|07|example|03|net|00|" --algo bm -j RETURN -m comment --comment "example.net"
iptables -A DNSvalidate -m string --from 40 --to 297 --hex-string "|07|example|03|com|00|" --algo bm -j RETURN -m comment --comment "example.com"
iptables -A DNSvalidate -m string --from 40 --to 297 --hex-string "|09|subdomain|02|of|07|example|02|nu|00|" --algo bm -j RETURN -m comment --comment "subdomain.of.example.nu"
# and end the new chain with a drop
/sbin/iptables -A DNSvalidate -j DROP
# And, at last, make the new chain active (on UDP/53)
/sbin/iptables -A INPUT -i eth1 -p udp --dport 53 -j DNSvalidate
Salvare l'output in uno script, inserirlo in una shell o copiarlo e incollarlo nel terminale per creare la nuova catena e iniziare a filtrare tutte le query DNS non valide.
esegui / sbin / iptables -L DNSvalidate -nvx
per vedere i contatori di pacchetti (e byte) su ogni regola nella nuova catena (potresti voler spostare la zona con la maggior parte dei pacchetti in cima all'elenco per renderla più efficiente).
Nella speranza che qualcuno possa trovare questo utile :)