====== PowerDNS DNSDist ======
Розгортаємо високопродуктивний безкоштовний DNS-сервер в складі авторитарного сервера з доменами користувача, окремим рекурсивним сервером і балансувальником dnsdist
All we do from sudo user!!!
[[https://repo.powerdns.com/]]
===== Install DNSdist =====
Встановлення та налаштування балансувальника dnsdist
Dnsdist - це високопродуктивний DNS-, DoS- та abuse балансувальник.
Основне його завдання полягає у маршрутизації трафіку на найкращий сервер,
що забезпечує максимальну продуктивність для дозволених користувачів,
у той час як відбувається шунтування або блокування шкідливого трафіку.
Має величезну кількість корисних функцій:
* Фільтрувати трафік (з ядра)
* Перевіряти прямий трафік з консолі
* Затримувати та обмежувати швидкість поганих запитів
* Інтелектуальне балансування навантаження
* Обмеження QPS та ін.
В репозиторіях є зазвичай, застаріла версія, тому заглянемо на сайт [[https://repo.powerdns.com/]]
Там знаходимо стабільну версію програми і застосовуємо зміни в списку репозиторіїв
sh -c 'echo "deb [arch=amd64] http://repo.powerdns.com/debian bullseye-dnsdist-17 main" > /etc/apt/sources.list.d/pdns.list'
tee /etc/apt/preferences.d/dnsdist<
curl https://repo.powerdns.com/FD380FBB-pub.asc | sudo gpg --no-default-keyring --keyring gnupg-ring:/etc/apt/trusted.gpg.d/powerdns.gpg --import
chmod 644 /etc/apt/trusted.gpg.d/*
Встановимо пакет dnsdist.
apt-get update -y
apt-get install -y dnsdist dnstop
cp -r /etc/dnsdist/ /etc/dnsdist.orig/
Відкриваємо конфігураційний файл
nano /etc/dnsdist/dnsdist.conf
і наводимо до такого вигляду:
setLocal('127.0.0.1')
addLocal('ANOTHER_IP')
addLocal('ANOTHER_IPV6_IP')
setACL({'0.0.0.0/0'}) -- Allow all IPs access
newServer({address='127.0.0.1:5300', pool='auth'})
newServer({address='127.0.0.1:5301', pool='recursor'})
recursive_ips = newNMG()
recursive_ips:addMask('127.0.0.1/32')
recursive_ips:addMask('192.168.2.0/23')
addAction(NetmaskGroupRule(recursive_ips), PoolAction('recursor'))
addAction(AllRule(), PoolAction('auth'))
pip install dnsdist_console
python3 -c "from dnsdist_console import HashPassword as H;print(H().generate(\"mysupersecret\"))"
$scrypt$ln=10,p=1,r=8$rY9YB+QnTOkxKOBlNUUYaw==$4C4Hm5IFiOTluLkjGtPMl4FtYQIwJvSA/eb7uqAlFg4=
Якщо хочемо відкрити рекурсію для всіх, то прибираємо всі правила і додаємо recursive_ips:addMask('0.0.0.0/0').
УВАГА! У такому режимі є можливість DDoS-атаки!
Підправимо конфіг рекурсора:
nano /etc/powerdns/recursor.d/recursor.local.conf
local-address=127.0.0.1
local-port=5301
Підправимо конфіг auth:
nano /etc/powerdns/pdns.d/pdns.local.conf
local-address=127.0.0.1
local-port=5300
service pdns-recursor restart
service pdns restart
[[https://stat.ripe.net/widget/dns-check]]
Додаємо сервіс dnsdist в автозавантаження та перезапускаємо:
systemctl enable dnsdist
systemctl start dnsdist
===== DNSDIST BLACK LIST =====
[[https://github.com/enilfodne/dnsdist-adblock/blob/master/dagg/dagg.lua]]
Часто буває необхідність з різних причин блокувати небажані домени, ось мій варіант
mkdir -p /etc/dnsdist/conf.d/
Для активації читання конфігурацій з /etc/dnsdist/conf.d/ підправимо
nano /etc/dnsdist/dnsdist.conf
додавши після рядків з "newServer()"
includeDirectory("/etc/dnsdist/conf.d")
nano /etc/dnsdist/conf.d/dagg.conf
-- https://github.com/enilfodne/dnsdist-adblock/blob/master/dagg/dagg.lua
-- add "includeDirectory("/etc/dnsdist/conf.d")" to /etc/dnsdist/dnsdist.conf
-- put this file into "/etc/dnsdist/conf.d"
Dagg = {
-- config
config = {
blacklistip = "127.0.0.127",
actionlog = {
path = "/var/log/dnsdist_blocked.log",
},
blocklist = {
path = "/etc/dnsdist/black.list",
},
reload = {
target = "reload.blacklist.local.",
},
unload = {
target = "unload.blacklist.local.",
},
},
-- table storing the domains that need to be blocked
table = {
-- only used for wildcard domains
smn = newSuffixMatchNode(),
-- default - fast string comparison
str = {},
},
}
-- read all the domains in a set
function DaggLoadDomainsFromFile(file)
local f = io.open(file, "rb")
-- verify that the file exists and it is accessible
if f ~= nil then
for domain in f:lines() do
domain = domain:lower()
domain = domain.."."
if string.find(domain, "*.") then
local suffix = domain:gsub("*.", "")
Dagg.table.smn:add(suffix)
else
Dagg.table.str[domain] = true
end
end
f:close()
end
end
-- verbose, but clear
function DaggLoadBlocklist()
-- no reason, just for clarity
local file = Dagg.config.blocklist.path
-- not really necessary, but keep similarity to other versions
local f = io.open(file, "rb")
if f ~= nil then
-- file exists, close and proceed with sed
f:close()
-- it appears that even when using:
--
-- 'local var = str2 .. str2'
--
-- the variable is not being garbage-collected
-- and it ends up looking like a memory leak.
--
-- let me know if if there's a better way
-- os.execute("sed '/\\.$/ ! s/$/\\./' -i " .. file)
else
errlog "[Dagg] the blocklist file is missing or inaccessible!"
end
DaggLoadDomainsFromFile(file)
end
-- clear the table from memory
function DaggClearTable()
Dagg.table = {
smn = newSuffixMatchNode(),
str = {},
}
end
-- write down a query to the action log
function DaggWriteToActionLog(dq)
-- write-down the query
local f = io.open(Dagg.config.actionlog.path, "a")
if f ~= nil then
local query_name = dq.qname:toString()
local remote_addr = dq.remoteaddr:toString()
local msg = string.format("[%s][%s] %s", os.date("!%Y-%m-%dT%TZ", t), remote_addr, query_name)
f:write(msg, "\n")
f:close()
end
end
-- main query action
function DaggIsDomainBlocked(dq)
local qname = dq.qname:toString():lower()
if (Dagg.table.smn:check(dq.qname) or Dagg.table.str[qname] ) then
errlog ("[Dagg] "..qname.." in blacklist and redirected to ".. Dagg.config.blacklistip)
-- set QueryResponse, so the query never goes upstream
dq.dh:setQR(true)
-- set a CustomTag
-- you can optionally set a tag and process
-- this request with other actions/pools
-- dq:setTag("Dagg", "true")
-- WARNING: it (may?) affect(s) performance
-- DaggWriteToActionLog(dq)
-- return NXDOMAIN - its fast, but apparently
-- some apps resort to hard-coded entries if NX
-- try spoofing in this instance.
-- return DNSAction.Nxdomain, ""
-- return Spoof - you can spoof the response
-- instead of NX, but this may lead to time-outs
return DNSAction.Spoof, Dagg.config.blacklistip
end
return DNSAction.None, ""
end
addAction(AllRule(), LuaAction(DaggIsDomainBlocked))
-- reload action
function DaggReloadBlocklist(dq)
infolog "[Dagg] re/loading blocklist..."
-- prevent the query from going upstream
dq.dh:setQR(true)
-- clear
DaggClearTable()
-- clean-up
collectgarbage "collect"
-- load
DaggLoadBlocklist()
-- clean-up
collectgarbage "collect"
-- respond with a local address just in case
return DNSAction.Spoof, "127.0.0.127"
end
addAction(Dagg.config.reload.target, LuaAction(DaggReloadBlocklist))
-- unload action
function DaggUnloadBlocklist(dq)
infolog "[Dagg] unloading blocklist..."
-- prevent the query from going upstream
dq.dh:setQR(true)
-- clear
DaggClearTable()
-- clean-up
collectgarbage "collect"
-- respond with a local address just in case
return DNSAction.Spoof, "127.0.0.127"
end
addAction(Dagg.config.unload.target, LuaAction(DaggUnloadBlocklist))
nano /etc/dnsdist/black.list
*.lohotron.shop
porn.xxx
xn--80ayhh.xn--c1avg
[[https://www.punycoder.com/]]
mkdir -p /etc/systemd/system/dnsdist.service.d/
tee /etc/systemd/system/dnsdist.service.d/override.conf<
systemctl daemon-reload
systemctl enable dnsdist
systemctl restart dnsdist
journalctl -xe
nano /etc/dnsdist/get_black_list.pl
#!/usr/bin/perl
# -------------------------- Dagg --------------------------
# https://t.me/MrMethod
# ----------------------------------------------------------
# ver. 2022.10.06 rev. 654
# ----------------------------------------------------------
use strict;
use warnings;
use JSON;
use Net::DNS;
#use Data::Dumper;
use LWP::UserAgent;
my $blacklist_url = 'http://example.com/blackdns.json';
my $blacklist_file = '/etc/dnsdist/black.list';
my $reload_host = 'reload.blacklist.local';
my %old_dns = ();
if (open(my $fh, $blacklist_file)) {#'<:encoding(UTF-8)',
while( my $line = <$fh>) {
chomp $line;
$old_dns{$line} = 1;
}
close($fh);
} else {
warn "Could not open file '$blacklist_file' $!";
}
#print Dumper \%old_dns;
my $ua = LWP::UserAgent->new(
ssl_opts => { verify_hostname => 0 },
protocols_allowed => ['http', 'https'],
timeout => 60,
);
my $response = $ua->get($blacklist_url);
my %new_dns = ();
if ($response->is_success) {
#print $response->decoded_content;
my @new_list = @{decode_json($response->decoded_content)};
%new_dns = map { $_ => 1 } @new_list;
# print Dumper \%new_dns;
} elsif ($response->is_error){
print "Error: $blacklist_url\n";
print $response->error_as_HTML;
} else {
die $response->status_line;
}
if ( scalar %new_dns && join( ' ', sort keys %old_dns) ne join( ' ', sort keys %new_dns) ) {
my $new = join( "\n", sort keys %new_dns);
#print $new."\n";
if (open(my $fh, '>', $blacklist_file)) {
print $fh $new;
close($fh);
# Set options in the constructor
my $resolver = Net::DNS::Resolver->new(
nameservers => [ '127.0.0.1' ],
recurse => 0,
debug => 0,
);
print "Have updates for black.list\n";
my $packet = $resolver->send( $reload_host, 'A' ) if $reload_host;
} else {
warn "Could not open file '$blacklist_file' $!";
}
} else {
print "black.list already updated\n";
}
1;
cpanm -n JSON JSON::PP Net::DNS LWP::UserAgent LWP::Protocol::https
perl /etc/dnsdist/get_black_list.pl
[
"*.lohotron.shop",
"porn.xxx",
"xn--80ayhh.xn--c1avg"
]
cp /etc/dnsdist/get_black_list.pl /etc/cron.hourly/get_black_list
chmod +x /etc/cron.hourly/get_black_list
===== SNMP =====
До основних репозиторіїв необхідно в кінець кожного додати "non-free", інакше деякі пакунки не буде знайдено для встановлення.
nano /etc/apt/sources.list
# deb cdrom:[Debian GNU/Linux 11.4.0 _Bullseye_ - Official amd64 NETINST 20220709-10:31]/ bullseye main
deb http://deb.debian.org/debian/ bullseye main non-free
deb-src http://deb.debian.org/debian/ bullseye main non-free
deb http://security.debian.org/debian-security bullseye-security main non-free
deb-src http://security.debian.org/debian-security bullseye-security main non-free
# bullseye-updates, to get updates before a point release is made;
# see https://www.debian.org/doc/manuals/debian-reference/ch02.en.html#_updates_and_backports
deb http://deb.debian.org/debian/ bullseye-updates main non-free
deb-src http://deb.debian.org/debian/ bullseye-updates main non-free
apt install software-properties-common
apt-add-repository non-free
apt update && apt install -y snmp-mibs-downloader snmp snmpd
comment line "mibs :" for snmp-mibs-downloader
nano /etc/snmp/snmp.conf
nano /etc/snmp/snmpd.conf
master agentx
agentxperms 0700 0700 _dnsdist _dnsdist
rocommunity dnsdist42
cd /usr/share/snmp/mibs
wget https://raw.githubusercontent.com/PowerDNS/pdns/master/pdns/dnsdistdist/DNSDIST-MIB.txt
wget https://www.circitor.fr/Mibs/Mib/F/FLOAT-TC-MIB.mib
chown -R _dnsdist:root /etc/dnsdist/
chown -R pdns:root /etc/powerdns/
chmod 775 /var/agentx/
Для активації SNMP потрібно в кінець файлу
nano /etc/dnsdist/dnsdist.conf
додати рядок
snmpAgent(true,"/var/agentx/master")
systemctl restart snmpd
systemctl restart dnsdist
journalctl -xe
Перевірити працездатність SNMP можна наступними командами
snmpwalk -v2c -c dnsdist42 127.0.0.1 .1.3.6.1.4.1.43315
snmpwalk -v2c -m DNSDIST-MIB -c dnsdist42 127.0.0.1 1.3.6.1.4.1.43315
Ось вивід останньої
DNSDIST-MIB::queries.0 = Counter64: 18
DNSDIST-MIB::responses.0 = Counter64: 1
DNSDIST-MIB::servfailResponses.0 = Counter64: 0
DNSDIST-MIB::aclDrops.0 = Counter64: 0
DNSDIST-MIB::ruleDrop.0 = Counter64: 0
DNSDIST-MIB::ruleNXDomain.0 = Counter64: 0
DNSDIST-MIB::ruleRefused.0 = Counter64: 0
DNSDIST-MIB::selfAnswered.0 = Counter64: 17
DNSDIST-MIB::downstreamTimeouts.0 = Counter64: 0
DNSDIST-MIB::downstreamSendErrors.0 = Counter64: 0
DNSDIST-MIB::truncFailures.0 = Counter64: 0
DNSDIST-MIB::noPolicy.0 = Counter64: 0
DNSDIST-MIB::latency01.0 = Counter64: 17
DNSDIST-MIB::latency110.0 = Counter64: 0
DNSDIST-MIB::latency1050.0 = Counter64: 0
DNSDIST-MIB::latency50100.0 = Counter64: 0
DNSDIST-MIB::latency1001000.0 = Counter64: 1
DNSDIST-MIB::latencySlow.0 = Counter64: 0
DNSDIST-MIB::latencyAVG100.0 = STRING: "2404.139327"
DNSDIST-MIB::latencyAVG1000.0 = STRING: "263.185870"
DNSDIST-MIB::latencyAVG10000.0 = STRING: "26.556655"
DNSDIST-MIB::latencyAVG1000000.0 = STRING: "0.265830"
DNSDIST-MIB::uptime.0 = Counter64: 33051
DNSDIST-MIB::realMemoryUsage.0 = Counter64: 73154560
DNSDIST-MIB::nonCompliantQueries.0 = Counter64: 0
DNSDIST-MIB::nonCompliantResponses.0 = Counter64: 0
DNSDIST-MIB::rdQueries.0 = Counter64: 18
DNSDIST-MIB::emptyQueries.0 = Counter64: 0
DNSDIST-MIB::cacheHits.0 = Counter64: 0
DNSDIST-MIB::cacheMisses.0 = Counter64: 0
DNSDIST-MIB::cpuUserMSec.0 = Counter64: 18324
DNSDIST-MIB::cpuSysMSec.0 = Counter64: 60627
DNSDIST-MIB::fdUsage.0 = Counter64: 97
DNSDIST-MIB::dynBlocked.0 = Counter64: 0
DNSDIST-MIB::dynBlockNMGSize.0 = Counter64: 0
DNSDIST-MIB::ruleServFail.0 = Counter64: 0
DNSDIST-MIB::securityStatus.0 = Counter64: 1
DNSDIST-MIB::specialMemoryUsage.0 = Counter64: 55439360
DNSDIST-MIB::ruleTruncated.0 = Counter64: 0
DNSDIST-MIB::backendName.0 = STRING: 127.0.0.1:5300
DNSDIST-MIB::backendName.1 = STRING: 127.0.0.1:5301
DNSDIST-MIB::backendLatency.0 = Counter64: 0
DNSDIST-MIB::backendLatency.1 = Counter64: 2
DNSDIST-MIB::backendWeight.0 = Counter64: 1
DNSDIST-MIB::backendWeight.1 = Counter64: 1
DNSDIST-MIB::backendOutstanding.0 = Counter64: 0
DNSDIST-MIB::backendOutstanding.1 = Counter64: 0
DNSDIST-MIB::backendQPSLimit.0 = Counter64: 0
DNSDIST-MIB::backendQPSLimit.1 = Counter64: 0
DNSDIST-MIB::backendReused.0 = Counter64: 0
DNSDIST-MIB::backendReused.1 = Counter64: 0
DNSDIST-MIB::backendState.0 = STRING: up
DNSDIST-MIB::backendState.1 = STRING: up
DNSDIST-MIB::backendAddress.0 = STRING: "127.0.0.1:5300"
DNSDIST-MIB::backendAddress.1 = STRING: "127.0.0.1:5301"
DNSDIST-MIB::backendPools.0 = STRING: auth
DNSDIST-MIB::backendPools.1 = STRING: recursor
DNSDIST-MIB::backendQPS.0 = Counter64: 0
DNSDIST-MIB::backendQPS.1 = Counter64: 0
DNSDIST-MIB::backendQueries.0 = Counter64: 0
DNSDIST-MIB::backendQueries.1 = Counter64: 1
DNSDIST-MIB::backendOrder.0 = Counter64: 1
DNSDIST-MIB::backendOrder.1 = Counter64: 1
Для відкриття доступу до SNMPD з мережі потрібно відкрити порт в файрволі
nft add rule ip filter input ct state new udp dport 161 counter accept comment "SNMPD"
та змінити параметр agentaddress в /etc/snmp/snmpd.conf з 127.0.0.1 на іп адресу, за якою можна звернутись до хоста з мережі, або на 0.0.0.0 для глобального досупу
===== Нотатки =====
+[[https://dnsdist.org/guides/cache.html|DnsDist Query cache]]
[[https://www.ylsoftware.com/news/712|Установка DNSCrypt-сервера]]
[[https://dnslookup.online/ptr.html]]
[[https://repo.powerdns.com/]]
[[https://fossies.org/linux/pdns-dnsdist/pdns/dnsdistdist/docs/advanced/snmp.rst]]
/etc/init.d/pdns-recursor restart
rec_control wipe-cache
/etc/init.d/pdns-recursor status
[[https://www.iana.org/assignments/enterprise-numbers/assignment/apply/| Зареєструвати свій номер (SNMP ENTERPRISE VENDOR NUMBER)]]