Во-первых, я знаю, что это старый вопрос, но ...
Я десятилетиями управлял своим собственным авторитетным, не рекурсивным DNS-сервером, но никогда не был жертвой каких-либо DNS-атак DDoS - до тех пор, пока я не переключился на нового интернет-провайдера. Тысячи поддельных DNS-запросов наводнили мои журналы, и я был действительно раздражен - не столько о влиянии на мой сервер, сколько о том, что он загромождает мои журналы и неприятном ощущении злоупотребления. Похоже, что злоумышленник пытается использовать мой DNS в « Атаке авторитетного сервера имен ».
Поэтому я решил, что, хотя я ограничиваю рекурсивные запросы своей внутренней сетью (отказывая во всех остальных), я скорее трачу свои циклы ЦП на сопоставление строк в iptables, чем на отправку отрицательных ответов на поддельные IP-адреса (меньше беспорядка в моих журналах, меньше сетевой трафик и мой более высокий уровень удовлетворенности).
Я начал с того, что поступил так же , как и все остальные , выяснил, какие доменные имена запрашиваются, и создал совпадение строк в этом домене с целевым DROP. Но вскоре я понял, что получу огромное количество правил, каждое из которых потребляет циклы процессора. Так что делать? Поскольку я не запускаю рекурсивный сервер имен, я подумал, что мог бы сопоставить действительные зоны, для которых я уполномочен, и отбросить все остальное.
Моя политика по умолчанию в iptables - ПРИНЯТЬ, если ваша политика DROP, вам, вероятно, нужно внести некоторые изменения, если вы хотите использовать следующее решение.
Я храню конфигурацию своей зоны в отдельном файле (/etc/bind/named.conf.local), давайте использовать это в качестве примера:
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;
};
Обратите внимание на комментарий «// Private» в моих первых двух зонах, я использую его в следующем сценарии, чтобы исключить их из списка допустимых зон.
#!/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";
Запустите приведенный выше скрипт с файлом конфигурации зоны в качестве аргумента.
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
Сохраните вывод в сценарии, передайте его в оболочку или скопируйте и вставьте в свой терминал, чтобы создать новую цепочку и начать отфильтровывать все недействительные DNS-запросы.
запустить / SBIN / Iptables -L DNSvalidate -nvx
видеть пакетов (и байтов) счетчики по каждому правилу в новой цепи (вы можете захотеть , чтобы переместить зону с большинством пакетов в верхней части списка , чтобы сделать его более эффективным).
В надежде, что кто-то найдет это полезным :)