Pull to refresh

DNSTT. DNS туннель для обхода блокировок

Level of difficultyMedium
Reading time11 min
Views12K

dnstt — это DNS-туннель (VPN over DNS), который может использовать резолверы DNS через HTTPS (DoH) и DNS через TLS (DoT). Проект написан на языке Go.

Руководство будет включать:

  1. Как работает DNSTT

  2. Настройку DNS для домена

  3. Настройку туннеля dnstt на сервере

  4. Настройку клиента dnstt на OpenWRT

  5. Настройку автозапуска сервера, клиента и tun2socks

1. Как работает DNSTT

DNS-туннель – это один из способов обойти сетевую цензуру. Цель рекурсивного DNS-резолвера — получать пакеты и пересылать их куда-то еще — по сути, работая как своего рода сетевой прокси. DNS-туннели через UDP с открытым текстом обычно считаются легко обнаруживаемыми из-за необычных DNS-сообщений, которые они используют. Однако DoH и DoT зашифрованы — внешний наблюдатель может видеть, что вы общаетесь с общедоступным резолвером, но не может расшифровать сообщения DNS и убедиться, что они являются частью туннельного протокола. (Сам резолвер все равно может легко определить, что вы используете туннель.)

Схема работы dnstt
Схема работы dnstt

dnstt по умолчанию использует сквозное шифрование и аутентификацию между клиентом туннеля и сервером туннеля. По словам создателей структура протокола обеспечивает более высокую производительность, чем другие туннели DNS. (на практике удалось добиться скорости около 1.5 Мбит/с на DOWN/UP при использовании резолвера DoH Google)

dnstt не предлагает сетевой интерфейс TUN/TAP или даже интерфейс прокси-сервера SOCKS или HTTP, а только соединение типа netcat между локальным сокетом TCP и удаленным сокетом TCP. Однако довольно легко заставить туннель действовать как стандартный прокси-сервер, запустив прокси на туннельном сервере.

DNS-туннель работает за счет того, что туннельный сервер выступает в качестве авторитетного резолвера для определенного доменного имени. Рекурсивный DNS преобразователь посередине действует как прокси, перенаправляя запросы для этого домена на туннельный сервер. Чтобы настроить DNS-туннель, вам понадобится доменное имя и хост, который может принимать UDP-пакеты, на котором вы запускаете туннельный сервер. Рассмотрим следующий пример настройки:

  • Ваше доменное имя example.com (вам понадобится уже зарегистрированный домен)

  • IPv4-адрес вашего сервера 203.0.113.2.

  • IPv6-адрес вашего сервера 2001:db8::2 (опционально)

2. Настройка DNS

Зайдите в панель конфигурации вашего регистратора имен и добавьте три записи:

Тип

Имя

Данные

A

tns.example.com

203.0.113.2

AAAA

tns.example.com

2001:db8::2

NS

t.example.com

tns.example.com

Метки «tns» и «t» могут быть любыми, но метка «tns» не должна быть субдоменом метки «t» (все, что находится под этим субдоменом, зарезервировано для полезных данных туннеля). Метка «t» должна быть короткой, поскольку пространство в DNS-сообщении ограничено, и часть его занимает имя домена.

3. Настройка туннеля на сервере Linux

В качестве примера используем сервер на Debian 10 (amd64)

Скачиваем бинарный файл с Github:

wget https://github.com/1andrevich/dnstt/releases/download/v0.20210424.0server/dnstt-server-amd64-linux.zip -O dnstt-server.zip

Альтернативно можно воспользоваться инструкцией по сборке пакета из официального руководства

Распаковываем:

unzip dnstt-server.zip

Разрешаем запуск исполняемого файла:

chmod +x dnstt-server-amd64-linux

Сначала вам необходимо сгенерировать секретный и открытый ключи для сквозного шифрования туннеля:

./dnstt-server-amd64-linux -gen-key -privkey-file server.key -pubkey-file server.pub

127.0.0.1:8000 — это TCP-адрес, на который будут перенаправляться входящие туннелированные потоки («удаленное приложение» на схеме).

Запускаем сервер:

./dnstt-server-amd64-linux -udp :5300 -privkey-file server.key t.example.com 127.0.0.1:8000

