DNS outbound: Add rules (matches qtype, domain, then action)

This commit is contained in:
Meow
2026-04-26 01:22:13 +08:00
parent 0f8ded11a1
commit b63b2a3b00
6 changed files with 447 additions and 60 deletions
+1 -1
View File
@@ -12,7 +12,7 @@
- Например, в `freedom` Outbound, если `domainStrategy` установлен в `UseIP`, запрос, исходящий из этого Outbound, сначала будет разрешен в IP через встроенный сервер, а затем произойдет подключение.
- Например, в `sockopt`, если `domainStrategy` установлен в `UseIP`, системное подключение, инициированное этим Outbound, сначала будет разрешено в IP встроенным сервером.
- Перехват DNS-трафика в режиме Transparent Proxy или работа в качестве рекурсивного DNS-сервера, открытого на порту 53.
- Перехват DNS-трафика в режиме TUN/Transparent Proxy через связку routing и DNS outbound, чтобы направлять DNS-трафик в этот модуль; либо работа в качестве рекурсивного DNS-сервера, открытого на порту 53.
::: tip TIP 1
DNS-запросы, отправляемые встроенным DNS-сервером, автоматически перенаправляются в соответствии с конфигурацией маршрутизации (Routing).
+149 -20
View File
@@ -1,54 +1,183 @@
# DNS
DNS — это исходящий протокол, который в основном используется для перехвата и пересылки DNS-запросов.
DNS — это исходящий протокол, который принимает DNS-запросы, переданные routing, и пересылает или обрабатывает их по правилам.
Этот исходящий протокол может принимать только DNS-трафик (включая запросы по протоколам UDP и TCP), другие типы трафика вызовут ошибку.
Этот outbound поддерживает только традиционный открытый DNS, то есть запросы по UDP и TCP; нестандартные для него варианты, такие как DoH, DoT и DoQ, к этому outbound не применимы. Типичные сценарии: TUN, прозрачный прокси или `dokodemo-door` принимают DNS-трафик, после чего routing направляет его в этот outbound.
При обработке DNS-запросов этот исходящий протокол пересылает запросы IP-адресов (то есть A и AAAA) на встроенный [DNS-сервер](../dns.md). Другие типы запросов см. в разделе `nonIPQuery` ниже.
По правилам он может пропускать запросы к целевому DNS-серверу, выполнять `hijack` во встроенный [DNS-сервер](../dns.md) для дальнейшей обработки, отбрасывать запросы или явно отказывать в них. Также он может изменять целевой адрес, порт и транспортный протокол.
## OutboundConfigurationObject
```json
{
"network": "tcp",
"network": "udp",
"address": "1.1.1.1",
"port": 53,
"userLevel": 0,
"nonIPQuery": "drop",
"blockTypes": []
"rules": [
{
"action": "reject",
"domain": ["domain:example.com"]
},
{
"action": "direct",
"qtype": 65,
"domain": ["geosite:geolocation-!cn"]
}
]
}
```
> `network`: "tcp" | "udp"
Пример выше только демонстрирует синтаксис полей. Полную конфигурацию см. в примере ниже.
Изменяет транспортный протокол DNS-трафика. Допустимые значения: `"tcp"` и `"udp"`. Если не указано, используется исходный транспортный протокол.
> `network`: [ "tcp" | "udp" ]
Изменяет транспортный протокол DNS-трафика. Допустимые значения: `"tcp"` и `"udp"`. Если не указано, исходный транспортный способ сохраняется.
> `address`: address
Изменяет адрес DNS-сервера. Если не указано, используется адрес, указанный в источнике.
Изменяет адрес DNS-сервера. Если не указано, сохраняется адрес, указанный источником.
> `port`: number
Изменяет порт DNS-сервера. Если не указано, используется порт, указанный в источнике.
Изменяет порт DNS-сервера. Если не указано, сохраняется порт, указанный источником.
> `userLevel`: number
Уровень пользователя. Соединение будет использовать [локальную политику](../policy.md#levelpolicyobject), соответствующую этому уровню пользователя.
Уровень пользователя. Соединения будут использовать [локальную политику](../policy.md#levelpolicyobject), соответствующую этому уровню пользователя.
Значение `userLevel` соответствует значению `level` в [policy](../policy.md#policyobject). Если не указано, по умолчанию используется значение `0`.
Значение `userLevel` соответствует значению `level` в [policy](../policy.md#policyobject). Если не указано, по умолчанию используется `0`.
> `nonIPQuery`: string
> `rules`: \[[RuleObject](#ruleobject)\]
Управляет запросами, не относящимися к IP-адресам (не A и AAAA). `"drop"` — отклонять, `"skip"` — не обрабатывать встроенным DNS-сервером, а пересылать на целевой сервер. В отличие от `"drop"`, это позволяет избежать ситуации, когда приложение тратит слишком много времени в ожидании ответа DNS до тайм-аута.
DNS-запросы сопоставляются с правилами по порядку; поддерживается детальное управление по `qtype` и `domain`.
Значение по умолчанию — `"reject"`.
Если ни одно правило не совпало, используется встроенное правило по умолчанию: запросы A и AAAA направляются во встроенный DNS-модуль, а запросы других типов явно отклоняются.
> `blockTypes`: array
## RuleObject
Массив целых чисел (`int`), определяющий типы DNS-запросов, которые необходимо блокировать. Например, `"blockTypes": [65,28]` означает блокировку типа 65 (HTTPS) и 28 (AAAA). Распространенный сценарий использования — блокировка типа 65 для предотвращения инициализации ECH браузерами.
```json
{
"action": "hijack",
"qtype": 1,
"domain": ["geosite:cn"]
}
```
Поскольку опция `nonIPQuery` по умолчанию отбрасывает (`drop`) все запросы, кроме A и AAAA, необходимо переключить её в режим `skip`, чтобы данная настройка могла вступить в силу (для типов, отличных от A/AAAA). Разумеется, можно не изменять `nonIPQuery` и использовать эту опцию исключительно для блокировки A или AAAA (отключение IPv4/IPv6), однако делать это **крайне не рекомендуется**. Для этих целей лучше использовать настройку `queryStrategy` во встроенном DNS.
Все условия сопоставления внутри правила объединяются логикой AND. Если условие не указано, ограничение по этому условию не применяется.
Внимание: если вы используете `blockTypes` только для блокировки A или AAAA, и при этом `nonIPQuery` установлен в значение `reject`, то блокировка также будет осуществляться путем возврата ответа DNS reject, а не простым отбрасыванием пакета.
> `action`: [ "direct" | "hijack" | "drop" | "reject" ]
## Примеры конфигурации DNS <Badge text="В РАЗРАБОТКЕ" type="warning"/>
Определяет действие при совпадении правила.
- `direct`: напрямую пропускает запрос к целевому DNS-серверу. Если на уровне outbound также настроены `network`, `address` или `port`, запрос пересылается к измененной цели.
- `hijack`: направляет запрос во встроенный [DNS-сервер](../dns.md) для дальнейшей обработки. Это можно использовать для дополнительного разделения трафика через конфигурацию встроенного DNS. В настоящее время поддерживаются только записи A и AAAA.
- `drop`: напрямую отбрасывает запрос и не возвращает ответ.
- `reject`: возвращает явный отказ. По сравнению с `drop`, это может предотвратить слишком долгое ожидание DNS timeout приложениями.
> `qtype`: number | string
Сопоставляет типы DNS-запросов. Есть три формы:
- `"a-b"`: `a` и `b` — целые числа. Это закрытый интервал; правило срабатывает, когда тип запроса попадает в этот диапазон.
- `a`: `a` — целое число. Правило срабатывает, когда тип запроса равен `a`.
- Комбинация двух форм выше через запятую. Например: `"1,3,23-24"`.
Распространенные номера типов можно посмотреть в [List of DNS record types](https://en.wikipedia.org/wiki/List_of_DNS_record_types).
Если не указано, сопоставляются все типы запросов.
> `domain`: [string]
Сопоставляет список доменов. Синтаксис такой же, как у [`domain` в правилах routing](../routing.md#ruleobject), например `domain:example.com`, `full:example.com`, `geosite:cn`. Если не указано, домены не ограничиваются.
## Пример конфигурации DNS
Следующий пример показывает практический сценарий: в прозрачном прокси inbound включает `sniffing` для разделения по домену / SNI, зарубежные домены идут через proxy, а остальной IP-трафик идет напрямую. При этом `dns-out` отклоняет HTTPS-записи для зарубежных доменов, чтобы уменьшить случаи, когда клиент получает ECH-конфигурацию и это влияет на разделение по открытому SNI; распространенные запросы вроде MX, TXT и SRV пересылаются указанному upstream, а запросы AAAA блокируются, потому что у proxy-сервера нет IPv6-среды.
```json
{
"inbounds": [
{
"tag": "all-in",
"port": 12345,
"protocol": "dokodemo-door",
"settings": {
"network": "tcp,udp",
"followRedirect": true
},
"sniffing": {
"enabled": true,
"destOverride": ["http", "tls", "quic"],
"routeOnly": true
},
"streamSettings": {
"sockopt": {
"tproxy": "tproxy"
}
}
}
],
"dns": {
"servers": ["https+local://1.1.1.1/dns-query"]
},
"outbounds": [
{
"tag": "direct",
"protocol": "freedom"
},
{
"tag": "proxy",
"protocol": "vless",
"settings": {
// Опущено...
}
},
{
"tag": "dns-out",
"protocol": "dns",
"settings": {
"network": "tcp",
"address": "1.1.1.1",
"port": 53,
"rules": [
{
"action": "reject",
"qtype": "28,65",
"domain": ["geosite:geolocation-!cn"]
},
{
"action": "direct",
"qtype": "15-16,33"
}
]
}
}
],
"routing": {
"domainStrategy": "AsIs",
"rules": [
{
"inboundTag": ["all-in"],
"network": "tcp,udp",
"port": "53",
"outboundTag": "dns-out"
},
{
"domain": ["geosite:geolocation-!cn"],
"outboundTag": "proxy"
}
]
}
}
```
Поведение примера:
- `all-in` включает `sniffing` и использует `routeOnly: true`, позволяя routing разделять трафик по определенным целевым доменам HTTP, TLS и QUIC, сохраняя исходный целевой адрес.
- Открытые DNS-запросы UDP/TCP от `all-in` к порту 53 направляются правилом routing в `dns-out`.
- Для обычного трафика `geosite:geolocation-!cn` идет через `proxy`; трафик, который не совпал с этим доменным правилом, автоматически использует первый outbound — `direct`.
- HTTPS-записи с `qtype` `65` для доменов из `geosite:geolocation-!cn` явно отклоняются, что может помочь при разделении по открытому SNI.
- AAAA-запросы с `qtype` `28` для доменов из `geosite:geolocation-!cn` явно отклоняются; это можно использовать для блокировки IPv6-резолвинга зарубежных доменов.
- Запросы с `qtype` `15-16,33` напрямую разрешаются и пересылаются на `1.1.1.1:53` согласно конфигурации outbound, используя TCP в качестве транспорта.
- Запросы, которые не совпали ни с одним правилом, попадают во встроенную fallback-логику: запросы A и AAAA направляются во встроенный DNS-модуль, а другие типы запросов явно отклоняются. Затем встроенный DNS обращается к upstream через `https+local://1.1.1.1/dns-query`, избегая зацикливания.