Primeiro, sei que essa é uma pergunta antiga, mas ...
Eu tenho executado meu próprio servidor DNS autoritativo, não recursivo por décadas, mas nunca fui uma vítima em nenhum ataque DDoS baseado em DNS - até agora, quando mudei para um novo ISP.
Milhares de consultas DNS falsas inundaram meus registros e eu fiquei muito aborrecido - não tanto com o impacto no meu servidor, mas com o fato de que ele bagunçou meus logs e a desconfortável sensação de estar sendo abusada.
Parece que o invasor tenta usar meu DNS em um “ ataque Authoritative Name Server ".
Então imaginei que, embora eu limite as consultas recursivas à minha rede interna (negando todas as outras), prefiro gastar meus ciclos de CPU em correspondência de cadeia de caracteres em iptables do que enviar respostas negativas aos endereços IP falsificados (menos confusão na minha logs, menos tráfego de rede e um nível de satisfação maior do que eu).
Comecei fazendo todo mundo parecer , descubra quais nomes de domínio são consultados e criei uma correspondência de seqüência de caracteres domínio com um DROP alvo. Mas logo percebi que acabaria com uma enorme quantidade de regras, cada uma delas consumindo ciclos de CPU. Então o que fazer? Como não executo um servidor de nomes recursivo, imaginei que poderia fazer a correspondência nas zonas reais para as quais tenho autoridade e largar todo o resto.
Minha política padrão no iptables é ACCEPT, se sua política for DROP, você provavelmente precisará fazer alguns ajustes se quiser usar a seguinte solução.
Eu mantenho minha configuração de zona em um arquivo separado (/etc/bind/named.conf.local), vamos usar isso como um exemplo:
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;
};
Observe o comentário "// Particular" nas minhas duas primeiras zonas, faço uso disso no script a seguir para excluí-las da 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";
Execute o script acima com o arquivo de configuração da 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
Salve a saída em um script, canalize-a para um shell ou copie-a e cole-a em seu terminal para criar a nova cadeia e inicie o filtro de todas as consultas DNS inválidas.
executar / sbin / iptables -L DNSvalidate -nvx
para ver os contadores de pacotes (e bytes) em cada regra na nova cadeia (você pode querer mover a zona com a maioria dos pacotes para o topo da lista para torná-la mais eficiente).
Na esperança de que alguém possa achar isso útil:)