PowerDNS AIO
В цій статті опишу встановлення комплексу для обслуговування DNS, в неї входить:
- Підготовка - базове налаштування системи та встановлення необхідних компонентів
- PowerDNS-Recursor - для обслуговування DNS-запитів клієнтів. Відповідає за рекурсивне вирішення DNS-запитів
- PowerDNS-Authoritative - для обслуговування DNS-запитів регуляторів та тримання власних DNS-зон. Відповідає за авторитарне вирішення DNS-запитів
- DNSDist - високопродуктивний балансувальник DNS-запитів (теж від команди PowerDNS). Забезпечує балансування навантаження та захист від DDoS-атак
- PowerDNS-Admin - веб-інтерфейс для керування PowerDNS-Authoritative
- Додаткові можливості - Додаткові можливості для покращення продуктивності та функціоналу
- DNSDist SNMP - для моніторингу станів серверів
- Чорний список DNSDist - для блокування шкідливого трафіку
- Логування запитів DNSDist - для відстеження на запити спецслужб
- Нотатки - додаткові посилання та інформація
Підготовка
В репозиторіях Debian, зазвичай застарілі версії, тому заглянемо на сайт https://repo.powerdns.com/
Там знаходимо стабільні версії програм і додамо їх до списку репозиторіїв
cat <<EOT > /etc/apt/sources.list.d/powerdns.list deb [signed-by=/etc/apt/keyrings/powerdns-pub.asc] http://repo.powerdns.com/debian bookworm-dnsdist-19 main deb [signed-by=/etc/apt/keyrings/powerdns-pub.asc] http://repo.powerdns.com/debian bookworm-auth-49 main deb [signed-by=/etc/apt/keyrings/powerdns-pub.asc] http://repo.powerdns.com/debian bookworm-rec-52 main EOT
Встиновимо сертифікат підпису для репозиторіїв stable
install -d /etc/apt/keyrings; curl https://repo.powerdns.com/FD380FBB-pub.asc | sudo tee /etc/apt/keyrings/powerdns-pub.asc
Налаштуємо пріоритет для репозиторіїв
cat <<EOT > /etc/apt/preferences.d/powerdns Package: * Pin: origin repo.powerdns.com Pin-Priority: 600 EOT
PowerDNS Recursor
PowerDNS Recursor - це рекурсивний DNS-сервер, який відповідає за вирішення DNS-запитів клієнтів. Основні функції:
- Рекурсивне вирішення DNS-запитів
- Кешування відповідей
- Фільтрація запитів
- Підтримка DNSSEC
Встановимо пакет pdns-recursor і збережемо оригінальні конфігураційні файли
apt-get update apt-get install -y pdns-recursor cp -r /etc/powerdns/ /etc/powerdns.orig/
Налаштуємо адресу та порт для прослуховування для версій від 5.0
cat <<EOT > /etc/powerdns/recursor.d/00_incoming.yml incoming: listen: !override - 127.0.0.1 - 172.16.0.16 port: 53 EOT
Налаштуємо адресу та порт для прослуховування для версій до 5.0
cat <<EOT > /etc/powerdns/recursor.d/recursor.local.conf local-address=0.0.0.0 local-port=53 EOT
Налаштуємо дозволені мережі для прослуховування для версій від 5.0
cat <<EOT > /etc/powerdns/recursor.d/01_allow-from.yml incoming: allow_from: !override - 0.0.0.0/0 # Allow from any - 127.0.0.1/8 # Allow from loopback - 193.0.0.1/8 # Allow from arpa (PTR) - 192.168.0.1/16 # Allow from local network EOT
Налаштуємо дозволені мережі для прослуховування для версій до 5.0
cat <<EOT > /etc/powerdns/recursor.d/allow-from.conf # !!! Першою лінією має бути запис "allow-from=[ip/mask]", будь-яка інша лінія як "allow-from+=[ip/mask]" (WITH "+" before "=") !!! allow-from=0.0.0.0/0 # Allow from any allow-from+=127.0.0.1/8 # Allow from loopback allow-from+=193.0.0.1/8 # Allow from arpa (PTR) allow-from+=192.168.0.1/16 # Allow from arpa (PTR) EOT
Далі необхідно налаштувати переадресацію запитів DNS на авторитарні сервери, які безпосередньо тримають зони доменів.
Тут можна піти двома шляхами на вибір:
- Перший і правильніший - налаштувати зв'язок з кореневими DNS серверами
cd /etc/powerdns/ wget ftp://ftp.rs.internic.net/domain/root.zone.gz && gunzip root.zone.gz systemctl disable systemd-resolved systemctl stop systemd-resolved ls -lh /etc/resolv.conf mv /etc/resolv.conf /etc/resolv.old.conf echo "nameserver 127.0.0.1" > /etc/resolv.conf echo "hint-file=/etc/powerdns/root.zone" >> /etc/powerdns/recursor.conf systemctl restart pdns-recursor
- Другий простіший, але менш продуктивний - переадресувати запити на інші DNS рекурсори, наприклад Cloudflare та Google
для версій від 5.0
cat <<EOT > /etc/powerdns/recursor.d/recursor.local.conf recursor: forward_zones_recurse: !override - zone: . recurse: true forwarders: - 1.1.1.1 - 1.0.0.1 - 8.8.8.8 - 8.8.4.4 EOT
для версій до 5.0
echo "forward-zones-recurse=.=1.1.1.1;1.0.0.1;8.8.8.8;8.8.4.4;" > /etc/powerdns/recursor.d/forward-zones-recurse.conf
chown -R pdns:root /etc/powerdns/ systemctl restart pdns-recursor netstat -tap | grep pdns
dig mydomain.local @127.0.0.1 dig @127.0.0.1
Install PowerDNS Server
PowerDNS Server (Authoritative) - це авторитарний DNS-сервер, який відповідає за обслуговування власних DNS-зон. Основні функції:
- Обслуговування авторитарних DNS-зон
- Підтримка різних бекендів (MySQL, PostgreSQL, SQLite)
- Підтримка DNSSEC
- API для управління зонами
Встановимо пакети pdns-server, pdns-backend-mysql та збережемо оригінальні конфігураційні файли
apt-get update apt-get install -y pdns-server pdns-backend-mysql cp -r /etc/powerdns/ /etc/powerdns.orig/ rm /etc/powerdns/pdns.d/*
Налаштуємо конфігурацію pdns.conf
cat <<EOT > /etc/powerdns/pdns.conf setgid=pdns setuid=pdns include-dir=/etc/powerdns/pdns.d launch= EOT
Налаштуємо конфігурацію прослуховування pdns.d/00_listen.conf
cat <<EOT > /etc/powerdns/pdns.d/00_listen.conf local-address=127.0.0.1 local-port=53 EOT
Налаштуємо конфігурацію тюнінгу pdns.d/09_tuning.conf
cat <<EOT > /etc/powerdns/pdns.d/09_tuning.conf allow-axfr-ips=127.0.0.1/32 cache-ttl=60 control-console=no default-ttl=3600 disable-axfr=no log-dns-queries=yes logging-facility=0 loglevel=4 max-queue-length=5000 max-tcp-connections=50 EOT
prepeare mysql db
Підключимося до MySQL сервера
mysql -u root -p
Створимо базу даних powerdns та користувача powerdns з паролем strongpassword (замініть на власний пароль)
CREATE DATABASE powerdns; GRANT ALL ON powerdns.* TO 'powerdns'@'localhost' IDENTIFIED BY 'strongpassword'; FLUSH PRIVILEGES; quit;
Якщо це нове встановлення, то створимо структуру бази даних
mysql powerdns < /usr/share/pdns-backend-mysql/schema/schema.mysql.sql
Інакше, відновимо структуру з вашої резервної копії
mysql powerdns < /tmp/backup_powerdns.sql
Після відновлення з резервної копії, перевіряємо наявність змін структури
ls /usr/share/pdns-backend-mysql/schema/ | grep -E -i '^[0-9]' 3.4.0_to_4.1.0_schema.mysql.sql 4.1.0_to_4.2.0_schema.mysql.sql 4.2.0_to_4.3.0_schema.mysql.sql 4.3.0_to_4.7.0_schema.mysql.sql
Якщо вони є, застосовуємо всі почерзі, починаючи з версії з резервної копії, наприклад з 4.1.0
mysql powerdns < /usr/share/pdns-backend-mysql/schema/4.1.0_to_4.2.0_schema.mysql.sql mysql powerdns < /usr/share/pdns-backend-mysql/schema/4.2.0_to_4.3.0_schema.mysql.sql mysql powerdns < /usr/share/pdns-backend-mysql/schema/4.3.0_to_4.7.0_schema.mysql.sql
Налаштуємо конфігурацію pdns.local.gmysql.conf для підключення до нашої базі даних
cat <<EOT > /etc/powerdns/pdns.d/pdns.local.gmysql.conf # MySQL Configuration # Launch gmysql backend launch+=gmysql # gmysql parameters gmysql-host=127.0.0.1 gmysql-port=3306 gmysql-dbname=powerdns gmysql-user=powerdns gmysql-password=strongpassword gmysql-dnssec=yes # gmysql-socket= EOT
Перезапустимо сервіси та перевіримо статус
systemctl restart pdns netstat -tap | grep pdns
Перевіримо роботу сервера
dig mydomain.local @127.0.0.1 dig @127.0.0.1
DNSDist
Dnsdist - це високопродуктивний DNS-, DoS- та abuse балансувальник.
Основне його завдання полягає у маршрутизації трафіку на найкращий сервер, що забезпечує максимальну продуктивність для дозволених користувачів, у той час як відбувається шунтування або блокування шкідливого трафіку.
Має величезну кількість корисних функцій:
- Фільтрувати трафік (з ядра)
- Перевіряти прямий трафік з консолі
- Затримувати та обмежувати швидкість поганих запитів
- Інтелектуальне балансування навантаження
- Обмеження QPS та ін.
Встановимо пакети dnsdist, dnstop та збережемо оригінальні конфігураційні файли
apt-get update -y apt-get install -y dnsdist dnstop cp -r /etc/dnsdist/ /etc/dnsdist.orig/
Створімо конфігураційний файл
cat <<EOT > /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')) EOT
Згенеруємо пароль для консолі
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-атаки!
Змінимо конфігурацію прослуховування для рекурсора версії від 5.0
cat <<EOT > /etc/powerdns/recursor.d/00_incoming.yml incoming: listen: !override - 127.0.0.1 - 172.16.0.16 port: 5301 EOT
Змінимо конфігурацію прослуховування для рекурсора версії до 5.0
cat <<EOT > /etc/powerdns/recursor.d/recursor.local.conf local-address=127.0.0.1 local-port=5301 EOT
Змінимо конфігурацію прослуховування для авторитарного сервера:
cat <<EOT > /etc/powerdns/pdns.d/pdns.local.conf local-address=127.0.0.1 local-port=5300 EOT
Перезапустимо сервіси та перевіримо статус
service pdns-recursor restart service pdns restart
https://stat.ripe.net/widget/dns-check
Додаємо сервіс dnsdist в автозавантаження та перезапускаємо:
systemctl enable dnsdist
systemctl restart dnsdist
Панель адміністрування PowerDNS-Admin
PowerDNS-Admin - це веб-інтерфейс для управління PowerDNS:
- Управління DNS-зонами
- Управління записами
- Управління користувачами
- Моніторинг стану
- API для автоматизації
https://computingforgeeks.com/install-powerdns-and-powerdns-admin-on-debian/
https://github.com/PowerDNS-Admin/PowerDNS-Admin/wiki/Running-PowerDNS-Admin-on-Ubuntu-or-Debian
Встановимо необхідні пакети
apt install python3-dev python3-venv libpq-dev default-libmysqlclient-dev libsasl2-dev libldap2-dev libssl-dev libxml2-dev libxslt1-dev libxmlsec1-dev libffi-dev pkg-config apt-transport-https virtualenv build-essential curl jq
Встановимо Node.js
curl -sL https://deb.nodesource.com/setup_20.x | bash - apt-get update && apt install -y nodejs
Встановимо Yarn
curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | gpg --dearmor | sudo tee /usr/share/keyrings/yarnkey.gpg > /dev/null echo "deb [signed-by=/usr/share/keyrings/yarnkey.gpg] https://dl.yarnpkg.com/debian stable main" | sudo tee /etc/apt/sources.list.d/yarn.list apt-get update && apt-get install yarn
Підключимося до MySQL сервера
mysql -u root -p
Створимо базу даних для панелі адміністрування
CREATE DATABASE poweradmin CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; GRANT ALL PRIVILEGES ON poweradmin.* TO 'pdnsadmin'@'%' IDENTIFIED BY 'p4ssw0rd'; FLUSH PRIVILEGES; quit
Склонуємо репозиторій панелі адміністрування
git clone https://github.com/PowerDNS-Admin/PowerDNS-Admin.git /usr/local/powerdns-admin
УВАГА! Якщо не перейти на конкретну версію з доступних тегів, то буде використана остання версія розробки з гілки master, яка не є стабільною.
Перейдемо до директорії панелі адміністрування та створимо віртуальне середовище
cd /usr/local/powerdns-admin
Переглянемо список доступних тегів
git config --global --add safe.directory /usr/local/powerdns-admin git tag
вивід
... v0.4.0 v0.4.1 v0.4.2
Отримуємо код останньої версії
git checkout v0.4.2
Створюємо віртуальне середовище та активуємо його
python3 -m venv .venv --prompt VirtualEnv source .venv/bin/activate python -V
Встановимо необхідні залежності
pip install --upgrade pip pip install -r requirements.txt
Створюємо конфігураційний файл
cat <<EOT > /usr/local/powerdns-admin/configs/production.py import os import urllib.parse basedir = os.path.abspath(os.path.dirname(__file__)) ### BASIC APP CONFIG SALT = '$2b$12$yLUMTIfl21FKJQpTkRQXCu' SECRET_KEY = 'e951e5a1f4b94151b360f47edf596dd2' BIND_ADDRESS = '0.0.0.0' PORT = 9191 OFFLINE_MODE = False ### DATABASE CONFIG SQLA_DB_USER = 'pdnsadmin' SQLA_DB_PASSWORD = 'p4ssw0rd' SQLA_DB_HOST = '127.0.0.1' SQLA_DB_NAME = 'poweradmin' SQLALCHEMY_TRACK_MODIFICATIONS = True ### DATABASE - MySQL SQLALCHEMY_DATABASE_URI = 'mysql://{}:{}@{}/{}'.format( urllib.parse.quote_plus(SQLA_DB_USER), urllib.parse.quote_plus(SQLA_DB_PASSWORD), SQLA_DB_HOST, SQLA_DB_NAME ) # Зберігаємо сесію в файлі якщо версія python більше рівній 3.11 інакше 'sqlalchemy' SESSION_TYPE = 'filesystem' if sys.version_info >= (3, 11) else 'sqlalchemy' ### DATABASE - SQLite #SQLALCHEMY_DATABASE_URI = 'sqlite:///' + os.path.join(basedir, 'pdns.db') EOT
Додаємо правило для порту 9191 в nftables. Не забудьте поправити правило в /etc/nftables.conf
nft add rule ip filter input ct state new tcp dport 9191 counter accept comment "PDNS_ADMIN"
Експортуємо змінні середовища
export FLASK_CONF=../configs/production.py export FLASK_APP=powerdnsadmin/__init__.py
Виконуємо команду для оновлення бази даних
flask db upgrade
Виконуємо команду для побудови ресурсів
yarn install --pure-lockfile flask assets build
Змінюємо власника директорії
chown -R www-data:root /usr/local/powerdns-admin
Створюємо файл сервісу
cat <<EOF > /etc/systemd/system/powerdns-admin.service [Unit] Description=PowerDNS-Admin Wants=network.target After=network.target [Service] PIDFile=/run/powerdns-admin/pid User=www-data Group=root WorkingDirectory=/usr/local/powerdns-admin ExecStart=/usr/local/powerdns-admin/.venv/bin/gunicorn --pid /run/powerdns-admin/pid --workers 2 --bind '0.0.0.0:9191' 'powerdnsadmin:create_app()' ExecReload=/bin/kill -s HUP $MAINPID ExecStop=/bin/kill -s TERM $MAINPID PrivateTmp=true Restart=on-failure RestartSec=10 StartLimitInterval=0 [Install] WantedBy=multi-user.target EOF
Додаємо змінні середовища
mkdir -p /etc/systemd/system/powerdns-admin.service.d/ tee /etc/systemd/system/powerdns-admin.service.d/override.conf<<EOF [Service] Environment="FLASK_CONF=../configs/production.py" EOF
Створюємо директорію для зберігання PID та додаємо правило для неї
echo 'd /run/powerdns-admin 0755 www-data root -' > /etc/tmpfiles.d/powerdns-admin.conf mkdir -p /run/powerdns-admin chown -R www-data:root /run/powerdns-admin
Перезавантажуємо сервіси
systemctl daemon-reload
systemctl enable powerdns-admin
systemctl restart powerdns-admin
systemctl status powerdns-admin
Додаємо конфігурацію для API до авторитарного сервера
cat <<EOT > /etc/powerdns/pdns.d/api.conf webserver-port=8081 api=yes api-key=changemechangemechangeme EOT chown -R pdns:root /etc/powerdns/
Перезавантажуємо сервіси
systemctl restart pdns
Переходимо на http://your-host-ip:9191/ та створюємо користувача
Натисніть кнопку “Create an account” та зареєструйте користувача. Перший користувач буде в ролі Адміністратора.
Надайте інформацію про підключення до PowerDNS API перед тим, як PowerDNS-Admin зможе запитувати дані вашого PowerDNS. Це робиться в розділі Settings > PDNS
Додаткові можливості
Чорний список DNSDist
Чорний список DNSDist дозволяє блокувати небажані домени з різних причин:
- Блокування шкідливих доменів
- Блокування реклами
- Блокування фішингових сайтів
- Блокування піратського контенту
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
Додаємо зміни фалу сервісу для затримки завантаження чорного списку
mkdir -p /etc/systemd/system/dnsdist.service.d/ cat <<EOF > /etc/systemd/system/dnsdist.service.d/override.conf [Service] ExecStartPost=-/usr/bin/sleep 5 ExecStartPost=-/usr/bin/env dig @127.0.0.1 reload.blacklist.local EOF
Перезавантажуємо сервіси
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;
Дамо дозвіл на виконання скрипту
chmod +x /etc/dnsdist/get_black_list.pl
Встановлюємо необхідні модулі для perl
apt install libperl-dev cpanminus cpanm -n JSON JSON::PP Net::DNS LWP::UserAgent LWP::Protocol::https
Виконуємо скрипт для завантаження чорного списку
perl /etc/dnsdist/get_black_list.pl
Приклад файлу black.list
[ "*.lohotron.shop", "porn.xxx", "xn--80ayhh.xn--c1avg" ]
Створимо юніт-файл сервісу завантаження чорного списку
cat <<EOT >> /etc/systemd/system/get-blacklist.service [Unit] Description=Run get_black_list.pl to update DNS blocklist [Service] Type=oneshot ExecStart=/usr/bin/perl /etc/dnsdist/get_black_list.pl EOT
Створимо юніт-файл таймеру для запуску нашого сервісу кожні 20 хвилин
cat <<EOT >> /etc/systemd/system/get-blacklist.timer [Unit] Description=Run get_black_list.pl every 20 minutes [Timer] OnBootSec=5min OnUnitActiveSec=20min Unit=get-blacklist.service [Install] WantedBy=timers.target EOT
Додамо в автозавантаження та запустимо наш таймер
sudo systemctl daemon-reexec sudo systemctl daemon-reload sudo systemctl enable --now get-blacklist.timer
перевіримо лог спрацювання таймера
journalctl -u get-blacklist.service
DNSDist SNMP
SNMP (Simple Network Management Protocol) дозволяє моніторити стан DNS-серверів:
- Кількість запитів/відповідей
- Затримки обробки запитів
- Стан бекендів
- Використання ресурсів
Додаємо репозиторій non-free в apt
apt install software-properties-common
apt-add-repository non-free
Додаємо репозиторій non-free в apt (альтернативний спосіб)
nano /etc/apt/sources.list
Додаємо рядок “non-free” в кінець кожного додати “non-free”.
deb http://deb.debian.org/debian/ bookworm main non-free deb-src http://deb.debian.org/debian/ bookworm main non-free deb http://security.debian.org/debian-security bookworm-security main non-free deb-src http://security.debian.org/debian-security bookworm-security main non-free # bookworm-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/ bookworm-updates main non-free deb-src http://deb.debian.org/debian/ bookworm-updates main non-free
Встановлюємо необхідні пакунки
apt update && apt install -y snmp-mibs-downloader snmp snmpd
Коментуємо рядок “mibs :” для snmp-mibs-downloader
sudo sed -i 's/^mibs\s*:/#mibs :/' /etc/snmp/snmp.conf
Вмикаємо snmpd agentx
cat <<EOT > /etc/snmp/snmpd.conf.d/00_agent.conf master agentx EOT
Конфігурація snmpd agentx для dnsdist
cat <<EOT > /etc/snmp/snmpd.conf.d/10_dnsdist.conf agentxperms 0700 0700 _dnsdist _dnsdist rocommunity dnsdist42 EOT
Вказуємо IP, на яких буде слухати snmp agentx.
cat <<EOT > /etc/snmp/snmpd.conf.d/99_bind_address.conf agentaddress 127.0.0.1,[::1], 172.16.0.16 EOT
Встановлюємо необхідні mibs
wget -P /usr/share/snmp/mibs https://raw.githubusercontent.com/PowerDNS/pdns/master/pdns/dnsdistdist/DNSDIST-MIB.txt wget -P /usr/share/snmp/mibs https://mibbrowser.online/mibs/FLOAT-TC-MIB.mib
Правимо права до каталогу
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 для глобального досупу
{{anchor:configure_dnsdist_logger} }
Логування запитів DNSDist
Логування запитів DNSDist дозволяє перенаправити всі DNS-запити на віддалений syslog сервер
інформаційний блок
apt install lua-socket
nano /etc/dnsdist/conf.d/00_logger.lua
-- apt install lua-socket -- Спроба підключити luasocket local socket_ok, socket = pcall(require, "socket") -- UDP сокет для syslog local udp if socket_ok then udp = socket.udp() udp:settimeout(0) udp:setpeername("172.16.0.48", 514) end -- Функція для декодування qtype у рядок function qtype_to_string(qtype) if not qtype then return nil end -- Якщо qtype не вказано, повертаємо nil local names = { [1]="A",[2]="NS",[3]="MD",[4]="MF",[5]="CNAME",[6]="SOA",[7]="MB",[8]="MG",[9]="MR",[10]="NULL",[11]="WKS",[12]="PTR",[13]="HINFO", [14]="MINFO",[15]="MX",[16]="TXT",[17]="RP",[18]="AFSDB",[19]="X25",[20]="ISDN",[21]="RT",[22]="NSAP",[23]="NSAP-PTR",[24]="SIG", [25]="KEY",[26]="PX",[27]="GPOS",[28]="AAAA",[29]="LOC",[30]="NXT",[31]="EID",[32]="NIMLOC",[33]="SRV",[34]="ATMA",[35]="NAPTR", [36]="KX",[37]="CERT",[38]="A6",[39]="DNAME",[40]="SINK",[41]="OPT",[42]="APL",[43]="DS",[44]="SSHFP",[45]="IPSECKEY",[46]="RRSIG", [47]="NSEC",[48]="DNSKEY",[49]="DHCID",[50]="NSEC3",[51]="NSEC3PARAM",[52]="TLSA",[53]="SMIMEA",[55]="HIP",[56]="NINFO", [57]="RKEY",[58]="TALINK",[59]="CDS",[60]="CDNSKEY",[61]="OPENPGPKEY",[62]="CSYNC",[63]="ZONEMD",[64]="SVCB",[65]="HTTPS", [99]="SPF",[100]="UINFO",[101]="UID",[102]="GID",[103]="UNSPEC",[104]="NID",[105]="L32",[106]="L64",[107]="LP",[108]="EUI48", [109]="EUI64",[249]="TKEY",[250]="TSIG",[255]="ANY",[256]="URI",[257]="CAA",[258]="AVC",[259]="TA",[260]="DLV" } return names[qtype] or "TYPE"..tostring(qtype) -- Якщо qtype не відповідає жодному з визначених типів, повертаємо "TYPE" + рядок qtype end -- Формат часу з мікросекундами (UTC ISO8601) local function timestamp_us() local t = os.date("!*t") local usec = math.floor(socket.gettime() * 1e6) % 1e6 return string.format("%04d-%02d-%02dT%02d:%02d:%02d.%06dZ", t.year, t.month, t.day, t.hour, t.min, t.sec, usec) end -- Логер function myFuncLoger(dq) local ip = dq.remoteaddr:toString() local ip_version = dq.remoteaddr:isIPv6() and "IPv6" or "IPv4" local qname = dq.qname:toStringNoDot() local qtype = qtype_to_string(dq.qtype) local protocol = dq:getProtocol() local time = timestamp_us() -- Побудова JSON local json = string.format( '{"timestamp":"%s","ip_version":"%s","client_ip":"%s","protocol":"%s","query_type":"%s","query_name":"%s"}', time, ip_version, ip, protocol, qtype, qname ) -- Лог в консоль dnsdist -- infolog("[LOG JSON] " .. json) -- Відправка if udp then udp:send(json) else local fifo = io.open("/var/run/dnsdist_query_log.fifo", "w") if fifo then fifo:write(json .. "\n") fifo:flush() fifo:close() end end return DNSAction.None end if udp then infolog "[LOGGER] Logger initialized: SYSLOG-UDP" else infolog "[LOGGER] Logger initialized: FIFO fallback" end addAction(NetmaskGroupRule(recursive_ips), LuaAction(myFuncLoger))
Нотатки
https://dnslookup.online/ptr.html
https://fossies.org/linux/pdns-dnsdist/pdns/dnsdistdist/docs/advanced/snmp.rst
Зареєструвати свій номер (SNMP ENTERPRISE VENDOR NUMBER)
/etc/init.d/pdns-recursor restart rec_control wipe-cache /etc/init.d/pdns-recursor status