Туннельный сервер должен быть доступен через UDP-порт 53. Вы можете напрямую привязать его к порту 53 (-udp :53), но для этого вам потребуется запустить сервер от имени пользователя root. Лучше запустить сервер на непривилегированном порту, как показано выше, и использовать переадресацию портов для перенаправления на него порта 53. В Linux эти команды перенаправят порт 53 на порт 5300:

sudo iptables -I INPUT -p udp --dport 5300 -j ACCEPT
sudo iptables -t nat -I PREROUTING -i enp1s0 -p udp --dport 53 -j REDIRECT --to-ports 5300
sudo ip6tables -I INPUT -p udp --dport 5300 -j ACCEPT
sudo ip6tables -t nat -I PREROUTING -i enp1s0 -p udp --dport 53 -j REDIRECT --to-ports 5300

Для сохранения правил iptables после перезагрузки воспользуйтесь пакетом iptables-persistent или иным способом

Впишите ваш интерфейс по умолчанию вместо enp1s0 если у вас он другой (eth0, enp2s0 и т.д.)

Вам потребуется запущенная служба по адресу 127.0.0.1:8000, к чему туннельный сервер мог бы подключиться. Ниже приведены инструкции по запуску прокси-сервера. В целях тестирования вы можете использовать Ncat для вывода входящих данных на терминал:

sudo apt install ncat
ncat -l -k -v 127.0.0.1 8000

По умолчанию dnstt-server предполагает, что рекурсивный преобразователь поддерживает ответы DNS до определенного размера, а именно 1232 байта. Если выбранный вами резолвер не поддерживает такие большие ответы (в выходных данных сервера будут сообщения об ошибках), вы можете указать меньший предел, используя опцию -mtu, например -mtu 512.

4. Настройка клиента на OpenWRT

Рекомендуется роутер минимум с 128 МБ RAM (256 предпочтительно) и памятью более 16 Мб

Необходимо скачать с Github файл клиента для архитектуры вашего роутера

Определяем архитектуру:

grep "OPENWRT_ARCH" /etc/os-release | awk -F '"' '{print $2> }'

На выходе получаем ответ похожего вида (В моём случае TP-Link Archer C6U):

mipsel_24kc

Далее ищем пакет для архитектуры MIPS - Release v0.20210424.0 Binary Client · 1andrevich/dnstt (github.com)

Так как в репозитории Github есть версии для Mips и mipsle рекомендую сверится с OpenWRT Table of Hardware Wiki для вашего устройства

Для TP-Link Archer C6U - архитектура mipsle (он же mipsel)

Скачиваем по ссылке на роутер при помощи wget:

cd /tmp
wget https://github.com/1andrevich/dnstt/releases/download/v0.20210424.0/dnstt-client-mipsle-softfloat-linux.zip -O dnstt-client.zip

