Пишем DNS proxy на Go

Автор: admin от 5-02-2018, 08:45, посмотрело: 168

Пишем DNS proxy на Go

Давно хотел решить проблему с рекламой. Наиболее простым способом сделать это на всех устройствах оказалось поднятие своего DNS сервера с блокированием запросов на получений IP адресов рекламных доменов.

dnsmasq, но мне захотелось грузить списки из интернета и получать какую-нибудь статистику по использованию. Поэтому я и решил писать свой сервер.



Конечно, он написан не полностью с нуля, вся работа с DNS взята из этой библиотеки.



Конфигурация



Работать программа начинает, конечно же, с загрузки конфигурационного файла. Сразу подумал о необходимости автоматической подгрузки конфига при его изменении дабы избежать рестарта сервера. Для этого пригодился пакет fsnotify.



Структура конфига:



type Config struct {
	Nameservers    []string      `yaml:"nameservers"`
	Blocklist      []string      `yaml:"blocklist"`
	BlockAddress4  string        `yaml:"blockAddress4"`
	BlockAddress6  string        `yaml:"blockAddress6"`
	ConfigUpdate   bool          `yaml:"configUpdate"`
	UpdateInterval time.Duration `yaml:"updateInterval"`
}


Тут самым интересным моментом является слежение за обновлениями файла конфигурации. С помощью библиотеки делается это довольно просто: мы создаем Watcher, цепляем к нему файл и слушаем события из канала. True Go!





BlackList



Конечно, по-скольку целью стоит блокировка неугодных сайтов, то их необходимо где-то хранить. Для этого при небольшой нагрузке подойдет простая хэш-таблица пустых структур, где в качестве ключа используется блокируемый домен. Хочу заметить, что необходимо наличие точки на конце.

Но так-как у нас нет одновременного read/write, то можно обойтись без мьютексов.





Кэширование



Изначально я думал обойтись без него, все-таки все мои устройства не создают существенного количества запросов. Но в один прекрасный вечер мой сервер каким-то образом обнаружили и начали флудить его одним и тем же запросом с частотой ~ 100 rps. Да, это немного, но ведь запросы проксируются на реальные namespace-сервера (в моем случае Google) и было бы очень неприятно получить блокировку.



Основной проблемой кэширования является большое количество различных запросов и их нужно хранить отдельно, поэтому получилась двухуровневая хеш-таблица.





Обработчик



Конечно, основной частью программы является обработчик входящих запросов, поэтому я его оставил на десерт. Основная логика примерно такая: мы получаем запрос, проверяем его наличие в блек-листе, проверяем наличие в кэше, проксируем запрос на реальные сервера.



Основной интерес представляет функция лукапа. В ней мы отправляем одновременно запрос сразу на все сервера (если успеем до прихода ответа) и ждем успешного ответа хотя бы от одного из них.





Метрики



Для метрики будем использовать клиент от prometheus. Используется он очень просто, сначала необходимо объявить счетчик, затем его зарегистрировать и в нужном месте вызвать метод Inc(). Главное не забыть запустить вебсервер с prometheus handler, чтобы он смог считывать метрики.





Думаю main не нуждается в представлении и описании. В данной статье код представлен в сокращенном формате



Полный код можно посмотреть в репозитории (конечно же приветствуются фиксы и дополнения). Также в репозитории есть файл для Docker и примерная конфигурация CI для Gitlab.



Спасибо за внимание.


Источник: Хабрахабр

Теги: go golang dns

Категория: Админитстрирование » Системное администрирование

Уважаемый посетитель, Вы зашли на сайт как незарегистрированный пользователь.
Мы рекомендуем Вам зарегистрироваться либо войти на сайт под своим именем.

Добавление комментария

Имя:*
E-Mail:
Комментарий:
Полужирный Наклонный текст Подчеркнутый текст Зачеркнутый текст | Выравнивание по левому краю По центру Выравнивание по правому краю | Вставка смайликов Выбор цвета | Скрытый текст Вставка цитаты Преобразовать выбранный текст из транслитерации в кириллицу Вставка спойлера
Введите два слова, показанных на изображении: *