brainstate

necronicle/keenetic-doq · source

Snapshot 0 · 2026-11-04T12:47:28Z · Go · GPL-3.0

Content fingerprint327b1533d021cf8031702d793cd568084d478a22258ec2235c3587e863db32c8

GitHub description at capture

DNS-over-QUIC (RFC 9250) for Keenetic routers — instead of stock DoT/DoH, port 53 untouched

README at capture

# keenetic-doq

[English](README.en.md)

![CI](https://github.com/necronicle/keenetic-doq/actions/workflows/ci.yml/badge.svg)
![Release](https://img.shields.io/github/v/release/necronicle/keenetic-doq)

**Telegram: [топик DoQ в @zapret2keenetic](https://t.me/zapret2keenetic/55256)** — вопросы, помощь с настройкой, обсуждение  
**Веб-интерфейс: [keenetic-doq-web](https://github.com/accept4/keenetic-doq-web)** — веб-панель управления от @accept4 (Keenetic)

DNS-over-QUIC для **Keenetic** и **OpenWRT** с локальным SmartDNS.

`doqd` принимает обычные DNS-запросы от штатного DNS роутера. Для имён из hosts
он сам возвращает адреса прокси, для остальных использует обычные DoQ-серверы.
Отдельных гео-DNS, закрепления сервера обхода и гео-полосы больше нет.

```text
клиенты → DNS роутера → doqd → имя из hosts: локальный ответ с IP прокси
                            → остальные имена: кэш → обычные DoQ upstream
```

В бинарник включён снимок **1025 точных имён** на 24 октября 2026 года:
список GeoHide EU, добавления Sora и `releases.openai.com` для загрузки Codex.
Прокси — `5.175.161.3` и `5.175.161.40`. Cloudflare Zero Trust для этого не нужен.
**Сторонние прокси по-прежнему нужны:** doqd подставляет адрес, а не создаёт
зарубежный выход. Доступность каждого сервиса зависит от прокси и полноты списка.

Обычный DNS выбирается по сглаженному времени ответа среди здоровых основных
серверов. При задержке первого параллельно спрашивается следующий. Это не
рассылка каждого запроса всем серверам. Резервные серверы используются при
отказах основных. Сохраняются кэш, объединение одинаковых запросов и выдача
устаревшего ответа при недоступности upstream.

## Локальные правила

- Точное совпадение имени, без учёта регистра и завершающей точки. Запись
  `example.com` не означает `*.example.com`.
- A и AAAA содержат только адреса соответствующего семейства из hosts. Если
  IPv6-адреса нет, AAAA получает NOERROR с пустым ответом, а не NXDOMAIN.
- Для этих имён HTTPS/SVCB возвращаются без записей: реальные IP-подсказки
  и ECH-конфигурация не должны увести соединение мимо прокси. Это не отключает HTTPS.
- TTL локального ответа — 60 секунд. Локальная подмена не объявляется
  подтверждённой DNSSEC (флаг AD не выставляется).
- Автоматического выбора другого провайдера прокси и фонового скачивания
  hosts нет. Адреса берутся именно из выбранного файла.

Гарантии относятся к запросам, поступившим в doqd. Собственный DNS браузера,
VPN или другой DNS роутера может обойти эти правила. Старые ответы на устройствах
могут оставаться до истечения TTL.

## Требования

| | Keenetic | OpenWRT |
|---|---|---|
| Прошивка | KeeneticOS с [Entware](https://help.keenetic.com/hc/ru/articles/360000925139) | OpenWRT 24.10 или 25.12 с dnsmasq (стандартная сборка) |
| Скачивание | `curl` (`opkg install curl`) | `wget` из прошивки (uclient-fetch) или `curl` |
| Флеш | место в Entware (USB или встроенная память) | 4–5 МБ свободно на JFFS2/UBIFS, 9 МБ на ext4/f2fs; установщик проверит |
| Память | около 13–15 МБ RAM на демона | около 13–15 МБ RAM на демона |

На Keenetic busybox-`wget` собран без TLS и не открывает `https://`. Поэтому
нужен `curl`. Всё остальное для doqd и его скриптов есть в busybox.

Если Entware не устанавливается (сервер пакетов `bin.entware.net` обрывает
загрузку), возьмите офлайн-установщик из репозитория
[entware-offline](https://github.com/necronicle/entware-offline): официальный
установщик с пакетами внутри и с `curl`, сеть при установке не нужна.

Бинарник весит 7–8,5 МБ. JFFS2 и UBIFS на флеше роутеров сжимают файлы,
поэтому там он занимает меньше. Установщик для OpenWRT оценивает нужное
место до копирования. Если место всё же кончилось, установщик удаляет doqd
и возвращает dnsmasq в исходное состояние.

Для предыдущих выпусков OpenWRT 24.10.8 и 25.12.5 проверены в эмуляторе QEMU на aarch64, mipsel,
mips64, mips64el и armv7. Сборки amd64, 386, riscv64 и armv5 проверены
запуском с DoQ-запросом. Сборка loong64 собрана, но не запускалась.

### Архитектуры

| Бинарник | Keenetic (Entware) | OpenWRT (`DISTRIB_ARCH`) |
|---|---|---|
| `doqd-linux-arm64` | `aarch64-*` | `aarch64_*` |
| `doqd-linux-mipsle` | `mipsel-*` | `mipsel_*` |
| `doqd-linux-mips` | `mips-*` | `mips_*` |
| `doqd-linux-armv7` | — | `arm_cortex-a*_vfp*`, `arm_cortex-a*_neon*` |
| `doqd-linux-armv5` | — | ARM без FPU: `arm_cortex-a7`, `arm_cortex-a9`, `arm_arm926ej-s`, `arm_arm1176jzf-s_vfp`, `arm_xscale` |
| `doqd-linux-amd64` | — | `x86_64` |
| `doqd-linux-386` | — | `i386_pentium4`, `i386_pentium-mmx` |
| `doqd-linux-mips64` | — | `mips64_*` |
| `doqd-linux-mips64le` | — | `mips64el_*` |
| `doqd-linux-riscv64` | — | `riscv64_*` |
| `doqd-linux-loong64` | — | `loongarch64_*` |

Установщик выбирает бинарник сам. Если сборка `armv7` не запускается
(у процессора нет FPU), установщик берёт `armv5`.

Go не собирает программы под ARMv4 (`arm_fa526`), ARM big-endian
(`armeb_xscale`), PowerPC (`powerpc_*`) и ARC. На этих платформах doqd не
работает.

## Установка на Keenetic

Подключитесь к роутеру по SSH (Entware, порт 222) и выполните:

```sh
curl -fsSL https://raw.githubusercontent.com/necronicle/keenetic-doq/main/install.sh | sh
```

Установщик делает следующее:

1. Определяет архитектуру Entware.
2. Скачивает бинарник из релиза и сверяет SHA256.
3. Ставит `/opt/sbin/doqd`.
4. Пишет конфиг `/opt/etc/doqd.conf` с LAN-адресом роутера.
5. Пишет init-скрипт `/opt/etc/init.d/S56doqd` и запускает демона.
6. Регистрирует doqd в KeeneticOS: `ip name-server <LAN-IP>:5354`.

Существующий конфиг сохраняется с учётом описанной ниже миграции старых гео-настроек.

Без доступа к GitHub скопируйте бинарник на роутер и выполните:

```sh
sh install.sh --local ./doqd-linux-arm64
```

После установки выключите DoT и DoH в KeeneticOS. Иначе роутер не
спрашивает doqd (см. [ниже](#keenetic)).

## Установка на OpenWRT

Подключитесь к роутеру по SSH и выполните:

```sh
wget -qO- https://raw.githubusercontent.com/necronicle/keenetic-doq/main/install-openwrt.sh | sh
```

Установщик делает следующее:

1. Определяет архитектуру по `DISTRIB_ARCH` из `/etc/openwrt_release`.
2. Скачивает бинарник из релиза и сверяет SHA256.
3. Проверяет свободное место на флеше.
4. Ставит `/usr/sbin/doqd`.
5. Пишет конфиг `/etc/doqd.conf` (`listen 127.0.0.1:5354`).
6. Пишет init-скрипт procd `/etc/init.d/doqd`. procd перезапускает демона
   после любого сбоя.
7. Добавляет файлы doqd в список sysupgrade. Так doqd переживает обновление
   прошивки с сохранением настроек.
8. Запускает демона и ждёт, когда doqd ответит на запрос. **Если doqd не
   ответил, установщик останавливается, а dnsmasq работает со своими
   исходными серверами.** DNS роутера продолжает работать.
9. Делает doqd единственным сервером dnsmasq (подробно — в разделе
   [OpenWRT](#openwrt)).
10. Проверяет запрос через dnsmasq на порту 53. Если ответа нет, установщик
    возвращает dnsmasq в исходное состояние.

Без доступа к GitHub скопируйте бинарник на роутер и выполните:

```sh
sh install-openwrt.sh --local ./doqd-linux-mipsle
```

Повторный запуск обновляет doqd. Конфиг и исходные настройки dnsmasq
остаются.

## Как doqd встроен в DNS роутера

### Keenetic

Для KeeneticOS doqd — обычный DNS-сервер, прописанный вручную. Какой сервер
ответит клиенту, решает штатная логика роутера:

- **Включены DoT или DoH — роутер не спрашивает doqd.** По
  [справке Keenetic](https://support.keenetic.ru/ultra/kn-1811/ru/31543.html)
  все запросы тогда идут только на серверы DoT/DoH. DNS провайдера и
  серверы, прописанные вручную (в том числе doqd), не используются.
  Запасного переключения на них нет. Выключите DoT и DoH.
- **DoT и DoH выключены — запрос уходит сразу на все обычные серверы**: на
  doqd, на DNS провайдера и на другие серверы, прописанные вручную. Клиент
  получает самый быстрый ответ
  ([справка Keenetic](https://support.keenetic.ru/ultra/kn-1811/ru/22961.html)).

DNS провайдера можно оставить. Тогда часть ответов придёт от него, и заранее
не известно, какая. Чтобы все запросы шли через doqd, отключите DNS
провайдера:

1. В настройках подключения к интернету включите «игнорировать DNS
   провайдера». В CLI это `interface <имя-подключения> ip no name-servers`.
2. Выполните `system configuration save`.

После этого DNS роутера работает, только пока работает doqd.

Устройства с профилем интернет-фильтра (AdGuard DNS, Яндекс.DNS, SkyDNS)
спрашивают DNS фильтра, а не doqd.

### OpenWRT

На OpenWRT doqd стоит за dnsmasq. Установщик меняет в `/etc/config/dhcp`
(секция `dhcp.@dnsmasq[0]`) три вещи:

- `server 127.0.0.1#5354` — dnsmasq отправляет запросы в doqd.
- `noresolv 1` — dnsmasq не берёт DNS-серверы провайдера (WAN). Иначе
  dnsmasq отправлял бы каждый запрос на один из своих серверов, не
  обязательно на doqd
  ([man dnsmasq](https://thekelleys.org.uk/dnsmasq/docs/dnsmasq-man.html),
  `--strict-order`).
- `server /openwrt.pool.ntp.org/<bootstrap>` — имена серверов NTP идут на
  bootstrap-серверы обычным DNS. Причина ниже.

**Зачем обход для NTP.** После перезагрузки часы роутера могут отставать.
OpenWRT ставит часы по самому новому файлу в `/etc`
([sysfixtime](https://github.com/openwrt/openwrt/blob/main/package/base-files/files/etc/init.d/sysfixtime)).
Если часы отстают, сертификаты DoQ-серверов кажутся ещё не выданными. Тогда
doqd не может установить TLS-соединение. Без обхода NTP тоже спрашивал бы
doqd и не получил бы ответа: часы не исправились бы никогда. С обходом NTP
ставит часы, и doqd начинает работать сам. Так же поступает
[руководство OpenWRT](https://openwrt.org/docs/guide-user/base-system/dhcp_configuration)
(раздел «Race conditions with sysntpd»). Установщик берёт имена из
`system.ntp.server` и адреса из строк `bootstrap` в `/etc/doqd.conf`.

Исходное значение `noresolv` и добавленные записи установщик сохраняет в
`/etc/doqd/dnsmasq.saved`. Удаление возвращает `noresolv` и удаляет только
эти записи. Запись, которая была у dnsmasq до установки, остаётся.

**Другие серверы dnsmasq.** Если у dnsmasq уже есть серверы для всех имён
(их ставят https-dns-proxy, stubby, podkop и другие), установщик их не
удаляет. Он выводит предупреждение. `doqd status` показывает такие серверы
в строке `other DNS`. dnsmasq отправляет запрос на один из своих серверов,
поэтому часть запросов пройдёт мимо doqd. Чтобы все запросы шли через doqd,
удалите лишние серверы или прокси, который их добавил.

**AdGuard Home и другие DNS-серверы на порту 53.** Если dnsmasq не слушает
порт 53 (`dhcp.@dnsmasq[0].port` не 53), установщик предупредит об этом.
Тогда пропишите `127.0.0.1:5354` апстримом в этом DNS-сервере вручную.

**Обновление прошивки.** sysupgrade с сохранением настроек сохраняет
`/etc/config/dhcp`, где dnsmasq смотрит в doqd. Поэтому установщик
добавляет в `/lib/upgrade/keep.d/doqd` бинарник, init-скрипт, ссылки
автозапуска, конфиг и `/etc/doqd/`. После обновления doqd запускается сам.
Обновление без сохранения настроек удаляет doqd и возвращает стандартный
dnsmasq.

## Управление

```sh
doqd list                        # обычные и резервные upstream, живые пробы
doqd test quic://dns.quad9.net    # проверить сервер без изменения конфига
doqd add quic://dns.quad9.net     # проверить, добавить, перезапустить
doqd add --fallback quic://host   # добавить резервный сервер
doqd remove <номер-или-URL>
doqd status
doqd hosts status                # источник и число правил в конфиге
doqd hosts check                 # проверить выбранный hosts
doqd hosts export                # вывести встроенный снимок
doqd hosts reload                # попросить работающий демон перечитать файл
```

Флаг `-c /путь/doqd.conf` выбирает конфиг. `geo`, `geo reselect`, `--geo`,
`add-domain` и `remove-domain` удалены. Любой DoQ-сервер, включая ранее
использовавшийся как гео-DNS, можно явно добавить обычной командой `doqd add`.
Подмена hosts имеет приоритет перед его ответами.

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

Keenetic: `/opt/etc/doqd.conf`. OpenWRT: `/etc/doqd.conf`.

```text
listen 127.0.0.1:5354
hosts builtin
upstream quic://dns.quad9.net
upstream quic://p0.freedns.controld.com
upstream quic://dns.nextdns.io
cache_size 4096
min_ttl 60
max_ttl 86400
log info
```

На Keenetic установщик заменяет listen на LAN-адрес роутера. `fallback`
добавляет резервный DoQ-сервер, `bootstrap` задаёт внешний IP DNS для разрешения
имён upstream без петли через сам doqd. По умолчанию используются встроенные
bootstrap-адреса. Первая строка upstream/fallback заменяет стандартный список
серверов; нужен хотя бы один основной upstream.

### Свой hosts

На Keenetic:

```sh
doqd hosts export > /opt/etc/doqd.hosts
```

На OpenWRT выберите `/etc/doqd/hosts` (создайте каталог при необходимости).
Замените `hosts builtin` в конфиге на `hosts /полный/путь/к/файлу` и один раз
перезапустите doqd: SIGHUP перечитывает hosts, а не весь конфиг.

Формат файла:

```text
5.175.161.3 chatgpt.com releases.openai.com
5.175.161.40 chatgpt.com releases.openai.com
# Комментарии разрешены. Несколько строк дают несколько адресов.
```

Это пример формата: не заменяйте им полный список, если нужны остальные сервисы.
Пользовательский файл **заменяет** встроенный снимок, а не дополняет его.
После редактирования:

```sh
doqd hosts check
doqd hosts reload
```

Перезагрузка атомарна. Ошибочный файл не заменяет рабочие правила. Команда
reload подтверждает отправку запроса; результат применения смотрите в журнале
демона. Кэш обычного DNS не перекрывает локальные правила.

### Переход с 0.5.x

Используйте новый установщик, а не только замену бинарника: старые `geo` и
`geo-domain` новый демон не принимает. Установщик сохраняет резервную копию
изменяемого конфига, убирает стандартные гео-DNS и переносит пользовательские
гео-серверы в обычные upstream. Собственные `geo-domain` без IP автоматически
перенести невозможно: установщик остановится с объяснением, прежде чем менять
работающую установку. Перенесите необходимые имена с известными IP в hosts,
удалите старые директивы и повторите установку. Сторонняя веб-панель может
потребовать обновления для новых команд.

Для отката на 0.5.x восстановите **и старый бинарник, и соответствующий конфиг**
из `.pre-smartdns.*`: старый бинарник не понимает `hosts`. Затем перезапустите
службу. Миграция сохраняет конфиг, но не является транзакционным откатом всей
установки при сбое питания или записи на флеш.

## Проверка и диагностика

```sh
doqd -version
doqd hosts check
doqd list
doqd status
nslookup chatgpt.com
```

При наличии `dig` проверяйте doqd напрямую (на Keenetic подставьте LAN-IP):

```sh
dig @127.0.0.1 -p 5354 releases.openai.com A
dig @127.0.0.1 -p 5354 releases.openai.com AAAA
dig @127.0.0.1 -p 5354 releases.openai.com TYPE65
```

Для встроенного списка A содержит два IP прокси, AAAA и HTTPS — пустые успешные
ответы. Обычный домен, например `example.com`, должен разрешаться через upstream.
HTTP 403 или живой TLS-порт сами по себе не подтверждают исправность всего
сервиса: проверяйте фактическое открытие сайта и работу авторизованной сессии.
На OpenWRT журнал доступен через `logread -e doqd`.

## Удаление

Keenetic:

```sh
curl -fsSL https://raw.githubusercontent.com/necronicle/keenetic-doq/main/uninstall.sh | sh
```

Скрипт снимает регистрацию name-server, останавливает и удаляет демона.
Конфиг `/opt/etc/doqd.conf` остаётся.

OpenWRT:

```sh
wget -qO- https://raw.githubusercontent.com/necronicle/keenetic-doq/main/uninstall-openwrt.sh | sh
```

Скрипт сначала возвращает dnsmasq исходные `server` и `noresolv`. DNS
роутера при этом продолжает работать. Затем скрипт останавливает и удаляет
демона, init-скрипт и список sysupgrade. Конфиг `/etc/doqd.conf` остаётся.

## Сборка из исходников

Нужен Go 1.26 или новее.

```sh
sh scripts/build-all.sh dist          # все архитектуры в каталог dist
CGO_ENABLED=0 GOOS=linux GOARCH=arm64 go build -ldflags "-s -w" -o doqd ./cmd/doqd   # одна архитектура
```

Параметры каждой архитектуры (GOARM, GOMIPS и другие) — в
`scripts/build-all.sh`.

## Ограничения

- DoQ шифрует DNS между роутером и upstream. В LAN используются обычные DNS-запросы.
- Локальный hosts — снимок, не автоматический каталог всех доменов сервиса.
- Недоступный прокси не превращается в рабочий от смены DNS. Проверки и
  автоматического удаления адресов прокси в этой версии нет.
- Браузеры с собственным DNS, VPN и конкурирующие DNS-серверы могут обходить doqd.
- doqd не является VPN или сервисом проксирования трафика.

## Лицензия

[GPL-3.0](LICENSE)