DNSCrypt довольно популярный вариант защиты обычно не шифруемого DNS-трафика от внешних лиц. Клиентский резолвер dnscrypt-proxy поддерживает в том числе протокол DNS-over-HTTPS, позволяя разрешать доменные имена через DoH.
Все бы ничего, только при использовании dnscrypt-proxy с настройками по умолчанию контейнеры Docker перестают корректно резолвить адреса. Исправим это, не включая слушатель DNS на публичных интерфейсах.
- Создаем dummy-адаптер, вешаем ему IP из частного диапазона.
- Заставляем dnscrypt-proxy слушать на этом IP, меняем настройки DNS соответственно.
- PROFIT
Суть проблемы
Для настройки DNS Docker копирует настройки DNS с хоста, копируя файл
/etc/resolv.conf
с хостовой машины.
При использовании dnscrypt-proxy и других кастомных резолверов
обычно их запускают на 127.0.0.1:53
, записывая этот
адрес как nameserver
в resolv.conf
, эта
же настройка приходит контейнеру. Так как контейнер находится в
другом сетевом пространстве имен, 127.0.0.1 у хоста и контейнера
разные.
Как исправить
Самым простым вариантом решения является запуск dnscrypt-proxy
на публичном адресе хоста и добавление этого же адреса в
/etc/resolv.conf
. Однако в таком случае мы выставляем
в сеть DNS-резолвер, чего хочется избежать.
Вместо этого мы создадим сетевой адаптер-затычку, через который
не идет трафик, но на который можно повесить IP-адрес из частного
диапазона; он будет роутабелен из контейнера, поскольку
Docker-контейнеры с сетью через бридж используют в качестве шлюза
по умолчанию хост. В Linux такой адаптер называется
dummy
.
Создаем адаптер
Если в вашей системе не загружен модуль ядра dummy
(lsmod | grep dummy
не показывает этот модуль),
загрузите его и включите его загрузку на постоянной основе:
# modprobe dummy# echo "dummy" >> /etc/modules-load.d/net_dummy.conf
Для того, чтобы создать и настроить dummy-адаптер, в современной Linux-системе с iproute2 достаточно выполнить две следующие команды:
# ip link add type dummy name dummy0# ip addr add dev dummy0 10.0.197.1/24
Создание адаптера для его использования на постоянной основе будет разниться в зависимости от используемого вами сетевого конфигуратора. Например, для systemd-networkd понадобятся два конфигурационных файла:
/etc/systemd/network/50-dummy0.netdev
:
[NetDev]Name=dummy0Kind=dummyDescription=Dummy network for dnscrypt-proxy
/etc/systemd/network/50-dummy0.network
:
[Match]Name=dummy0[Network]DHCP=noAddress=10.0.197.1/24DefaultRouteOnDevice=false
Изменяем настройки DNS
Для того, чтобы заставить dnscrypt-proxy слушать на новом
адресе, потребуется отредактировать
/etc/dnscrypt-proxy/dnscrypt-proxy.toml
. Отредактируем
директиву listen_addresses
и приведем ее к следующему
виду:
listen_addresses = ['127.0.0.1:53', '[::1]:53', '10.0.197.1:53']
Перезапустим dnscrypt-proxy, после чего в
/etc/resolv.conf
(либо где в вашем сетевом
конфигураторе находятся настройки DNS) оставим в списке серверов
имен один: nameserver 10.0.197.1
.
Проверяем
Запускаем контейнер:
% docker run -it --rm alpine:3.12# cat /etc/resolv.confnameserver 10.0.197.1# ping -c 1 ya.ru
Дополнительно
Если вы используете файрволл, разрешите входящий трафик на
10.0.197.1:53
из подсетей, из которых Docker выдает
адреса контейнерам.
В случае, если вы используете в качестве локального резолвера
systemd-resolved, направленный на dnscrypt-proxy (например, для
кэширования или для использования разных DNS-серверов для разных
интерфейсов), тот факт, что systemd-resolved слушает исключительно
на 127.0.0.53
и не позволяет изменение этого адреса,
вас смущать не должен: Docker определяет использование systemd-resolved и
вместо содержимого /etc/resolv.conf
копирует
содержимое /run/systemd/resolve/resolv.conf
,
генерируемое из настроек systemd-resolved.