Primero, sé que esta es una vieja pregunta pero ...
He estado ejecutando mi propio servidor DNS autorizado, no recursivo, durante décadas, pero nunca he sido víctima de ningún ataque DDoS basado en DNS, hasta ahora, cuando me cambié a un nuevo ISP. Miles de consultas falsas de DNS inundaron mis registros y me molesté mucho, no tanto por el impacto en mi servidor, sino por el hecho de que saturaba mis registros y la incómoda sensación de abuso. Parece que el atacante intenta usar mi DNS en un " ataque autorizado del servidor de nombres ".
Entonces pensé que, aunque limito las consultas recursivas a mi red interna (negando todas las demás), prefiero pasar mis ciclos de CPU en la coincidencia de cadenas en iptables que enviar respuestas negativas a las direcciones IP falsificadas (menos desorden en mis registros, menos tráfico de red y un mayor nivel de satisfacción propio).
Comencé haciendo lo que todos los demás parecen hacer , averiguar qué nombres de dominio se consultan y crear una coincidencia de cadena en ese dominio con un DROP de destino. Pero pronto me di cuenta de que terminaría con una gran cantidad de reglas, cada una de las cuales consumía ciclos de CPU. ¿Entonces lo que hay que hacer? Como no ejecuto un servidor de nombres recursivo, pensé que podría hacer la coincidencia en las zonas reales para las que tengo autoridad y descartar todo lo demás.
Mi política predeterminada en iptables es ACEPTAR, si su política es DROP, probablemente necesite hacer algunos ajustes si desea utilizar la siguiente solución.
Mantengo mi configuración de zona en un archivo separado (/etc/bind/named.conf.local), usemos esto como ejemplo:
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;
};
Tenga en cuenta el comentario "// Privado" en mis dos primeras zonas, hago uso de esto en el siguiente script para excluirlos de la lista de zonas válidas.
#!/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";
Ejecute el script anterior con el archivo de configuración de zona como argumento.
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
Guarde el resultado en un script, canalícelo a un shell o cópielo y péguelo en su terminal para crear la nueva cadena y comenzar a filtrar todas las consultas DNS no válidas.
ejecute / sbin / iptables -L DNSvalidate -nvx
para ver los contadores de paquetes (y bytes) en cada regla de la nueva cadena (es posible que desee mover la zona con la mayoría de los paquetes al principio de la lista para que sea más eficiente).
Con la esperanza de que alguien pueda encontrar esto útil :)