mirror of
https://github.com/itdoginfo/podkop.git
synced 2025-12-08 12:36:50 +03:00
Compare commits
39 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
86038e2756 | ||
|
|
f3f5bca555 | ||
|
|
174f16bc76 | ||
|
|
7c63a35faa | ||
|
|
86a86df982 | ||
|
|
ac445bc227 | ||
|
|
4398e6885b | ||
|
|
9974b42cc2 | ||
|
|
8cd990f8a3 | ||
|
|
c509fd38c7 | ||
|
|
38991a803a | ||
|
|
29c34e31db | ||
|
|
a77e8fae7d | ||
|
|
6d83737336 | ||
|
|
84115e2f3b | ||
|
|
2dbdb9d2c1 | ||
|
|
88c6717152 | ||
|
|
b3986308ce | ||
|
|
a15c3cf171 | ||
|
|
4c91223f85 | ||
|
|
7cf7b1f626 | ||
|
|
a2536534f8 | ||
|
|
c49354fe38 | ||
|
|
6e01e036eb | ||
|
|
7484d0c203 | ||
|
|
0eb4ca4ea9 | ||
|
|
c2d95162b7 | ||
|
|
1fc2947fbc | ||
|
|
ea931d8463 | ||
|
|
e2f36c35d4 | ||
|
|
e8f8dcc5e7 | ||
|
|
1e2174bb80 | ||
|
|
85e515ef15 | ||
|
|
418cdc4366 | ||
|
|
25b0dcaad5 | ||
|
|
cc59e756dd | ||
|
|
210714c499 | ||
|
|
8b6c336584 | ||
|
|
5c543c1608 |
74
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
Normal file
74
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
Normal file
@@ -0,0 +1,74 @@
|
||||
---
|
||||
name: 🐛 Сообщение об ошибке
|
||||
description: Создавайте только, если проблема точно не на вашей стороне.
|
||||
title: "[BUG] "
|
||||
labels: ["bug"]
|
||||
assignees: []
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
Спасибо за создание отчета об ошибке!
|
||||
|
||||
Перед отправкой, пожалуйста:
|
||||
- Проверьте [существующие issues](https://github.com/itdoginfo/podkop/issues)
|
||||
- Просмотрите [документацию](https://podkop.net)
|
||||
|
||||
- type: textarea
|
||||
id: description
|
||||
attributes:
|
||||
label: 📝 Описание проблемы
|
||||
description: Четкое и краткое описание того, что не работает
|
||||
placeholder: Опишите проблему
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
id: reproduction
|
||||
attributes:
|
||||
label: Шаги для воспроизведения
|
||||
description: Шаги для воспроизведения проблемы. Если вы настраваете что-то по мануалу, приложите ссылку на него.
|
||||
placeholder: |
|
||||
1.
|
||||
2.
|
||||
3.
|
||||
4.
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
id: expected
|
||||
attributes:
|
||||
label: ✅ Ожидаемое поведение
|
||||
description: Четкое и краткое описание того, что должно было произойти
|
||||
placeholder: Опишите ожидаемое поведение
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
id: environment
|
||||
attributes:
|
||||
label: 🖥️ Информация о системе
|
||||
description: |
|
||||
Информация о вашей системе (заполните всё применимое)
|
||||
value: |
|
||||
- **OpenWrt версия**:
|
||||
- **Podkop версия**:
|
||||
- **Роутер модель**:
|
||||
- **Sing-box версия**:
|
||||
render: markdown
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
id: config
|
||||
attributes:
|
||||
label: ⚙️ Конфигурация
|
||||
description: |
|
||||
Релевантные части конфигурации (удалите чувствительную информацию!)
|
||||
placeholder: |
|
||||
Например:
|
||||
- Содержимое /etc/config/podkop
|
||||
- Конфигурация sing-box (если релевантно)
|
||||
- Дополнительные конфиги, которые потребуются wireless/network/dhcp и т.д.
|
||||
render: shell
|
||||
8
.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
8
.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
blank_issues_enabled: false
|
||||
contact_links:
|
||||
- name: 💬 Если у вас что-то не работает, прежде всего прочитайте README проекта
|
||||
url: https://github.com/itdoginfo/podkop
|
||||
about: README проекта
|
||||
- name: 📚 Если вы не нашли в README документацию, то вот ссылка на неё
|
||||
url: https://podkop.net
|
||||
about: Официальная документация PodKop
|
||||
68
.github/ISSUE_TEMPLATE/feature_request.yml
vendored
Normal file
68
.github/ISSUE_TEMPLATE/feature_request.yml
vendored
Normal file
@@ -0,0 +1,68 @@
|
||||
---
|
||||
name: ✨ Запрос новой функции
|
||||
description: Предложите новую функцию или улучшение для Podkop
|
||||
title: "[FEATURE] "
|
||||
labels: ["enhancement", "needs-discussion"]
|
||||
assignees: []
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
Спасибо за предложение новой функции!
|
||||
|
||||
Перед отправкой, пожалуйста:
|
||||
- Проверьте [существующие запросы](https://github.com/itdoginfo/podkop/issues?q=is%3Aissue+label%3Aenhancement)
|
||||
- Убедитесь, что функции не существует в [документации](https://podkop.net)
|
||||
|
||||
- type: textarea
|
||||
id: summary
|
||||
attributes:
|
||||
label: Краткое описание
|
||||
description: Краткое описание предлагаемой функции
|
||||
placeholder: В одном предложении опишите, что вы хотите добавить...
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
id: problem
|
||||
attributes:
|
||||
label: Проблема, которую решает
|
||||
description: |
|
||||
Описание проблемы или неудобства, которое решит эта функция
|
||||
placeholder: |
|
||||
Сейчас нет возможности [...]
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
id: solution
|
||||
attributes:
|
||||
label: 💡 Предлагаемое решение
|
||||
description: Четкое и краткое описание того, что вы хотите реализовать
|
||||
placeholder: |
|
||||
Я хочу, чтобы Podkop мог [...]
|
||||
Предлагаю добавить функцию, которая [...]
|
||||
Можно было бы улучшить [...] путем [...]
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
id: alternatives
|
||||
attributes:
|
||||
label: Workaround
|
||||
description: |
|
||||
Опишите альтернативные решения или функции, которые вы рассматривали
|
||||
Есть ли обходные пути, которые вы используете сейчас?
|
||||
placeholder: |
|
||||
Сейчас я решаю это проблему путем [...]
|
||||
Альтернативой могло бы быть [...]
|
||||
Пробовал использовать [...], но это не подходит потому что [...]
|
||||
|
||||
- type: textarea
|
||||
id: implementation
|
||||
attributes:
|
||||
label: Идеи реализации (опционально)
|
||||
description: |
|
||||
Если у вас есть идеи о том, как это можно реализовать, поделитесь ими. Помните про ограничения LuCI.
|
||||
placeholder: |
|
||||
Это можно реализовать с помощью [...]
|
||||
12
.github/pull_request_template.md
vendored
Normal file
12
.github/pull_request_template.md
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
# Описание изменений
|
||||
|
||||
Краткое описание ваших изменений и их цель.
|
||||
|
||||
## Что изменено
|
||||
|
||||
Детальное описание изменений:
|
||||
-
|
||||
-
|
||||
-
|
||||
|
||||
(Этим вы экономите время ревьювера)
|
||||
22
.github/workflows/build.yml
vendored
22
.github/workflows/build.yml
vendored
@@ -10,28 +10,22 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4.2.1
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Check version match
|
||||
- name: Extract version
|
||||
id: version
|
||||
run: |
|
||||
PODKOP_VERSION=$(grep '^PKG_VERSION:=' podkop/Makefile | cut -d '=' -f 2)
|
||||
LUCI_APP_PODKOP_VERSION=$(grep '^PKG_VERSION:=' luci-app-podkop/Makefile | cut -d '=' -f 2)
|
||||
|
||||
TAG_VERSION=${GITHUB_REF#refs/tags/v}
|
||||
|
||||
echo "Podkop version: $PODKOP_VERSION"
|
||||
echo "Luci-app-podkop version: $LUCI_APP_PODKOP_VERSION"
|
||||
echo "Tag version: $TAG_VERSION"
|
||||
|
||||
if [ "$PODKOP_VERSION" != "$TAG_VERSION" ] || [ "$LUCI_APP_PODKOP_VERSION" != "$TAG_VERSION" ]; then
|
||||
echo "Error: Version mismatch"
|
||||
exit 1
|
||||
fi
|
||||
VERSION=$(git describe --tags --exact-match 2>/dev/null || echo "dev_$(date +%d%m%Y)")
|
||||
echo "version=$VERSION" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Build and push
|
||||
uses: docker/build-push-action@v6.9.0
|
||||
with:
|
||||
context: .
|
||||
tags: podkop:ci
|
||||
build-args: |
|
||||
PKG_VERSION=${{ steps.version.outputs.version }}
|
||||
|
||||
- name: Create Docker container
|
||||
run: docker create --name podkop podkop:ci
|
||||
|
||||
1
.shellcheckrc
Normal file
1
.shellcheckrc
Normal file
@@ -0,0 +1 @@
|
||||
disable=SC3036,SC3010,SC3014,SC3015,SC3020,SC3003
|
||||
@@ -1,5 +1,8 @@
|
||||
FROM itdoginfo/openwrt-sdk:24.10.1
|
||||
|
||||
ARG PKG_VERSION
|
||||
ENV PKG_VERSION=${PKG_VERSION}
|
||||
|
||||
COPY ./podkop /builder/package/feeds/utilites/podkop
|
||||
COPY ./luci-app-podkop /builder/package/feeds/luci/luci-app-podkop
|
||||
|
||||
|
||||
18
README.md
18
README.md
@@ -2,7 +2,7 @@
|
||||
|
||||
- Это бета-версия, которая находится в активной разработке. Из версии в версию что-то может меняться.
|
||||
- При возникновении проблем, нужен технически грамотный фидбэк в чат.
|
||||
- При обновлении **обязательно** [сбрасывайте кэш LuCI](https://podkop.net/docs/clearbrowsercache/).
|
||||
- При обновлении **обязательно** [сбрасывайте кэш LuCI](https://podkop.net/docs/clear-browser-cache/).
|
||||
- Также при обновлении всегда заходите в конфигурацию и проверяйте свои настройки. Конфигурация может измениться.
|
||||
- Необходимо минимум 15МБ свободного места на роутере. Роутеры с флешками на 16МБ сразу мимо.
|
||||
- При старте программы редактируется конфиг Dnsmasq.
|
||||
@@ -32,20 +32,18 @@ sh <(wget -O - https://raw.githubusercontent.com/itdoginfo/podkop/refs/heads/mai
|
||||
- [ ] Возможно поменять структуру
|
||||
|
||||
## Списки
|
||||
- [ ] Speedtest
|
||||
- [x] Google AI
|
||||
- [x] Google PlayMarket. Здесь уточнить, что точно не работает через корректную настройку FakeIP, а не dnsmasq+nft.
|
||||
- [x] Hetzner ASN (AS24940)
|
||||
- [x] OVH ASN (AS16276)
|
||||
- [ ] CloudFront
|
||||
- [ ] DO
|
||||
- [ ] HODCA
|
||||
|
||||
## Будущее
|
||||
- [ ] После наполнения вики про туннели, убрать всё что связано с их установкой из скрипта. Только с AWG что-то решить, лучше чтоб был скрипт в сторонем репозитории.
|
||||
- [ ] Подписка. Здесь нужна реализация, чтоб для каждой секции помимо ручного выбора, был выбор фильтрации по тегу. Например, для main выбираем ключевые слова NL, DE, FI. А для extra секции фильтруем по RU. И создаётся outbound c urltest в которых перечислены outbound из фильтров.
|
||||
- [ ] [Подписка](https://github.com/itdoginfo/podkop/issues/118). Здесь нужна реализация, чтоб для каждой секции помимо ручного выбора, был выбор фильтрации по тегу. Например, для main выбираем ключевые слова NL, DE, FI. А для extra секции фильтруем по RU. И создаётся outbound c urltest в которых перечислены outbound из фильтров.
|
||||
- [ ] Опция, когда все запросы (с роутера в первую очередь), а не только br-lan идут в прокси. С этим связана #95. Требуется много переделать для nftables.
|
||||
- [ ] Весь трафик в Proxy\VPN. Вопрос, что делать с экстрасекциями в этом случае. FakeIP здесь скорее не нужен, а значит только main секция остаётся. Всё что касается fakeip проверок, придётся выключать в этом режиме.
|
||||
- [ ] Поддержка Source format. Нужна расшифровка в json и если присуствуют подсети, заносить их в custom subnet nftset.
|
||||
- [x] Поддержка Source format. Нужна расшифровка в json и если присуствуют подсети, заносить их в custom subnet nftset.
|
||||
- [ ] Переделывание функции формирования кастомных списков в JSON. Обрабатывать сразу скопом, а не по одному.
|
||||
- [ ] При успешном запуске переходит в фоновый режим и следит за состоянием sing-box. Если вдруг идёт exit 1, выполняется dnsmasq restore и снова следит за состоянием. Вопрос в том, как это искусcтвенно провернуть. Попробовать положить прокси и посмотреть, останется ли работать DNS в этом случае. И здесь, вероятно, можно обойтись триггером в init.d.
|
||||
- [ ] При успешном запуске переходит в фоновый режим и следит за состоянием sing-box. Если вдруг идёт exit 1, выполняется dnsmasq restore и снова следит за состоянием. Вопрос в том, как это искусcтвенно провернуть. Попробовать положить прокси и посмотреть, останется ли работать DNS в этом случае. И здесь, вероятно, можно обойтись триггером в init.d. [Issue](https://github.com/itdoginfo/podkop/issues/111)
|
||||
- [ ] Формирование конфига sing-box в /tmp
|
||||
- [ ] Галочка, которая режет доступ к doh серверам.
|
||||
- [ ] IPv6. Только после наполнения Wiki.
|
||||
|
||||
|
||||
@@ -57,6 +57,11 @@ vless://uuid@server:443?security=tls&sni=server&fp=chrome&type=ws&path=/websocke
|
||||
vless://33333@example.com:443/?type=ws&encryption=none&path=%2Fwebsocket&security=tls&sni=example.com&fp=chrome#vless-tls-ws-4
|
||||
```
|
||||
|
||||
7.
|
||||
```
|
||||
vless://id@sub.domain.example:443?type=ws&path=%2Fdir%2Fpath&host=sub.domain.example&security=tls#configname
|
||||
```
|
||||
|
||||
## No security
|
||||
```
|
||||
vless://8b60389a-7a01-4365-9244-c87f12bb98cf@example.com:443?type=tcp&security=none#vless-tls-no-encrypt
|
||||
|
||||
@@ -142,10 +142,6 @@ check_system() {
|
||||
esac
|
||||
done
|
||||
fi
|
||||
|
||||
if opkg list-installed | grep -q "iptables-mod-extra"; then
|
||||
msg "Found incompatible iptables packages. If you're using FriendlyWrt: https://t.me/itdogchat/44512/181082"
|
||||
fi
|
||||
}
|
||||
|
||||
sing_box() {
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
include $(TOPDIR)/rules.mk
|
||||
|
||||
PKG_NAME:=luci-app-podkop
|
||||
PKG_VERSION:=0.4.5
|
||||
|
||||
PKG_VERSION := $(if $(PKG_VERSION),$(PKG_VERSION),dev_$(shell date +%d%m%Y))
|
||||
|
||||
PKG_RELEASE:=1
|
||||
|
||||
LUCI_TITLE:=LuCI podkop app
|
||||
|
||||
@@ -198,6 +198,18 @@ function createAdditionalSection(mainSection, network) {
|
||||
return ['lan', 'loopback'].indexOf(value) === -1 && !value.startsWith('@');
|
||||
};
|
||||
|
||||
o = mainSection.taboption('additional', form.Value, 'procd_reload_delay', _('Interface Monitoring Delay'), _('Delay in milliseconds before reloading podkop after interface UP'));
|
||||
o.ucisection = 'main';
|
||||
o.depends('mon_restart_ifaces', '1');
|
||||
o.default = '2000';
|
||||
o.rmempty = false;
|
||||
o.validate = function (section_id, value) {
|
||||
if (!value) {
|
||||
return _('Delay value cannot be empty');
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
o = mainSection.taboption('additional', form.Flag, 'dont_touch_dhcp', _('Dont touch my DHCP!'), _('Podkop will not change the DHCP config'));
|
||||
o.default = '0';
|
||||
o.rmempty = false;
|
||||
|
||||
@@ -179,10 +179,6 @@ function createConfigSection(section, map, network) {
|
||||
if (!params.get('pbk')) return _('Invalid VLESS URL: missing pbk parameter for reality security');
|
||||
if (!params.get('fp')) return _('Invalid VLESS URL: missing fp parameter for reality security');
|
||||
}
|
||||
|
||||
if (security === 'tls' && type !== 'tcp' && !params.get('sni')) {
|
||||
return _('Invalid VLESS URL: missing sni parameter for tls security');
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
@@ -421,6 +417,9 @@ function createConfigSection(section, map, network) {
|
||||
const subnetRegex = /^(\d{1,3}\.){3}\d{1,3}(\/\d{1,2})?$/;
|
||||
if (!subnetRegex.test(value)) return _('Invalid format. Use format: X.X.X.X or X.X.X.X/Y');
|
||||
const [ip, cidr] = value.split('/');
|
||||
if (ip === "0.0.0.0") {
|
||||
return _('IP address 0.0.0.0 is not allowed');
|
||||
}
|
||||
const ipParts = ip.split('.');
|
||||
for (const part of ipParts) {
|
||||
const num = parseInt(part);
|
||||
|
||||
@@ -21,7 +21,10 @@ const ALLOWED_WITH_RUSSIA_INSIDE = [
|
||||
'google_ai',
|
||||
'google_play',
|
||||
'hetzner',
|
||||
'ovh'
|
||||
'ovh',
|
||||
'hodca',
|
||||
'digitalocean',
|
||||
'cloudfront'
|
||||
];
|
||||
|
||||
const DOMAIN_LIST_OPTIONS = {
|
||||
@@ -43,8 +46,11 @@ const DOMAIN_LIST_OPTIONS = {
|
||||
cloudflare: 'Cloudflare',
|
||||
google_ai: 'Google AI',
|
||||
google_play: 'Google Play',
|
||||
hodca: 'H.O.D.C.A',
|
||||
hetzner: 'Hetzner ASN',
|
||||
ovh: 'OVH ASN'
|
||||
ovh: 'OVH ASN',
|
||||
digitalocean: 'Digital Ocean ASN',
|
||||
cloudfront: 'CloudFront ASN'
|
||||
};
|
||||
|
||||
const UPDATE_INTERVAL_OPTIONS = {
|
||||
|
||||
@@ -232,6 +232,9 @@ msgstr "Неверный формат URL. URL должен начинаться
|
||||
msgid "Invalid format. Use format: X.X.X.X or X.X.X.X/Y"
|
||||
msgstr "Неверный формат. Используйте формат: X.X.X.X или X.X.X.X/Y"
|
||||
|
||||
msgid "IP address 0.0.0.0 is not allowed"
|
||||
msgstr "IP адрес не может быть 0.0.0.0"
|
||||
|
||||
msgid "IP address parts must be between 0 and 255"
|
||||
msgstr "Части IP-адреса должны быть между 0 и 255"
|
||||
|
||||
@@ -873,3 +876,24 @@ msgstr "Обновление списков не удалось"
|
||||
|
||||
msgid "Error: "
|
||||
msgstr "Ошибка: "
|
||||
|
||||
msgid "Interface monitoring"
|
||||
msgstr "Мониторинг интерфейсов"
|
||||
|
||||
msgid "Interface monitoring for bad WAN"
|
||||
msgstr "Мониторинг интерфейсов для плохого WAN"
|
||||
|
||||
msgid "Interface for monitoring"
|
||||
msgstr "Интерфейс для мониторинга"
|
||||
|
||||
msgid "Select the WAN interfaces to be monitored"
|
||||
msgstr "Выберите WAN интерфейсы для мониторинга"
|
||||
|
||||
msgid "Interface Monitoring Delay"
|
||||
msgstr "Задержка при мониторинге интерфейсов"
|
||||
|
||||
msgid "Delay in milliseconds before reloading podkop after interface UP"
|
||||
msgstr "Задержка в миллисекундах перед перезагрузкой podkop после поднятия интерфейса"
|
||||
|
||||
msgid "Delay value cannot be empty"
|
||||
msgstr "Значение не может быть пустым"
|
||||
@@ -232,6 +232,9 @@ msgstr ""
|
||||
msgid "Invalid format. Use format: X.X.X.X or X.X.X.X/Y"
|
||||
msgstr ""
|
||||
|
||||
msgid "IP address 0.0.0.0 is not allowed"
|
||||
msgstr ""
|
||||
|
||||
msgid "IP address parts must be between 0 and 255"
|
||||
msgstr ""
|
||||
|
||||
@@ -1227,3 +1230,24 @@ msgstr ""
|
||||
|
||||
msgid "Error: "
|
||||
msgstr ""
|
||||
|
||||
msgid "Interface monitoring"
|
||||
msgstr ""
|
||||
|
||||
msgid "Interface monitoring for bad WAN"
|
||||
msgstr ""
|
||||
|
||||
msgid "Interface for monitoring"
|
||||
msgstr ""
|
||||
|
||||
msgid "Select the WAN interfaces to be monitored"
|
||||
msgstr ""
|
||||
|
||||
msgid "Interface Monitoring Delay"
|
||||
msgstr ""
|
||||
|
||||
msgid "Delay in milliseconds before reloading podkop after interface UP"
|
||||
msgstr ""
|
||||
|
||||
msgid "Delay value cannot be empty"
|
||||
msgstr ""
|
||||
@@ -1,7 +1,9 @@
|
||||
include $(TOPDIR)/rules.mk
|
||||
|
||||
PKG_NAME:=podkop
|
||||
PKG_VERSION:=0.4.5
|
||||
|
||||
PKG_VERSION := $(if $(PKG_VERSION),$(PKG_VERSION),dev_$(shell date +%d%m%Y))
|
||||
|
||||
PKG_RELEASE:=1
|
||||
|
||||
PKG_MAINTAINER:=ITDog <podkop@itdog.info>
|
||||
@@ -13,7 +15,7 @@ define Package/podkop
|
||||
SECTION:=net
|
||||
CATEGORY:=Network
|
||||
DEPENDS:=+sing-box +curl +jq +kmod-nft-tproxy +coreutils-base64
|
||||
CONFLICTS:=https-dns-proxy
|
||||
CONFLICTS:=https-dns-proxy nextdns luci-app-passwall luci-app-passwall2
|
||||
TITLE:=Domain routing app
|
||||
URL:=https://podkop.net
|
||||
PKGARCH:=all
|
||||
|
||||
@@ -21,8 +21,6 @@ config main 'main'
|
||||
#list custom_download_subnets ''
|
||||
option all_traffic_from_ip_enabled '0'
|
||||
#list all_traffic_ip ''
|
||||
option delist_domains_enabled '0'
|
||||
#list delist_domains ''
|
||||
option exclude_from_ip_enabled '0'
|
||||
#list exclude_traffic_ip ''
|
||||
option yacd '0'
|
||||
@@ -35,11 +33,12 @@ config main 'main'
|
||||
option dns_server '8.8.8.8'
|
||||
option split_dns_enabled '1'
|
||||
option split_dns_type 'udp'
|
||||
option split_dns_server '8.8.8.8'
|
||||
option split_dns_server '1.1.1.1'
|
||||
option dns_rewrite_ttl '60'
|
||||
option cache_file '/tmp/cache.db'
|
||||
list iface 'br-lan'
|
||||
option mon_restart_ifaces '0'
|
||||
#list restart_ifaces 'wan'
|
||||
option procd_reload_delay '2000'
|
||||
option ss_uot '0'
|
||||
option detour '0'
|
||||
@@ -34,13 +34,16 @@ service_triggers() {
|
||||
|
||||
config_get mon_restart_ifaces "main" "mon_restart_ifaces"
|
||||
config_get restart_ifaces "main" "restart_ifaces"
|
||||
config_get procd_reload_delay "main" "procd_reload_delay" "2000"
|
||||
|
||||
PROCD_RELOAD_DELAY=$procd_reload_delay
|
||||
|
||||
procd_open_trigger
|
||||
procd_add_config_trigger "config.change" "$NAME" "$initscript" restart 'on_config_change'
|
||||
|
||||
if [ "$mon_restart_ifaces" = "1" ]; then
|
||||
for iface in $restart_ifaces; do
|
||||
procd_add_reload_interface_trigger $iface
|
||||
procd_add_interface_trigger "interface.*.up" "$iface" /etc/init.d/podkop reload
|
||||
done
|
||||
fi
|
||||
procd_close_trigger
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#!/bin/ash
|
||||
# shellcheck shell=dash
|
||||
|
||||
[ -r /lib/functions.sh ] && . /lib/functions.sh
|
||||
[ -r /lib/config/uci.sh ] && . /lib/config/uci.sh
|
||||
@@ -18,15 +19,19 @@ SUBNETS_TELERAM="${GITHUB_RAW_URL}/Subnets/IPv4/telegram.lst"
|
||||
SUBNETS_CLOUDFLARE="${GITHUB_RAW_URL}/Subnets/IPv4/cloudflare.lst"
|
||||
SUBNETS_HETZNER="${GITHUB_RAW_URL}/Subnets/IPv4/hetzner.lst"
|
||||
SUBNETS_OVH="${GITHUB_RAW_URL}/Subnets/IPv4/ovh.lst"
|
||||
SUBNETS_DIGITALOCEAN="${GITHUB_RAW_URL}/Subnets/IPv4/digitalocean.lst"
|
||||
SUBNETS_CLOUDFRONT="${GITHUB_RAW_URL}/Subnets/IPv4/cloudfront.lst"
|
||||
SING_BOX_CONFIG="/etc/sing-box/config.json"
|
||||
FAKEIP="198.18.0.0/15"
|
||||
VALID_SERVICES="russia_inside russia_outside ukraine_inside geoblock block porn news anime youtube discord meta twitter hdrezka tiktok telegram cloudflare google_ai google_play hetzner ovh"
|
||||
VALID_SERVICES="russia_inside russia_outside ukraine_inside geoblock block porn news anime youtube discord meta twitter hdrezka tiktok telegram cloudflare google_ai google_play hetzner ovh hodca digitalocean cloudfront"
|
||||
DNS_RESOLVERS="1.1.1.1 1.0.0.1 8.8.8.8 8.8.4.4 9.9.9.9 9.9.9.11 94.140.14.14 94.140.15.15 208.67.220.220 208.67.222.222 77.88.8.1 77.88.8.8"
|
||||
TEST_DOMAIN="fakeip.podkop.fyi"
|
||||
INTERFACES_LIST=""
|
||||
SRC_INTERFACE=""
|
||||
RESOLV_CONF="/etc/resolv.conf"
|
||||
CLOUDFLARE_OCTETS="103.21 103.22 103.31 104.16 104.17 104.18 104.19 104.20 104.21 104.22 104.23 104.24 104.25 104.26 104.27 104.28 108.162 131.0 141.101 162.158 162.159 172.64 172.65 172.66 172.67 172.68 172.69 172.70 172.71 173.245 188.114 190.93 197.234 198.41"
|
||||
|
||||
# Endpoints https://github.com/ampetelin/warp-endpoint-checker
|
||||
CLOUDFLARE_OCTETS="8.47 162.159 188.114"
|
||||
|
||||
# Color constants
|
||||
COLOR_CYAN="\033[0;36m"
|
||||
@@ -69,10 +74,6 @@ start_main() {
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if opkg list-installed | grep -q iptables-mod-extra; then
|
||||
log "[critical] Conflicting package detected: iptables-mod-extra"
|
||||
fi
|
||||
|
||||
if grep -qE 'doh_backup_noresolv|doh_backup_server|doh_server' /etc/config/dhcp; then
|
||||
log "[critical] Detected https-dns-proxy in dhcp config. Edit /etc/config/dhcp"
|
||||
fi
|
||||
@@ -81,6 +82,8 @@ start_main() {
|
||||
|
||||
config_foreach process_validate_service
|
||||
|
||||
br_netfilter_disable
|
||||
|
||||
# Sync time for DoH/DoT
|
||||
/usr/sbin/ntpd -q -p 194.190.168.1 -p 216.239.35.0 -p 216.239.35.4 -p 162.159.200.1 -p 162.159.200.123
|
||||
|
||||
@@ -109,7 +112,9 @@ start_main() {
|
||||
config_foreach sing_box_rule_preset
|
||||
config_foreach process_domains_list_local
|
||||
config_foreach process_subnet_for_section
|
||||
config_foreach process_remote_ruleset_srs
|
||||
config_foreach configure_community_lists
|
||||
config_foreach configure_remote_domain_lists
|
||||
config_foreach configure_remote_subnet_lists
|
||||
config_foreach process_all_traffic_for_section
|
||||
config_foreach add_cron_job
|
||||
|
||||
@@ -302,6 +307,14 @@ process_validate_service() {
|
||||
fi
|
||||
}
|
||||
|
||||
br_netfilter_disable() {
|
||||
if lsmod | grep -q br_netfilter && [ "$(sysctl -n net.bridge.bridge-nf-call-iptables 2>/dev/null)" = "1" ]; then
|
||||
log "br_netfilter enabled detected. Disabling"
|
||||
sysctl -w net.bridge.bridge-nf-call-iptables=0
|
||||
sysctl -w net.bridge.bridge-nf-call-ip6tables=0
|
||||
fi
|
||||
}
|
||||
|
||||
# Main funcs
|
||||
|
||||
route_table_rule_mark() {
|
||||
@@ -362,8 +375,26 @@ create_nft_table() {
|
||||
|
||||
nft_interfaces
|
||||
|
||||
log "Create localv4 set"
|
||||
nft add set inet $table localv4 { type ipv4_addr\; flags interval\; }
|
||||
nft add element inet $table localv4 { \
|
||||
0.0.0.0/8, \
|
||||
10.0.0.0/8, \
|
||||
127.0.0.0/8, \
|
||||
169.254.0.0/16, \
|
||||
172.16.0.0/12, \
|
||||
192.0.0.0/24, \
|
||||
192.0.2.0/24, \
|
||||
192.88.99.0/24, \
|
||||
192.168.0.0/16, \
|
||||
198.51.100.0/24, \
|
||||
203.0.113.0/24, \
|
||||
224.0.0.0/4, \
|
||||
240.0.0.0-255.255.255.255 }
|
||||
|
||||
log "Create nft rules"
|
||||
nft add chain inet $table mangle { type filter hook prerouting priority -150 \; policy accept \;}
|
||||
nft add chain inet $table mangle_output { type route hook output priority -150 \; policy accept\; }
|
||||
nft add chain inet $table proxy { type filter hook prerouting priority -100 \; policy accept \;}
|
||||
|
||||
nft add set inet $table podkop_subnets { type ipv4_addr\; flags interval\; auto-merge\; }
|
||||
@@ -375,6 +406,12 @@ create_nft_table() {
|
||||
|
||||
nft add rule inet $table proxy meta mark 0x105 meta l4proto tcp tproxy ip to :1602 counter
|
||||
nft add rule inet $table proxy meta mark 0x105 meta l4proto udp tproxy ip to :1602 counter
|
||||
|
||||
nft add rule inet $table mangle_output ip daddr @localv4 return
|
||||
nft add rule inet $table mangle_output ip daddr @podkop_subnets meta l4proto tcp meta mark set 0x00000105 counter
|
||||
nft add rule inet $table mangle_output ip daddr @podkop_subnets meta l4proto udp meta mark set 0x00000105 counter
|
||||
nft add rule inet $table mangle_output ip daddr 198.18.0.0/15 meta l4proto tcp meta mark set 0x00000105 counter
|
||||
nft add rule inet $table mangle_output ip daddr 198.18.0.0/15 meta l4proto udp meta mark set 0x00000105 counter
|
||||
}
|
||||
|
||||
save_dnsmasq_config() {
|
||||
@@ -419,8 +456,9 @@ dnsmasq_restore() {
|
||||
log "Removing configuration for dnsmasq"
|
||||
|
||||
local cachesize=$(uci get dhcp.@dnsmasq[0].podkop_cachesize 2>/dev/null)
|
||||
if [ -z "$cachesize" ]; then
|
||||
if [[ "$cachesize" == "unset" ]]; then
|
||||
log "dnsmasq revert: cachesize is unset"
|
||||
uci -q delete dhcp.@dnsmasq[0].cachesize
|
||||
else
|
||||
uci set dhcp.@dnsmasq[0].cachesize="$cachesize"
|
||||
fi
|
||||
@@ -565,7 +603,7 @@ prepare_custom_ruleset() {
|
||||
sing_box_rules $tag $section
|
||||
sing_box_dns_rule_fakeip_section $tag $tag
|
||||
|
||||
log "Added 'test' rule_set to sing-box config"
|
||||
log "Added $tag rule_set to sing-box config"
|
||||
fi
|
||||
}
|
||||
|
||||
@@ -613,9 +651,9 @@ list_update() {
|
||||
|
||||
echolog "📥 Downloading and processing lists..."
|
||||
|
||||
config_foreach process_remote_ruleset_subnet
|
||||
config_foreach process_domains_list_url
|
||||
config_foreach process_subnet_for_section_remote
|
||||
config_foreach import_community_subnet_lists
|
||||
config_foreach import_domains_from_remote_domain_lists
|
||||
config_foreach import_subnets_from_remote_subnet_lists
|
||||
|
||||
if [ $? -eq 0 ]; then
|
||||
echolog "✅ Lists update completed successfully"
|
||||
@@ -957,7 +995,7 @@ sing_box_dns_rule_fakeip() {
|
||||
|
||||
sing_box_dns_rule_fakeip_section() {
|
||||
local rule_set=$1
|
||||
echo $rule_set
|
||||
|
||||
log "Adding section to fakeip route rules in sing-box"
|
||||
|
||||
jq \
|
||||
@@ -1458,13 +1496,109 @@ sing_box_ruleset_subnets_json() {
|
||||
log "$subnet added to $section-custom-domains-subnets.json"
|
||||
}
|
||||
|
||||
#######################################
|
||||
# Adds a new remote ruleset to the sing-box configuration.
|
||||
# https://sing-box.sagernet.org/configuration/rule-set/#__tabbed_1_3
|
||||
#
|
||||
# Arguments:
|
||||
# tag: unique identifier for the ruleset.
|
||||
# format: format of the ruleset (e.g., "source" or "binary").
|
||||
# url: URL from which the ruleset can be fetched.
|
||||
# update_interval: update interval for the ruleset (e.g., "1d").
|
||||
# detour: flag indicating whether to use a download detour ("1" or "0").
|
||||
#
|
||||
# Outputs:
|
||||
# Modifies the sing-box configuration file by appending a new ruleset entry.
|
||||
#
|
||||
# Returns:
|
||||
# None. Always returns 0. If a ruleset with the same tag exists, it is skipped.
|
||||
#######################################
|
||||
sing_box_config_add_remote_ruleset() {
|
||||
local tag=$1
|
||||
local format=$2
|
||||
local url=$3
|
||||
local update_interval=$4
|
||||
local detour=$5
|
||||
|
||||
local tag_exists
|
||||
tag_exists=$(jq -r --arg tag "$tag" '
|
||||
.route.rule_set[]? | select(.tag == $tag) | .tag
|
||||
' "$SING_BOX_CONFIG")
|
||||
|
||||
if [[ -n "$tag_exists" ]]; then
|
||||
log "Ruleset with tag $tag already exists. Skipping addition."
|
||||
else
|
||||
jq \
|
||||
--arg tag "$tag" \
|
||||
--arg format "$format" \
|
||||
--arg url "$url" \
|
||||
--arg update_interval "$update_interval" \
|
||||
--arg detour "$detour" \
|
||||
'
|
||||
.route.rule_set += [
|
||||
(
|
||||
{
|
||||
"tag": $tag,
|
||||
"type": "remote",
|
||||
"format": $format,
|
||||
"url": $url,
|
||||
"update_interval": $update_interval
|
||||
} +
|
||||
(if $detour == "1" then {"download_detour": "main"} else {} end)
|
||||
)
|
||||
]' "$SING_BOX_CONFIG" | build_sing_box_config
|
||||
|
||||
log "Added new remote ruleset with tag $tag"
|
||||
fi
|
||||
}
|
||||
|
||||
#######################################
|
||||
# Adds a remote ruleset to the sing-box configuration and applies route and dns rules.
|
||||
#
|
||||
# Arguments:
|
||||
# url: remote ruleset URL.
|
||||
# section: configuration section where rules will be applied.
|
||||
# ruleset_content_type: Type of ruleset content (e.g., "domains" or "subnets").
|
||||
#
|
||||
# Returns:
|
||||
# 0 on success, non-zero if the file extension is unsupported.
|
||||
#######################################
|
||||
sing_box_add_remote_ruleset_and_rules() {
|
||||
local url="$1"
|
||||
local section="$2"
|
||||
local ruleset_content_type="$3"
|
||||
|
||||
local tag
|
||||
local format
|
||||
local update_interval='1d'
|
||||
local detour
|
||||
|
||||
case "$(get_url_file_extension "$url")" in
|
||||
json) format="source" ;;
|
||||
srs) format="binary" ;;
|
||||
*)
|
||||
log "Unsupported file extension: .$file_extension"
|
||||
return 1
|
||||
;;
|
||||
esac
|
||||
|
||||
tag=$(get_ruleset_tag_from_url "$url" "$section-remote-$ruleset_content_type")
|
||||
config_get_bool detour "main" "detour" "0"
|
||||
|
||||
sing_box_config_add_remote_ruleset "$tag" "$format" "$url" "$update_interval" "$detour"
|
||||
sing_box_rules "$tag" "$section"
|
||||
if [[ "$ruleset_content_type" = "domains" ]]; then
|
||||
sing_box_dns_rule_fakeip_section "$tag"
|
||||
fi
|
||||
}
|
||||
|
||||
process_domains_for_section() {
|
||||
local section="$1"
|
||||
|
||||
config_get custom_domains_list_type "$section" "custom_domains_list_type" "disabled"
|
||||
|
||||
if [ "$custom_domains_list_type" != "disabled" ]; then
|
||||
log "Adding a custom domains list for section $section"
|
||||
log "Adding a custom domains list for $section section"
|
||||
if [ "$custom_domains_list_type" = "dynamic" ]; then
|
||||
# Handle list domains from custom_domains
|
||||
config_list_foreach "$section" custom_domains "sing_box_ruleset_domains" "$section"
|
||||
@@ -1476,96 +1610,6 @@ process_domains_for_section() {
|
||||
fi
|
||||
}
|
||||
|
||||
sing_box_ruleset_remote() {
|
||||
local tag=$1
|
||||
local type=$2
|
||||
local update_interval=$3
|
||||
local detour=$4
|
||||
|
||||
url="$SRS_MAIN_URL/$tag.srs"
|
||||
|
||||
local tag_exists=$(jq -r --arg tag "$tag" '
|
||||
.route.rule_set[]? | select(.tag == $tag) | .tag
|
||||
' "$SING_BOX_CONFIG")
|
||||
|
||||
if [[ -n "$tag_exists" ]]; then
|
||||
log "Ruleset with tag $tag already exists. Skipping addition."
|
||||
else
|
||||
jq \
|
||||
--arg tag "$tag" \
|
||||
--arg type "$type" \
|
||||
--arg url "$url" \
|
||||
--arg update_interval "$update_interval" \
|
||||
--arg detour "$detour" \
|
||||
'
|
||||
.route.rule_set += [
|
||||
(
|
||||
{
|
||||
"tag": $tag,
|
||||
"type": $type,
|
||||
"format": "binary",
|
||||
"url": $url,
|
||||
"update_interval": $update_interval
|
||||
} +
|
||||
(if $detour == "1" then {"download_detour": "main"} else {} end)
|
||||
)
|
||||
]' "$SING_BOX_CONFIG" | build_sing_box_config
|
||||
|
||||
log "Added new ruleset with tag $tag"
|
||||
fi
|
||||
}
|
||||
|
||||
list_subnets_download() {
|
||||
local service="$1"
|
||||
local table="PodkopTable"
|
||||
|
||||
case "$service" in
|
||||
"twitter")
|
||||
URL=$SUBNETS_TWITTER
|
||||
;;
|
||||
"meta")
|
||||
URL=$SUBNETS_META
|
||||
;;
|
||||
"telegram")
|
||||
URL=$SUBNETS_TELERAM
|
||||
;;
|
||||
"cloudflare")
|
||||
URL=$SUBNETS_CLOUDFLARE
|
||||
;;
|
||||
"hetzner")
|
||||
URL=$SUBNETS_HETZNER
|
||||
;;
|
||||
"ovh")
|
||||
URL=$SUBNETS_OVH
|
||||
;;
|
||||
"discord")
|
||||
URL=$SUBNETS_DISCORD
|
||||
nft add set inet $table podkop_discord_subnets { type ipv4_addr\; flags interval\; auto-merge\; }
|
||||
nft add rule inet $table mangle iifname "$SRC_INTERFACE" ip daddr @podkop_discord_subnets udp dport { 50000-65535 } meta mark set 0x105 counter
|
||||
;;
|
||||
*)
|
||||
return
|
||||
;;
|
||||
esac
|
||||
|
||||
local filename=$(basename "$URL")
|
||||
|
||||
config_get_bool detour "main" "detour" "0"
|
||||
if [ "$detour" -eq 1 ]; then
|
||||
http_proxy="http://127.0.0.1:4534" https_proxy="http://127.0.0.1:4534" wget -O "/tmp/podkop/$filename" "$URL"
|
||||
else
|
||||
wget -O "/tmp/podkop/$filename" "$URL"
|
||||
fi
|
||||
|
||||
while IFS= read -r subnet; do
|
||||
if [ "$service" = "discord" ]; then
|
||||
nft add element inet $table podkop_discord_subnets { $subnet }
|
||||
else
|
||||
nft add element inet $table podkop_subnets { $subnet }
|
||||
fi
|
||||
done <"/tmp/podkop/$filename"
|
||||
}
|
||||
|
||||
sing_box_rules() {
|
||||
log "Configure rule in sing-box"
|
||||
local rule_set="$1"
|
||||
@@ -1641,29 +1685,11 @@ sing_box_quic_reject() {
|
||||
fi
|
||||
}
|
||||
|
||||
process_remote_ruleset_srs() {
|
||||
config_get_bool domain_list_enabled "$section" "domain_list_enabled" "0"
|
||||
if [ "$domain_list_enabled" -eq 1 ]; then
|
||||
config_get_bool detour "main" "detour" "0"
|
||||
log "Adding a srs list for $section"
|
||||
config_list_foreach "$section" domain_list "sing_box_ruleset_remote" "remote" "1d" "$detour"
|
||||
fi
|
||||
}
|
||||
|
||||
process_remote_ruleset_subnet() {
|
||||
config_get_bool domain_list_enabled "$section" "domain_list_enabled" "0"
|
||||
if [ "$domain_list_enabled" -eq 1 ]; then
|
||||
log "Adding a srs list for $section"
|
||||
config_list_foreach "$section" domain_list "list_subnets_download" "$section" "$domain_list"
|
||||
fi
|
||||
}
|
||||
|
||||
# TODO(ampetelin): function needs refactoring
|
||||
sing_box_rule_preset() {
|
||||
config_get custom_domains_list_type "$section" "custom_domains_list_type"
|
||||
config_get custom_subnets_list_enabled "$section" "custom_subnets_list_enabled"
|
||||
config_get custom_local_domains_list_enabled "$section" "custom_local_domains_list_enabled"
|
||||
# config_get custom_download_domains_list_enabled "$section" "custom_download_domains_list_enabled"
|
||||
# config_get custom_download_subnets_list_enabled "$section" "custom_download_subnets_list_enabled"
|
||||
|
||||
if [ "$custom_domains_list_type" != "disabled" ] || [ "$custom_subnets_list_enabled" != "disabled" ] ||
|
||||
[ "$custom_local_domains_list_enabled" = "1" ]; then
|
||||
@@ -1708,46 +1734,12 @@ process_domains_list_local() {
|
||||
fi
|
||||
}
|
||||
|
||||
list_custom_url_domains_create() {
|
||||
local section="$2"
|
||||
local URL="$1"
|
||||
local filename=$(basename "$URL")
|
||||
local filepath="/tmp/podkop/${filename}"
|
||||
|
||||
config_get_bool detour "main" "detour" "0"
|
||||
if [ "$detour" -eq 1 ]; then
|
||||
http_proxy="http://127.0.0.1:4534" https_proxy="http://127.0.0.1:4534" wget -O "$filepath" "$URL"
|
||||
else
|
||||
wget -O "$filepath" "$URL"
|
||||
fi
|
||||
|
||||
if grep -q $'\r' "$filepath"; then
|
||||
log "$filename has Windows line endings (CRLF). Converting to Unix (LF)"
|
||||
sed -i 's/\r$//' "$filepath"
|
||||
fi
|
||||
|
||||
while IFS= read -r domain; do
|
||||
log "From downloaded file: $domain"
|
||||
sing_box_ruleset_domains_json $domain $section
|
||||
done <"$filepath"
|
||||
}
|
||||
|
||||
process_domains_list_url() {
|
||||
local section="$1"
|
||||
|
||||
config_get custom_download_domains_list_enabled "$section" "custom_download_domains_list_enabled"
|
||||
if [ "$custom_download_domains_list_enabled" -eq 1 ]; then
|
||||
log "Adding a custom domains list from URL in $section"
|
||||
config_list_foreach "$section" "custom_download_domains" list_custom_url_domains_create "$section"
|
||||
fi
|
||||
}
|
||||
|
||||
process_subnet_for_section() {
|
||||
local section="$1"
|
||||
|
||||
config_get custom_subnets_list_enabled "$section" "custom_subnets_list_enabled" "disabled"
|
||||
if [ "$custom_subnets_list_enabled" != "disabled" ]; then
|
||||
log "Adding a custom subnet list for section $section"
|
||||
log "Adding a custom subnet list for $section section"
|
||||
if [ "$custom_subnets_list_enabled" = "dynamic" ]; then
|
||||
# Handle list domains from custom_domains
|
||||
config_list_foreach "$section" custom_subnets "sing_box_ruleset_subnets" "$section"
|
||||
@@ -1759,66 +1751,321 @@ process_subnet_for_section() {
|
||||
fi
|
||||
}
|
||||
|
||||
list_custom_url_subnets_create() {
|
||||
local section="$2"
|
||||
local URL="$1"
|
||||
local filename=$(basename "$URL")
|
||||
local filepath="/tmp/podkop/${filename}"
|
||||
|
||||
config_get_bool detour "main" "detour" "0"
|
||||
if [ "$detour" -eq 1 ]; then
|
||||
http_proxy="http://127.0.0.1:4534" https_proxy="http://127.0.0.1:4534" wget -O "$filepath" "$URL"
|
||||
else
|
||||
wget -O "$filepath" "$URL"
|
||||
configure_community_lists() {
|
||||
config_get_bool domain_list_enabled "$section" "domain_list_enabled" "0"
|
||||
if [ "$domain_list_enabled" -eq 1 ]; then
|
||||
log "Configuring community lists for $section section"
|
||||
config_list_foreach "$section" domain_list configure_community_list_handler
|
||||
fi
|
||||
|
||||
if grep -q $'\r' "$filepath"; then
|
||||
log "$filename has Windows line endings (CRLF). Converting to Unix (LF)"
|
||||
sed -i 's/\r$//' "$filepath"
|
||||
fi
|
||||
|
||||
while IFS= read -r subnet; do
|
||||
log "From local file: $subnet"
|
||||
sing_box_ruleset_subnets_json $subnet $section
|
||||
nft add element inet PodkopTable podkop_subnets { $subnet }
|
||||
done <"$filepath"
|
||||
}
|
||||
|
||||
process_subnet_for_section_remote() {
|
||||
configure_community_list_handler() {
|
||||
local tag=$1
|
||||
|
||||
local format="binary"
|
||||
local update_interval="1d"
|
||||
config_get_bool detour "main" "detour" "0"
|
||||
local url="$SRS_MAIN_URL/$tag.srs"
|
||||
|
||||
sing_box_config_add_remote_ruleset "$tag" "$format" "$url" "$update_interval" "$detour"
|
||||
}
|
||||
|
||||
configure_remote_domain_lists() {
|
||||
local section="$1"
|
||||
|
||||
config_get custom_download_subnets_list_enabled "$section" "custom_download_subnets_list_enabled" "disabled"
|
||||
if [ "$custom_download_subnets_list_enabled" -eq "1" ]; then
|
||||
log "Adding a custom SUBNET list from URL in $section"
|
||||
config_list_foreach "$section" "custom_download_subnets" list_custom_url_subnets_create "$section"
|
||||
config_get custom_download_domains_list_enabled "$section" custom_download_domains_list_enabled
|
||||
if [ "$custom_download_domains_list_enabled" -eq 1 ]; then
|
||||
log "Configuring remote domain lists for $section section"
|
||||
config_list_foreach "$section" custom_download_domains configure_remote_domain_list_handler "$section"
|
||||
fi
|
||||
}
|
||||
|
||||
configure_remote_domain_list_handler() {
|
||||
local url="$1"
|
||||
local section="$2"
|
||||
|
||||
log "Configuring remote domain list from URL: $url"
|
||||
|
||||
local file_extension
|
||||
file_extension=$(get_url_file_extension "$url")
|
||||
case "$file_extension" in
|
||||
json|srs)
|
||||
log "Detected file extension: .$file_extension → proceeding with processing"
|
||||
sing_box_add_remote_ruleset_and_rules "$url" "$section" "domains"
|
||||
;;
|
||||
*)
|
||||
log "Detected file extension: .$file_extension → no processing needed, managed on list_update"
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
configure_remote_subnet_lists() {
|
||||
local section="$1"
|
||||
|
||||
config_get custom_download_subnets_list_enabled "$section" custom_download_subnets_list_enabled disabled
|
||||
if [ "$custom_download_subnets_list_enabled" -eq "1" ]; then
|
||||
log "Configuring remote subnet lists for $section section"
|
||||
config_list_foreach "$section" custom_download_subnets configure_remote_subnet_list_handler "$section"
|
||||
fi
|
||||
}
|
||||
|
||||
configure_remote_subnet_list_handler() {
|
||||
local url="$1"
|
||||
local section="$2"
|
||||
|
||||
log "Configuring remote subnet list from URL: $url"
|
||||
|
||||
local file_extension
|
||||
file_extension=$(get_url_file_extension "$url")
|
||||
case "$file_extension" in
|
||||
json|srs)
|
||||
log "Detected file extension: .$file_extension → proceeding with processing"
|
||||
sing_box_add_remote_ruleset_and_rules "$url" "$section" "subnets"
|
||||
;;
|
||||
*)
|
||||
log "Detected file extension: .$file_extension → no processing needed, managed on list_update"
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
process_all_traffic_for_section() {
|
||||
local section="$1"
|
||||
|
||||
config_get all_traffic_from_ip_enabled "$section" "all_traffic_from_ip_enabled"
|
||||
if [ "$all_traffic_from_ip_enabled" -eq "1" ]; then
|
||||
log "Adding an IP to redirect all traffic"
|
||||
config_list_foreach $section all_traffic_ip list_all_traffic_from_ip
|
||||
config_list_foreach $section all_traffic_ip nft_list_all_traffic_from_ip
|
||||
config_list_foreach $section all_traffic_ip sing_box_rules_source_ip_cidr $all_traffic_ip $section
|
||||
fi
|
||||
}
|
||||
|
||||
import_community_subnet_lists() {
|
||||
config_get_bool domain_list_enabled "$section" "domain_list_enabled" "0"
|
||||
if [ "$domain_list_enabled" -eq 1 ]; then
|
||||
log "Importing community subnet lists for $section section"
|
||||
config_list_foreach "$section" domain_list import_community_service_subnet_list_handler
|
||||
fi
|
||||
}
|
||||
|
||||
import_community_service_subnet_list_handler() {
|
||||
local service="$1"
|
||||
local table="PodkopTable"
|
||||
|
||||
case "$service" in
|
||||
"twitter")
|
||||
URL=$SUBNETS_TWITTER
|
||||
;;
|
||||
"meta")
|
||||
URL=$SUBNETS_META
|
||||
;;
|
||||
"telegram")
|
||||
URL=$SUBNETS_TELERAM
|
||||
;;
|
||||
"cloudflare")
|
||||
URL=$SUBNETS_CLOUDFLARE
|
||||
;;
|
||||
"hetzner")
|
||||
URL=$SUBNETS_HETZNER
|
||||
;;
|
||||
"ovh")
|
||||
URL=$SUBNETS_OVH
|
||||
;;
|
||||
"digitalocean")
|
||||
URL=$SUBNETS_DIGITALOCEAN
|
||||
;;
|
||||
"cloudfront")
|
||||
URL=$SUBNETS_CLOUDFRONT
|
||||
;;
|
||||
"discord")
|
||||
URL=$SUBNETS_DISCORD
|
||||
nft add set inet $table podkop_discord_subnets { type ipv4_addr\; flags interval\; auto-merge\; }
|
||||
nft add rule inet $table mangle iifname "$SRC_INTERFACE" ip daddr @podkop_discord_subnets udp dport { 50000-65535 } meta mark set 0x105 counter
|
||||
;;
|
||||
*)
|
||||
return
|
||||
;;
|
||||
esac
|
||||
|
||||
local filename=$(basename "$URL")
|
||||
|
||||
config_get_bool detour "main" "detour" "0"
|
||||
if [ "$detour" -eq 1 ]; then
|
||||
http_proxy="http://127.0.0.1:4534" https_proxy="http://127.0.0.1:4534" wget -O "/tmp/podkop/$filename" "$URL"
|
||||
else
|
||||
wget -O "/tmp/podkop/$filename" "$URL"
|
||||
fi
|
||||
|
||||
while IFS= read -r subnet; do
|
||||
if [ "$service" = "discord" ]; then
|
||||
nft add element inet $table podkop_discord_subnets { $subnet }
|
||||
else
|
||||
nft add element inet $table podkop_subnets { $subnet }
|
||||
fi
|
||||
done <"/tmp/podkop/$filename"
|
||||
}
|
||||
|
||||
import_domains_from_remote_domain_lists() {
|
||||
local section="$1"
|
||||
|
||||
config_get custom_download_domains_list_enabled "$section" custom_download_domains_list_enabled
|
||||
if [ "$custom_download_domains_list_enabled" -eq 1 ]; then
|
||||
log "Importing domains from remote domain lists for $section section"
|
||||
config_list_foreach "$section" custom_download_domains import_domains_from_remote_domain_list_handler "$section"
|
||||
fi
|
||||
}
|
||||
|
||||
import_domains_from_remote_domain_list_handler() {
|
||||
local url="$1"
|
||||
local section="$2"
|
||||
|
||||
log "Importing domains from URL: $url"
|
||||
|
||||
local file_extension
|
||||
file_extension=$(get_url_file_extension "$url")
|
||||
case "$file_extension" in
|
||||
json|srs)
|
||||
log "Detected file extension: .$file_extension → no update needed, sing-box manages updates"
|
||||
;;
|
||||
*)
|
||||
log "Detected file extension: .$file_extension → proceeding with processing"
|
||||
import_domains_from_remote_lst_file "$url" "$section"
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
import_domains_from_remote_lst_file() {
|
||||
local url="$1"
|
||||
local section="$2"
|
||||
|
||||
local filename
|
||||
filename=$(basename "$url")
|
||||
local filepath="/tmp/podkop/${filename}"
|
||||
|
||||
download_to_tempfile "$url" "$filepath"
|
||||
|
||||
while IFS= read -r domain; do
|
||||
sing_box_ruleset_domains_json $domain $section
|
||||
done <"$filepath"
|
||||
|
||||
rm -f "$filepath"
|
||||
}
|
||||
|
||||
import_subnets_from_remote_subnet_lists() {
|
||||
local section="$1"
|
||||
|
||||
config_get custom_download_subnets_list_enabled "$section" custom_download_subnets_list_enabled disabled
|
||||
if [ "$custom_download_subnets_list_enabled" -eq "1" ]; then
|
||||
log "Importing subnets from remote subnet lists for $section section"
|
||||
config_list_foreach "$section" custom_download_subnets import_subnets_from_remote_subnet_list_handler "$section"
|
||||
fi
|
||||
}
|
||||
|
||||
import_subnets_from_remote_subnet_list_handler() {
|
||||
local url="$1"
|
||||
local section="$2"
|
||||
|
||||
log "Importing subnets from URL: $url"
|
||||
|
||||
local file_extension
|
||||
file_extension=$(get_url_file_extension "$url")
|
||||
case "$file_extension" in
|
||||
lst)
|
||||
log "Detected file extension: .$file_extension → proceeding with processing"
|
||||
import_subnets_from_remote_lst_file "$url" "$section"
|
||||
;;
|
||||
json)
|
||||
log "Detected file extension: .$file_extension → proceeding with processing"
|
||||
import_subnets_from_remote_json_file "$url"
|
||||
;;
|
||||
srs)
|
||||
log "Detected file extension: .$file_extension → proceeding with processing"
|
||||
import_subnets_from_remote_srs_file "$url"
|
||||
;;
|
||||
*)
|
||||
log "Detected file extension: .$file_extension → unsupported, skipping"
|
||||
return 1
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
import_subnets_from_remote_lst_file() {
|
||||
local url="$1"
|
||||
local section="$2"
|
||||
|
||||
local filename
|
||||
filename=$(basename "$url")
|
||||
local filepath="/tmp/podkop/${filename}"
|
||||
|
||||
download_to_tempfile "$url" "$filepath"
|
||||
|
||||
while IFS= read -r subnet; do
|
||||
sing_box_ruleset_subnets_json "$subnet" "$section"
|
||||
nft_add_podkop_subnet "$subnet"
|
||||
done <"$filepath"
|
||||
|
||||
rm -f "$filepath"
|
||||
}
|
||||
|
||||
import_subnets_from_remote_json_file() {
|
||||
local url="$1"
|
||||
|
||||
download_to_stream "$url" | jq -r '.rules[].ip_cidr[]?' | while read -r subnet; do
|
||||
nft_add_podkop_subnet "$subnet"
|
||||
done
|
||||
}
|
||||
|
||||
import_subnets_from_remote_srs_file() {
|
||||
local url="$1"
|
||||
|
||||
local filename
|
||||
filename=$(basename "$url")
|
||||
local binary_filepath="/tmp/podkop/${filename}"
|
||||
local json_filepath="/tmp/podkop/decompiled-${filename%%.*}.json"
|
||||
|
||||
download_to_tempfile "$url" "$binary_filepath"
|
||||
|
||||
if ! decompile_srs_file "$binary_filepath" "$json_filepath"; then
|
||||
return 1
|
||||
fi
|
||||
|
||||
jq -r '.rules[].ip_cidr[]' "$json_filepath" | while read -r subnet; do
|
||||
nft_add_podkop_subnet "$subnet"
|
||||
done
|
||||
|
||||
rm -f "$binary_filepath" "$json_filepath"
|
||||
}
|
||||
|
||||
# Decompiles a sing-box SRS binary file into a JSON ruleset file
|
||||
decompile_srs_file() {
|
||||
local binary_filepath="$1"
|
||||
local output_filepath="$2"
|
||||
|
||||
log "Decompiling $binary_filepath to $output_filepath"
|
||||
|
||||
if ! file_exists "$binary_filepath"; then
|
||||
log "File $binary_filepath not found"
|
||||
return 1
|
||||
fi
|
||||
|
||||
sing-box rule-set decompile "$binary_filepath" -o "$output_filepath"
|
||||
if [[ $? -ne 0 ]]; then
|
||||
log "Decompilation command failed for $binary_filepath"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
sing_box_rules_source_ip_cidr() {
|
||||
log "Configure source_ip_cidr rule in sing-box"
|
||||
local source_ip_cidr="$1"
|
||||
local outbound="$2"
|
||||
|
||||
local current_source_ip_cidr=$(jq -r '.route.rules[] | select(.outbound == "'"$outbound"'" and .action == "route" and (.rule_set | not))' $SING_BOX_CONFIG)
|
||||
|
||||
local current_source_ip_cidr=$(jq -r '.route.rules[] | select(.outbound == "'"$outbound"'" and .action == "route" and .source_ip_cidr and (.inbound // [] | contains(["tproxy-in"])))' $SING_BOX_CONFIG)
|
||||
|
||||
if [[ -n "$current_source_ip_cidr" ]]; then
|
||||
jq \
|
||||
--arg source_ip_cidr "$source_ip_cidr" \
|
||||
--arg outbound "$outbound" \
|
||||
'(.route.rules[] | select(.outbound == $outbound and .action == "route" and (.rule_set | not)) | .source_ip_cidr) += [$source_ip_cidr]' \
|
||||
"$SING_BOX_CONFIG" | build_sing_box_config
|
||||
'(.route.rules[] | select(.outbound == $outbound and .action == "route" and .source_ip_cidr and (.inbound // [] | contains(["tproxy-in"]))) | .source_ip_cidr) += [$source_ip_cidr]' "$SING_BOX_CONFIG" | build_sing_box_config
|
||||
else
|
||||
jq \
|
||||
--arg source_ip_cidr "$source_ip_cidr" \
|
||||
@@ -1860,31 +2107,27 @@ detour_mixed() {
|
||||
}
|
||||
|
||||
## nftables
|
||||
list_all_traffic_from_ip() {
|
||||
nft_list_all_traffic_from_ip() {
|
||||
local ip="$1"
|
||||
local table="PodkopTable"
|
||||
|
||||
if ! nft list chain inet $table mangle | grep -q "ip saddr $ip"; then
|
||||
nft add set inet $table localv4 { type ipv4_addr\; flags interval\; }
|
||||
nft add element inet $table localv4 { \
|
||||
0.0.0.0/8, \
|
||||
10.0.0.0/8, \
|
||||
127.0.0.0/8, \
|
||||
169.254.0.0/16, \
|
||||
172.16.0.0/12, \
|
||||
192.0.0.0/24, \
|
||||
192.0.2.0/24, \
|
||||
192.88.99.0/24, \
|
||||
192.168.0.0/16, \
|
||||
198.51.100.0/24, \
|
||||
203.0.113.0/24, \
|
||||
224.0.0.0/4, \
|
||||
240.0.0.0-255.255.255.255 }
|
||||
nft insert rule inet $table mangle iifname "$SRC_INTERFACE" ip saddr $ip meta l4proto { tcp, udp } meta mark set 0x105 counter
|
||||
nft insert rule inet $table mangle ip saddr $ip ip daddr @localv4 return
|
||||
fi
|
||||
}
|
||||
|
||||
# Adds an IPv4 subnet to nftables firewall set
|
||||
nft_add_podkop_subnet() {
|
||||
local subnet="$1"
|
||||
|
||||
if is_ipv4_cidr "$subnet"; then
|
||||
nft add element inet PodkopTable podkop_subnets { "$subnet" }
|
||||
else
|
||||
log "Invalid subnet format. $subnet is not a valid IPv4 CIDR"
|
||||
fi
|
||||
}
|
||||
|
||||
# Diagnotics
|
||||
check_proxy() {
|
||||
if ! command -v sing-box >/dev/null 2>&1; then
|
||||
@@ -2542,7 +2785,17 @@ global_check() {
|
||||
done
|
||||
fi
|
||||
|
||||
if [ -d "/etc/init.d/zapret" ]; then
|
||||
if uci show network | grep -q route_allowed_ips; then
|
||||
uci show network | grep route_allowed_ips | cut -d"'" -f2 | while read -r value; do
|
||||
if [ "$value" = "1" ]; then
|
||||
print_global "━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
print_global "⚠️ WG Route allowed IP enabled"
|
||||
continue
|
||||
fi
|
||||
done
|
||||
fi
|
||||
|
||||
if [ -f "/etc/init.d/zapret" ]; then
|
||||
print_global "━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
print_global "⚠️ Zapret detected"
|
||||
fi
|
||||
@@ -2597,6 +2850,87 @@ global_check() {
|
||||
fi
|
||||
}
|
||||
|
||||
# Download URL content directly
|
||||
download_to_stream() {
|
||||
local url="$1"
|
||||
|
||||
config_get_bool detour "main" "detour" "0"
|
||||
if [ "$detour" -eq 1 ]; then
|
||||
http_proxy="http://127.0.0.1:4534" https_proxy="http://127.0.0.1:4534" wget -qO- "$url" | sed 's/\r$//'
|
||||
else
|
||||
wget -qO- "$url" | sed 's/\r$//'
|
||||
fi
|
||||
}
|
||||
|
||||
# Download URL to temporary file
|
||||
download_to_tempfile() {
|
||||
local url="$1"
|
||||
local filepath="$2"
|
||||
|
||||
config_get_bool detour "main" "detour" "0"
|
||||
if [ "$detour" -eq 1 ]; then
|
||||
http_proxy="http://127.0.0.1:4534" https_proxy="http://127.0.0.1:4534" wget -O "$filepath" "$url"
|
||||
else
|
||||
wget -O "$filepath" "$url"
|
||||
fi
|
||||
|
||||
if grep -q $'\r' "$filepath"; then
|
||||
log "$filename has Windows line endings (CRLF). Converting to Unix (LF)"
|
||||
sed -i 's/\r$//' "$filepath"
|
||||
fi
|
||||
}
|
||||
|
||||
# helper function
|
||||
|
||||
# check if file exists
|
||||
file_exists() {
|
||||
local filepath="$1"
|
||||
|
||||
if [[ -f "$filepath" ]]; then
|
||||
return 0 # success
|
||||
else
|
||||
return 1 # failure
|
||||
fi
|
||||
}
|
||||
|
||||
# extracts file extension from URL
|
||||
get_url_file_extension() {
|
||||
local url="$1"
|
||||
|
||||
local file_extension="${url##*.}"
|
||||
|
||||
echo "$file_extension"
|
||||
}
|
||||
|
||||
# extracts file extension from URL
|
||||
get_ruleset_tag_from_url() {
|
||||
local url="$1"
|
||||
local prefix="${2:-}"
|
||||
local postfix="${3:-}"
|
||||
|
||||
local filename="${url##*/}"
|
||||
local basename="${filename%%.*}"
|
||||
|
||||
local tag="$basename"
|
||||
|
||||
if [ -n "$prefix" ]; then
|
||||
tag="${prefix}-${tag}"
|
||||
fi
|
||||
|
||||
if [ -n "$postfix" ]; then
|
||||
tag="${tag}-${postfix}"
|
||||
fi
|
||||
|
||||
echo "$tag"
|
||||
}
|
||||
|
||||
# check if string is valid IPv4 with CIDR mask
|
||||
is_ipv4_cidr() {
|
||||
local ip="$1"
|
||||
local regex="^((25[0-5]|(2[0-4]|1\d|[1-9]|)\d)\.?\b){4}(\/(3[0-2]|2[0-9]|1[0-9]|[0-9]))$"
|
||||
[[ $ip =~ $regex ]]
|
||||
}
|
||||
|
||||
show_help() {
|
||||
cat << EOF
|
||||
Usage: $0 COMMAND
|
||||
|
||||
Reference in New Issue
Block a user