По-перше, я знаю, що це старе питання, але ...
Я працював на власному авторитетному, не рекурсивному сервері DNS протягом десятиліть, але ніколи не став жертвою жодної DDoS-атаки на основі DNS - дотепер, коли я перейшов на новий провайдер. Тисячі підроблених запитів DNS заполонили мої журнали, і я справді роздратувався - не стільки щодо впливу на мій сервер, скоріше того, що воно захарастило мої журнали та незручне відчуття зловживання. Здається, що зловмисник намагається використовувати мій DNS в атаці " Авторитетний сервер імен ".
Тому я зрозумів, що, хоча я обмежую рекурсивні запити у своїй внутрішній мережі (заперечуючи всі інші), я швидше витрачаю свої цикли процесора на відповідність рядків у iptables, ніж на надсилання негативних відповідей на підроблені IP-адреси (менше захаращування в моїх журналах, менше мережевий трафік та більш високий рівень власної задоволеності).
Я почав, роблячи так, як це роблять всі інші , з'ясовуючи, які доменні імена запитуються, і створив відповідність рядка для цього домену з цільовим DROP. Але незабаром я зрозумів, що закінчу величезну кількість правил, кожен з яких вимагає циклу процесора. Отже, що робити? Оскільки я не запускаю рекурсивний сервер імен, я зрозумів, що я можу виконати відповідність у фактичних зонах, для яких я авторитетний, і відкинути все інше.
Моя політика за замовчуванням в iptables - це ACCEPT, якщо ваша політика 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;
};
Зверніть увагу на "// Приватний" коментар до моїх перших двох зон, я використовую це в наступному сценарії, щоб виключити їх зі списку дійсних зон.
#!/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,
щоб побачити лічильники пакетів (і байтів) кожного правила в новому ланцюжку (ви можете перенести зону з більшістю пакетів у верхню частину списку, щоб зробити її більш ефективною).
У надії, що хтось може виявити це корисним :)