В моём случае подошла версия mipsle-softfloat, если на более позднем этапе установки выдаст ошибку "line 1: syntax error: unexpected "(" , то сверьтесь ещё раз с OpenWRT wiki и попробуйте версию hardfloat.

Далее файл необходимо будет распаковать, для экономии постоянной памяти установим пакет unzip в ОЗУ (он удалится при перезагрузке устройства)

opkg update
opkg install unzip -d ram

Распакуем архив с dnstt-client

/tmp/usr/bin/unzip dnstt-client.zip -d /tmp/dnstt-client

Разрешаем запуск исполняемого файла:

chmod +x /tmp/dnstt-client/ddnstt-client-mipsle-softfloat-linux

Проверяем работу программы:

./tmp/dnstt-client/dnstt-client-mipsle-softfloat-linux

Если выдаёт справку по командам то движемся дальше, если выдаёт ошибку "line 1: syntax error: unexpected "(" то возвращаемся к началу п.3

Перенесите содержимое файла server.pub с сервера на клиент, например скопировав его содержимое через буфер обмена.

Чтобы использовать резолвер DNS over HTTPS (DoH), используйте опцию -doh:

./tmp/dnstt-client/dnstt-client-mipsle-softfloat-linux -doh https://doh.example/dns-query -pubkey-file server.pub t.example.com 127.0.0.1:7000

Чтобы использовать резолвер DNS over TLS (DoT), используйте -dot:

./tmp/dnstt-client/dnstt-client-mipsle-softfloat-linux -dot dot.example:853 -pubkey-file server.pub t.example.com 127.0.0.1:7000

Проверяем соединение:

На сервере
На сервере
На клиенте
На клиенте

Для дальнейшей настройки, на сервере:

Ncat HTTP-прокси

Для тестирования довольно удобно использовать режим HTTP-прокси-сервера Ncat. Но имейте в виду, что прокси-сервер Ncat не предназначен для использования ненадежными клиентами; например, это не помешает им подключаться к портам локального хоста на туннельном сервере.

tunnel-server$ ncat -l -k --proxy-type http 127.0.0.1 8000
tunnel-server$ ./dnstt-server-amd64-linux -udp :5300 -privkey-file server.key t.example.com 127.0.0.1:8000

SSH SOCKS-прокси

OpenSSH имеет встроенный прокси-сервер SOCKS, который позволяет легко добавить прокси-сервер SOCKS на сервер, на котором уже установлен sshd.

На сервере установите SSH-соединение с локальным хостом, используя опцию -D, чтобы открыть SOCKS на порту 8000. Затем настройте туннельный сервер для перенаправления входящих подключений на порт 8000. Пусть туннельный клиент прослушивает свой собственный локальный порт 7000.

tunnel-server$ ssh -N -D 127.0.0.1:8000 -o NoHostAuthenticationForLocalhost=yes 127.0.0.1
#Введите пароль локального пользователя на сервере
tunnel-server$ ./dnstt-server-amd64-linux -udp :5300 -privkey-file server.key t.example.com 127.0.0.1:8000

Для HTTP и SOCKS Прокси на клиенте:

./tmp/dnstt-client/dnstt-client-mipsle-softfloat-linux -doh https://doh.example/dns-query -pubkey-file server.pub t.example.com 127.0.0.1:7000

5. Настройку автозапуска сервера, клиента и tun2socks

Если всё заработало, и вы хотите автоматизировать процесс запуска, то на сервере создаем службу systemd (Пример подходит для дистрибутивов Ubuntu, Debian и основанных на них)

Копируем файл в /usr/bin

sudo cp dnstt-server-amd64-linux /usr/bin/dnstt-server

Копируем server.key и server.pub в /etc/ssl/dnstt-server

sudo mkdir /etc/ssl/dnstt-server
sudo cp server.key /etc/ssl/dnstt-server/server.key
sudo cp server.pub /etc/ssl/dnstt-server/server.pub

Создаём службу systemd:

sudo nano /etc/systemd/system/dnstt-service.service

Содержимое /etc/systemd/system/dnstt-service.service:

[Unit]
Description=dnstt-server
After=network.target
StartLimitIntervalSec=0

[Service]
Type=simple
#Service variables
Environment=SSH_PORT="22"
Environment=PORT=":5300"
Environment=DOMAIN="t.example.com"
Environment=KEY="/etc/ssl/dnstt-server/server.key"
Environment=PUB="/etc/ssl/dnstt-server/server.pub"
ExecStart=/usr/bin/dnstt-server -udp $PORT -privkey-file $KEY $DOMAIN 127.0.0.1:8000
#For HTTP-Proxy (Uncomment)
ExecStartPost=/bin/sh -c '/usr/bin/ncat -l -k --proxy-type http 127.0.0.1 8000 >> /var/log/dnstt-server.log'
#For SSH SOCKS Proxy (Uncomment)
#ExecStartPost=/usr/bin/ssh -p$SSH_PORT -i ~/.ssh/id_rsa -N -f -D  127.0.0.1:8000 -o NoHostAuthenticationForLocalhost=yes 127.0.0.1
# optional items below
Restart=always

[Install]
WantedBy=multi-user.target

Запускаем службу:

sudo systemctl daemon-reload
sudo systemctl enable dnstt-service.service
sudo systemctl start dnstt-service.service

C настройкой сервера окончено.

Переходим к настройке клиента.

Создадим службу для dnstt-client в OpenWRT.

Для этого перенесём файл /tmp/dnstt-client/dnstt-client-mipsle-softfloat-linux в /usr/bin/ (ПЗУ)

cp /tmp/dnstt-client/dnstt-client-mipsle-softfloat-linux /usr/bin/dnstt-client

Не забываем указать путь к файлу server.pub, в примере файл будет находится в папке /etc/ssl/dnstt

mkdir /etc/ssl/dnstt
cp /tmp/server.pub /etc/ssl/dnstt/server.pub

Создаём файл /etc/init.d/dnstt-client следующего содержания, например при помощи vi:

#!/bin/sh /etc/rc.common

USE_PROCD=1

# starts after network starts
START=98 #Number in rc.d queue
# stops before networking stops
STOP=90

PROG=/usr/bin/dnstt-client
DOMAIN="t.example.com"
PUB="/etc/ssl/dnstt/server.pub"
DOH="https://cloudflare-dns.com/dns-query"
DOT="cloudflare-dns.com"
DOT_PORT="853"

start_service() {
  procd_open_instance
  procd_set_param user root
#For DNS over HTTPS
  procd_set_param command "$PROG" -doh "$DOH" -pubkey-file "$PUB" "$DOMAIN" 127.0.0.1:7000
#For DNS over TLS
  #procd_set_param command "$PROG" -dot "$DOT":"$DOT_PORT" -pubkey-file ""$PUB" ""$DOMAIN" 127.0.0.1:7000
  procd_set_param stdout 1
  procd_set_param stderr 1
  procd_set_param respawn ${respawn_threshold:-3600} ${respawn_timeout:-5} ${respawn_retry:-5}
  procd_close_instance
  echo "dnstt-client is working!"
}
stop_service() {
  service_stop $PROG
  echo "dnstt-client has stopped!"
}

reload_service() {
  stop
  sleep 5s
  echo "dnstt-client restarted!"
  start
}

Делаем файл исполняемым:

chmod +x /etc/init.d/dnstt-client

Делаем автозапуск:

ln -s /etc/init.d/dnstt-client /etc/rc.d/S98dnstt-client

Запускаем:

/etc/init.d/dnstt-client start

Далее настраиваем tun2socks.

Ищем пакет tun2socks для архитектуры MIPS - mipsle softfloat (подробнее в п.3) - Release v2.5.1 · xjasonlyu/tun2socks (github.com).

Скачиваем по ссылке на роутер при помощи wget:

cd /tmp
wget https://github.com/xjasonlyu/tun2socks/releases/download/v2.5.1/tun2socks-linux-mipsle-softfloat.zip -O tun2socks.zip

В моём случае подошла версия mipsle-softfloat, если на более позднем этапе установки выдаст ошибку "line 1: syntax error: unexpected "(" , то сверьтесь ещё раз с OpenWRT wiki и попробуйте версию hardfloat.

Распакуем архив с tun2socks

/tmp/usr/bin/unzip tun2socks.zip -d /tmp/tun2socks

Если выдаёт ошибку убедитесь что unzip установлен ( opkg install unzip -d ram)

Убедимся в наличии файла:

ls /tmp/tun2socks

Результат:

Проверяем работу программы:

/tmp/tun2socks/tun2socks-linux-mipsle-softfloat --help

Если выдаёт справку по командам то движемся дальше, если выдаёт ошибку "line 1: syntax error: unexpected "(" то возвращаемся к началу п.4.

Согласно документации tun2socks рекомендуется присвоить интерфейсу ip адрес (на практике маршрутизация работает и перенаправлением в dev tun3).

Для этого в /etc/config/network добавим следующее содержание:

config interface 'tunnel'
        option device 'tun3'
        option proto 'static'
        option ipaddr '172.16.11.1'
        option netmask '255.255.255.252'

Диапазон 172.16.11.0/30 выбран случайно, может быть любой диапазон частных адресов

Так же создаём зону и правило в /etc/config/firewall

config zone
        option name 'dnsproxy'
        option forward 'REJECT'
        option output 'ACCEPT'
        option input 'REJECT'
        option masq '1'
        option mtu_fix '1'
        option device 'tun3'
        option family 'ipv4'

config forwarding
        option name 'lan-dnsproxy'
        option dest 'dnsproxy'
        option src 'lan'
        option family 'ipv4'

Перезагружаем сеть

/etc/init.d/network restart

Далее проверяем работоспособность туннеля tun2socks:

/tmp/tun2socks/tun2socks-linux-mipsle-softfloat -device tun3 -proxy http://127.0.0.1:7000 -loglevel debug

Запустится туннель с логированием:

Где tun3 наименование туннеля.

proxy - адрес http прокси на порту 7000.

Проверяем доступность интерфейса:

ping -n 172.16.11.2

Для проверки работоспособности можно временно добавить маршрут к сайту ipecho.net (не закрывая tun2socks, в параллельной вкладке).

ip route add 34.160.111.0/24 dev tun3

Обращаемся к сайту и смотрим логи:

wget -qO- http://ipecho.net/plain | xargs echo

Если всё работает, команда вернёт IP вашего прокси DNSTT.

Можно закрывать tun2socks и удалить маршрут:

ip route del 34.160.111.0/24

Теперь перенесём файл в постоянную память устройства

mv /tmp/tun2socks/tun2socks-linux-mipsle-softfloat /usr/bin/tun2socks

Теперь настроим службу, чтобы интерфейс начинал работать при загрузке роутера.

В файл /etc/init.d/tun2socks вносим следующее содержание, например при помощи vi:

vi /etc/init.d/tun2socks
#!/bin/sh /etc/rc.common
USE_PROCD=1
# starts after network starts
START=99 #Number in rc.d queue
# stops before networking stops
STOP=89
PROG=/usr/bin/tun2socks
IF="tun3"
HOST="127.0.0.1"
PORT="7000"
start_service() {
procd_open_instance
procd_set_param user root
#For HTTP Proxy
procd_set_param command "$PROG" -device "$IF" -proxy http://"$HOST":"$PORT"
# For SSH SOCKS Proxy
#procd_set_param command "$PROG" -device "$IF" -proxy "$HOST":"$PORT"
  procd_set_param stdout 1
  procd_set_param stderr 1
  procd_set_param respawn ${respawn_threshold:-3600} ${respawn_timeout:-5} ${respawn_retry:-5}
procd_close_instance
#ip route save default > /tmp/defroute.save  #Saves existing default route
#ip route del default #Deletes existing default route
echo "tun2socks is working!"
}
#ip route add default via 172.16.11.2 dev "$IF" #Creates default route through the proxy
stop_service() {
service_stop $PROG
#ip route restore default < /tmp/defroute.save #Restores saved default route
echo "tun2socks has stopped!"
}
reload_service() {
stop
sleep 5s
echo "tun2socks restarted!"
start
}

Где IF - интерфейс, в данном примере tun3 (вместо tun0) чтобы избежать конфликтов с существующими туннелями.

PORT - Порт туннеля DNSTT-Клиента (по умолчанию 7000).

Если требуется перенаправлять весь трафик в DNSTT (чего делать не рекомендую, т.к. DNSTT всё же сильно режет скорость), уберите # перед строками 22, 23 и 26 и 29. Перед этим убедитесь что указали статический маршрут до сервера DNSTT через шлюз по умолчанию (подробнее здесь).

Для правильной работы скрипта (сохранение и восстановление маршрута по умолчанию) потребуется установить пакет ip-full

opkg install ip-full

Делаем файл исполняемым:

chmod +x /etc/init.d/tun2socks

Делаем автозапуск:

ln -s /etc/init.d/tun2socks /etc/rc.d/S99tun2socks

Запускаем:

/etc/init.d/tun2socks start

Проверяем, что интерфейс заработал:

ip a | grep 'tun3'

Если заработал, выдаст подобный результат:

tun3: <POINTOPOINT,MULTICAST,NOARP,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UNKNOWN group default qlen 500
    inet 172.16.11.1/30 brd 172.16.11.3 scope global tun3

Если нет, ответ будет пустым.

Далее необходимо настроить маршруты к которым роутер будет обращаться через прокси.

Можно настроить маршрут по умолчанию для всего трафика (убрать # перед строками, не рекомендую), либо настроить точечный обход блокировок (в конце статьи)

Если настроен маршрут по умолчанию, проверяем работоспособность:

wget -qO- http://ipecho.net/plain | xargs echo

Команда вернёт IP адрес сервера DNSTT.

Для точечного обхода блокировок и настройки таких маршрутов:

Точечный обход блокировок на роутере OpenWrt c помощью BGP / Хабр (habr.com) С помощью BGP (bird2).

Точечный обход блокировок PKH на роутере с OpenWrt с помощью WireGuard и DNSCrypt / Хабр (habr.com) (путём скачивания списков и настройкой маршрутов в iptables, nftables).

По клиентам для других платформ всё не очень разнообразно:

Tags:
Hubs:
Total votes 7: ↑7 and ↓0+7
Comments8

Articles