Compare commits

...

15 Commits

Author SHA1 Message Date
itdoginfo
433724f762 v0.3.48 Custom URL CRLF 2025-05-10 18:55:35 +03:00
itdoginfo
6378aa9910 Update 2025-05-10 16:15:53 +03:00
itdoginfo
68f5f123ca v0.3.47 Fix noresolv 1 2025-05-10 12:50:01 +03:00
itdoginfo
fae43d0471 v0.3.46 2025-05-08 19:24:08 +03:00
itdoginfo
9d6dc45fdb #99 Block mode 2025-05-08 19:23:45 +03:00
itdoginfo
9aa5a2d242 Fix site. #100 added ntpd 2025-05-08 10:14:58 +03:00
itdoginfo
63dc86fca4 v0.3.45 Update checker domain 2025-05-07 22:39:23 +03:00
itdoginfo
4d9cedaf4c Return upgrade command 2025-05-07 17:58:19 +03:00
itdoginfo
14e7cbae01 v0.3.44 2025-05-07 17:26:57 +03:00
itdoginfo
c9f610bb1e Change to ip.podkop.net. Fix log. Added restart 2025-05-07 17:24:32 +03:00
itdoginfo
19671c7f67 Stop btn. Change to ip.podkop.net 2025-05-07 17:23:20 +03:00
itdoginfo
6d1e4091e5 Detour 2025-05-07 00:22:04 +03:00
itdoginfo
96d661c49f Fixed default values ttl in comment 2025-05-03 18:52:57 +03:00
itdoginfo
da8dd06b34 Move doc to wiki 2025-05-03 18:12:00 +03:00
itdoginfo
2c1bcffb6d fix iptables 2025-05-03 18:11:49 +03:00
7 changed files with 211 additions and 228 deletions

162
README.md
View File

@@ -2,7 +2,7 @@
- Это альфа версия, которая находится в активной разработке. Из версии в версию что-то может меняться. - Это альфа версия, которая находится в активной разработке. Из версии в версию что-то может меняться.
- Основной функционал работает, но побочные штуки сейчас могут сбоить. - Основной функционал работает, но побочные штуки сейчас могут сбоить.
- При обновлении **обязательно** сбрасывайте кэш LuCI. - При обновлении **обязательно** [сбрасывайте кэш LuCI](https://podkop.net/docs/clearbrowsercache/).
- Также при обновлении всегда заходите в конфигурацию и проверяйте свои настройки. Конфигурация может измениться. - Также при обновлении всегда заходите в конфигурацию и проверяйте свои настройки. Конфигурация может измениться.
- Необходимо минимум 15МБ свободного места на роутере. Роутерами с флешками на 16МБ сразу мимо. - Необходимо минимум 15МБ свободного места на роутере. Роутерами с флешками на 16МБ сразу мимо.
- При старте программы редактируется конфиг Dnsmasq. - При старте программы редактируется конфиг Dnsmasq.
@@ -11,68 +11,22 @@
- Если у вас не что-то не работает, то следуюет сходить в телеграм чат, прочитать закрепы и выполнить что там написано.. - Если у вас не что-то не работает, то следуюет сходить в телеграм чат, прочитать закрепы и выполнить что там написано..
- Если у вас установлен Getdomains, его следует удалить. - Если у вас установлен Getdomains, его следует удалить.
# Удаление GetDomains скриптом # Документация
``` https://podkop.net/
sh <(wget -O - https://raw.githubusercontent.com/itdoginfo/domain-routing-openwrt/refs/heads/master/getdomains-uninstall.sh)
```
Оставляет туннели, зоны, forwarding. А также stubby и dnscrypt. Они не помешают. Конфиг sing-box будет перезаписан в podkop.
# Установка Podkop # Установка Podkop
Пакет работает на всех архитектурах. Полная информация в [документации](https://podkop.net/docs/install/)
Тестировался на **ванильной** OpenWrt 23.05 и OpenWrt 24.10.
На FriendlyWrt 23.05 присуствуют зависимости от iptables, которые ломают tproxy. Если у вас появляется warning про это в логах, следуйте инструкции по приведённой там ссылке.
Поддержки APK на данный момент нет. APK будет сделан после того как разгребу основное. Вкратце, достаточно одного скрипта:
## Автоматическая
``` ```
sh <(wget -O - https://raw.githubusercontent.com/itdoginfo/podkop/refs/heads/main/install.sh) sh <(wget -O - https://raw.githubusercontent.com/itdoginfo/podkop/refs/heads/main/install.sh)
``` ```
Скрипт также предложит выбрать, какой туннель будет использоваться. Для выбранного туннеля будут установлены нужные пакеты, а для Wireguard и AmneziaWG также будет предложена автоматическая настройка - прямо в консоли скрипт запросит данные конфига. Для AmneziaWG можно также выбрать вариант с использованием конфига обычного Wireguard и автоматической обфускацией до AmneziaWG. ## Обновление
Для AmneziaWG скрипт проверяет наличие пакетов под вашу платформу в [стороннем репозитории](https://github.com/Slava-Shchipunov/awg-openwrt/releases), так как в официальном репозитории OpenWRT они отсутствуют, и автоматически их устанавливает.
## Вручную
Сделать `opkg update`, чтоб установились зависимости.
Скачать пакеты `podkop_*.ipk` и `luci-app-podkop_*.ipk` из релиза. `opkg install` сначала первый, потом второй.
# Обновление
Та же самая команда, что для установки. Но с флагом **upgrade** сразу передёт к обновлению.
``` ```
sh <(wget -qO- https://raw.githubusercontent.com/itdoginfo/podkop/refs/heads/main/install.sh) --upgrade sh <(wget -qO- https://raw.githubusercontent.com/itdoginfo/podkop/refs/heads/main/install.sh) --upgrade
``` ```
# Удаление
```
opkg remove luci-i18n-podkop-ru luci-app-podkop podkop
```
# Использование
Конфиг: /etc/config/podkop
Luci: Services/podkop
## Режимы
### Proxy
Для VLESS и Shadowsocks. Другие протоколы тоже будут, кидайте в чат примеры строк без чувствительных данных.
В этом режиме просто копируйте строку в **Proxy String** и из неё автоматически настроится sing-box.
### VPN
Здесь у вас должен быть уже настроен WG/OpenVPN/OpenConnect etc, зона Zone и Forwarding не обязательны.
Просто выбрать интерфейс из списка.
## Настройка доменов и подсетей
**Community Lists** - Включить списки комьюнити
**Custom domains enable** - Добавить свои домены
**Custom subnets enable** - Добавить подсети или IP-адреса. Для подсетей задать маску.
# ToDo # ToDo
Этот раздел не означает задачи, которые нужно брать и делать. Это общий список хотелок. Если вы хотите помочь, пожалуйста, спросите сначала в телеграмме. Этот раздел не означает задачи, которые нужно брать и делать. Это общий список хотелок. Если вы хотите помочь, пожалуйста, спросите сначала в телеграмме.
@@ -83,109 +37,5 @@ Luci: Services/podkop
- [ ] IPv6. Только после наполнения Wiki - [ ] IPv6. Только после наполнения Wiki
Рефактор Рефактор
- [ ] Handle для sing-box
- [ ] Handle для dnsmasq
- [ ] Unit тесты (BATS) - [ ] Unit тесты (BATS)
- [ ] Интеграционые тесты бекенда (OpenWrt rootfs + BATS) - [ ] Интеграционые тесты бекенда (OpenWrt rootfs + BATS)
# Don't touch my dhcp
Нужно в первую очередь, чтоб использовать опцию `server`.
В случае если опция активна, podkop не трогает /etc/config/dhcp. И вам требуется самостоятельно указать следующие значения:
```
option noresolv '1'
option cachesize '0'
list server '127.0.0.42'
```
Без этого podkop работать не будет.
Если нужно до определённых доменов ходить через определённый DNS-сервер, то конфиг выглядит так
```
option noresolv '1'
option cachesize '0'
list server '/itdog.info/1.1.1.1'
list server '127.0.0.42'
```
В этом случае домен и все субдомены ресурса itdog.info будут резолвится через DNS-сервер 1.1.1.1
# Bad WAN
При использовании опции **Interface monitoring** необходимо рестартовать podkop, чтоб init.d подхватил это
```
service podkop restart
```
# Разработка
Есть два варианта:
- Просто поставить пакет на роутер или виртуалку и прям редактировать через SFTP (opkg install openssh-sftp-server)
- SDK, чтоб собирать пакеты
Для сборки пакетов нужен SDK, один из вариантов скачать прям файл и разархивировать
https://downloads.openwrt.org/releases/23.05.5/targets/x86/64/
Нужен файл с SDK в имени
```
wget https://downloads.openwrt.org/releases/23.05.5/targets/x86/64/openwrt-sdk-23.05.5-x86-64_gcc-12.3.0_musl.Linux-x86_64.tar.xz
tar xf openwrt-sdk-23.05.5-x86-64_gcc-12.3.0_musl.Linux-x86_64.tar.xz
mv openwrt-sdk-23.05.5-x86-64_gcc-12.3.0_musl.Linux-x86_64 SDK
```
Последнее для удобства.
Создаём директорию для пакета
```
mkdir package/utilites
```
Симлинк из репозитория
```
ln -s ~/podkop/podkop package/utilites/podkop
ln -s ~/podkop/luci-app-podkop package/luci-app-podkop
```
В первый раз для сборки luci-app необходимо обновить пакеты
```
./scripts/feeds update -a
```
Для make можно добавить флаг -j N, где N - количество ядер для сборки. Первый раз пройдёт быстрее.
При первом make выводится менюшка, можно просто save, exit и всё. Первый раз долго грузит зависимости.
Сборка пакета. Сами пакеты собираются быстро.
```
make package/podkop/{clean,compile} V=s
```
Также для luci
```
make package/luci-app-podkop/{clean,compile} V=s
```
.ipk лежат в `bin/packages/x86_64/base/`
## Примеры строк
https://github.com/itdoginfo/podkop/blob/main/String-example.md
## Ошибки
```
Makefile:17: /SDK/feeds/luci/luci.mk: No such file or directory
make[2]: *** No rule to make target '/SDK/feeds/luci/luci.mk'. Stop.
time: package/luci/luci-app-podkop/clean#0.00#0.00#0.00
ERROR: package/luci/luci-app-podkop failed to build.
make[1]: *** [package/Makefile:129: package/luci/luci-app-podkop/clean] Error 1
make[1]: Leaving directory '/SDK'
make: *** [/SDK/include/toplevel.mk:226: package/luci-app-podkop/clean] Error 2
```
Не загружены пакеты для luci
## make зависимости
https://openwrt.org/docs/guide-developer/toolchain/install-buildsystem
Ubuntu
```
sudo apt update
sudo apt install build-essential clang flex bison g++ gawk \
gcc-multilib g++-multilib gettext git libncurses-dev libssl-dev \
python3-distutils rsync unzip zlib1g-dev file wget
```

View File

@@ -457,7 +457,7 @@ check_system() {
done done
fi fi
if opkg list-installed | grep -qE "iptables|kmod-iptab"; then if opkg list-installed | grep -q "iptables-mod-extra"; then
printf "\033[31;1mFound incompatible iptables packages. If you're using FriendlyWrt: https://t.me/itdogchat/44512/181082\033[0m\n" printf "\033[31;1mFound incompatible iptables packages. If you're using FriendlyWrt: https://t.me/itdogchat/44512/181082\033[0m\n"
fi fi
} }

View File

@@ -1,7 +1,7 @@
include $(TOPDIR)/rules.mk include $(TOPDIR)/rules.mk
PKG_NAME:=luci-app-podkop PKG_NAME:=luci-app-podkop
PKG_VERSION:=0.3.43 PKG_VERSION:=0.3.48
PKG_RELEASE:=1 PKG_RELEASE:=1
LUCI_TITLE:=LuCI podkop app LUCI_TITLE:=LuCI podkop app

View File

@@ -87,6 +87,7 @@ function createConfigSection(section, map, network) {
o = s.taboption('basic', form.ListValue, 'mode', _('Connection Type'), _('Select between VPN and Proxy connection methods for traffic routing')); o = s.taboption('basic', form.ListValue, 'mode', _('Connection Type'), _('Select between VPN and Proxy connection methods for traffic routing'));
o.value('proxy', ('Proxy')); o.value('proxy', ('Proxy'));
o.value('vpn', ('VPN')); o.value('vpn', ('VPN'));
o.value('block', ('Block'));
o.ucisection = s.section; o.ucisection = s.section;
o = s.taboption('basic', form.ListValue, 'proxy_config_type', _('Configuration Type'), _('Select how to configure the proxy')); o = s.taboption('basic', form.ListValue, 'proxy_config_type', _('Configuration Type'), _('Select how to configure the proxy'));
@@ -638,7 +639,7 @@ const showConfigModal = async (command, title) => {
const controller = new AbortController(); const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), 10000); const timeoutId = setTimeout(() => controller.abort(), 10000);
const response = await fetch('https://fakeip.tech-domain.club/check', { signal: controller.signal }); const response = await fetch('https://fakeip.podkop.fyi/check', { signal: controller.signal });
const data = await response.json(); const data = await response.json();
clearTimeout(timeoutId); clearTimeout(timeoutId);
@@ -646,15 +647,15 @@ const showConfigModal = async (command, title) => {
if (data.fakeip === true) { if (data.fakeip === true) {
formattedOutput += '\n✅ ' + _('FakeIP is working in browser!') + '\n'; formattedOutput += '\n✅ ' + _('FakeIP is working in browser!') + '\n';
} else { } else {
formattedOutput += '❌ ' + _('FakeIP is not working in browser') + '\n'; formattedOutput += '\n❌ ' + _('FakeIP is not working in browser') + '\n';
formattedOutput += _('Check DNS server on current device (PC, phone)') + '\n'; formattedOutput += _('Check DNS server on current device (PC, phone)') + '\n';
formattedOutput += _('Its must be router!') + '\n'; formattedOutput += _('Its must be router!') + '\n';
} }
// Bypass check // Bypass check
const bypassResponse = await fetch('https://fakeip.tech-domain.club/check', { signal: controller.signal }); const bypassResponse = await fetch('https://fakeip.podkop.fyi/check', { signal: controller.signal });
const bypassData = await bypassResponse.json(); const bypassData = await bypassResponse.json();
const bypassResponse2 = await fetch('https://ip.tech-domain.club/check', { signal: controller.signal }); const bypassResponse2 = await fetch('https://ip.podkop.fyi/check', { signal: controller.signal });
const bypassData2 = await bypassResponse2.json(); const bypassData2 = await bypassResponse2.json();
formattedOutput += '━━━━━━━━━━━━━━━━━━━━━━━━━━━\n'; formattedOutput += '━━━━━━━━━━━━━━━━━━━━━━━━━━━\n';
@@ -734,7 +735,7 @@ const createStatusPanel = (title, status, buttons, extraData = {}) => {
title === 'Sing-box Status' ? title === 'Sing-box Status' ?
(status.running && !status.enabled ? '✔ running' : '✘ ' + status.status) : (status.running && !status.enabled ? '✔ running' : '✘ ' + status.status) :
title === 'Podkop Status' ? title === 'Podkop Status' ?
(status.enabled ? '✔ enabled' : '✘ disabled') : (status.enabled ? '✔ Autostart enabled' : '✘ Autostart disabled') :
(status.running ? '✔' : '✘') + ' ' + status.status (status.running ? '✔' : '✘') + ' ' + status.status
]) ])
].filter(Boolean); ].filter(Boolean);
@@ -754,14 +755,20 @@ const createStatusPanel = (title, status, buttons, extraData = {}) => {
action: 'restart', action: 'restart',
reload: true reload: true
}), }),
ButtonFactory.createActionButton({
label: 'Stop Podkop',
type: 'apply',
action: 'stop',
reload: true
}),
ButtonFactory.createInitActionButton({ ButtonFactory.createInitActionButton({
label: status.enabled ? 'Disable Podkop' : 'Enable Podkop', label: status.enabled ? 'Disable Autostart' : 'Enable Autostart',
type: status.enabled ? 'remove' : 'apply', type: status.enabled ? 'remove' : 'apply',
action: status.enabled ? 'disable' : 'enable', action: status.enabled ? 'disable' : 'enable',
reload: true reload: true
}), }),
ButtonFactory.createModalButton({ ButtonFactory.createModalButton({
label: _('Global check'), label: E('strong', _('Global check')),
command: 'global_check', command: 'global_check',
title: _('Click here for all the info') title: _('Click here for all the info')
}), }),
@@ -1140,7 +1147,7 @@ return view.extend({
return true; return true;
}; };
o = mainSection.taboption('additional', form.Value, 'dns_rewrite_ttl', _('DNS Rewrite TTL'), _('Time in seconds for DNS record caching (default: 600)')); o = mainSection.taboption('additional', form.Value, 'dns_rewrite_ttl', _('DNS Rewrite TTL'), _('Time in seconds for DNS record caching (default: 60)'));
o.default = '60'; o.default = '60';
o.rmempty = false; o.rmempty = false;
o.ucisection = 'main'; o.ucisection = 'main';
@@ -1212,6 +1219,11 @@ return view.extend({
o.rmempty = false; o.rmempty = false;
o.ucisection = 'main'; o.ucisection = 'main';
o = mainSection.taboption('additional', form.Flag, 'detour', _('Proxy download of lists'), _('Downloading all lists via main Proxy/VPN'));
o.default = '0';
o.rmempty = false;
o.ucisection = 'main';
// Extra IPs and exclusions (main section) // Extra IPs and exclusions (main section)
o = mainSection.taboption('basic', form.Flag, 'exclude_from_ip_enabled', _('IP for exclusion'), _('Specify local IP addresses that will never use the configured route')); o = mainSection.taboption('basic', form.Flag, 'exclude_from_ip_enabled', _('IP for exclusion'), _('Specify local IP addresses that will never use the configured route'));
o.default = '0'; o.default = '0';
@@ -1302,7 +1314,7 @@ return view.extend({
const timeoutId = setTimeout(() => controller.abort(), 10000); const timeoutId = setTimeout(() => controller.abort(), 10000);
try { try {
const response = await fetch('https://fakeip.tech-domain.club/check', { signal: controller.signal }); const response = await fetch('https://fakeip.podkop.fyi/check', { signal: controller.signal });
const data = await response.json(); const data = await response.json();
clearTimeout(timeoutId); clearTimeout(timeoutId);
@@ -1341,7 +1353,7 @@ return view.extend({
return resolve(createStatus('not_working', 'DNS not configured', 'ERROR')); return resolve(createStatus('not_working', 'DNS not configured', 'ERROR'));
} }
const result = await safeExec('nslookup', ['-timeout=2', 'fakeip.tech-domain.club', '127.0.0.42']); const result = await safeExec('nslookup', ['-timeout=2', 'fakeip.podkop.fyi', '127.0.0.42']);
if (result.stdout && result.stdout.includes('198.18')) { if (result.stdout && result.stdout.includes('198.18')) {
return resolve(createStatus('working', 'working on router', 'SUCCESS')); return resolve(createStatus('working', 'working on router', 'SUCCESS'));
@@ -1390,7 +1402,7 @@ return view.extend({
const controller1 = new AbortController(); const controller1 = new AbortController();
const timeoutId1 = setTimeout(() => controller1.abort(), 10000); const timeoutId1 = setTimeout(() => controller1.abort(), 10000);
const response1 = await fetch('https://fakeip.tech-domain.club/check', { signal: controller1.signal }); const response1 = await fetch('https://fakeip.podkop.fyi/check', { signal: controller1.signal });
const data1 = await response1.json(); const data1 = await response1.json();
clearTimeout(timeoutId1); clearTimeout(timeoutId1);
@@ -1405,7 +1417,7 @@ return view.extend({
const controller2 = new AbortController(); const controller2 = new AbortController();
const timeoutId2 = setTimeout(() => controller2.abort(), 10000); const timeoutId2 = setTimeout(() => controller2.abort(), 10000);
const response2 = await fetch('https://ip.tech-domain.club/check', { signal: controller2.signal }); const response2 = await fetch('https://ip.podkop.fyi/check', { signal: controller2.signal });
const data2 = await response2.json(); const data2 = await response2.json();
clearTimeout(timeoutId2); clearTimeout(timeoutId2);

View File

@@ -1,7 +1,7 @@
include $(TOPDIR)/rules.mk include $(TOPDIR)/rules.mk
PKG_NAME:=podkop PKG_NAME:=podkop
PKG_VERSION:=0.3.43 PKG_VERSION:=0.3.48
PKG_RELEASE:=1 PKG_RELEASE:=1
PKG_MAINTAINER:=ITDog <podkop@itdog.info> PKG_MAINTAINER:=ITDog <podkop@itdog.info>
@@ -15,7 +15,7 @@ define Package/podkop
DEPENDS:=+sing-box +curl +jq +kmod-nft-tproxy +coreutils-base64 DEPENDS:=+sing-box +curl +jq +kmod-nft-tproxy +coreutils-base64
CONFLICTS:=https-dns-proxy CONFLICTS:=https-dns-proxy
TITLE:=Domain routing app TITLE:=Domain routing app
URL:=https://itdog.info URL:=https://podkop.net
PKGARCH:=all PKGARCH:=all
endef endef

View File

@@ -39,3 +39,4 @@ config main 'main'
option mon_restart_ifaces '0' option mon_restart_ifaces '0'
#list restart_ifaces 'wan' #list restart_ifaces 'wan'
option ss_uot '0' option ss_uot '0'
option detour '0'

View File

@@ -20,7 +20,7 @@ SING_BOX_CONFIG="/etc/sing-box/config.json"
FAKEIP="198.18.0.0/15" 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" VALID_SERVICES="russia_inside russia_outside ukraine_inside geoblock block porn news anime youtube discord meta twitter hdrezka tiktok telegram cloudflare"
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" 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.tech-domain.club" TEST_DOMAIN="fakeip.podkop.fyi"
INTERFACES_LIST="" INTERFACES_LIST=""
SRC_INTERFACE="" SRC_INTERFACE=""
RESOLV_CONF="/etc/resolv.conf" RESOLV_CONF="/etc/resolv.conf"
@@ -29,11 +29,7 @@ CLOUDFLARE_OCTETS="103.21 103.22 103.31 104.16 104.17 104.18 104.19 104.20 104.2
log() { log() {
local message="$1" local message="$1"
local timestamp=$(date +"%Y-%m-%d %H:%M:%S") local timestamp=$(date +"%Y-%m-%d %H:%M:%S")
local CYAN="\033[0;36m"
local GREEN="\033[0;32m"
local RESET="\033[0m"
echo -e "${CYAN}[$timestamp]${RESET} ${GREEN}$message${RESET}"
logger -t "podkop" "$timestamp $message" logger -t "podkop" "$timestamp $message"
} }
@@ -71,7 +67,10 @@ start_main() {
config_foreach process_validate_service config_foreach process_validate_service
sleep 3 # 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
sleep 2
mkdir -p /tmp/podkop mkdir -p /tmp/podkop
@@ -132,6 +131,12 @@ start_main() {
sing_box_quic_reject sing_box_quic_reject
fi fi
config_get_bool detour "main" "detour" "0"
if [ "$detour" -eq 1 ]; then
log "Detour mixed enable"
detour_mixed
fi
sing_box_config_check sing_box_config_check
/etc/init.d/sing-box start /etc/init.d/sing-box start
#/etc/init.d/sing-box enable #/etc/init.d/sing-box enable
@@ -158,7 +163,7 @@ stop_main() {
if [ -f /var/run/podkop_list_update.pid ]; then if [ -f /var/run/podkop_list_update.pid ]; then
pid=$(cat /var/run/podkop_list_update.pid) pid=$(cat /var/run/podkop_list_update.pid)
if kill -0 "$pid"; then if kill -0 "$pid" 2>/dev/null; then
kill "$pid" 2>/dev/null kill "$pid" 2>/dev/null
log "Stopped list_update" log "Stopped list_update"
fi fi
@@ -204,6 +209,12 @@ reload() {
start_main start_main
} }
restart() {
log "Podkop restart"
stop
start
}
# Migrations and validation funcs # Migrations and validation funcs
migration() { migration() {
# list migrate # list migrate
@@ -367,8 +378,6 @@ save_dnsmasq_config() {
dnsmasq_add_resolver() { dnsmasq_add_resolver() {
log "Save dnsmasq config" log "Save dnsmasq config"
save_dnsmasq_config "dhcp.@dnsmasq[0].noresolv" "dhcp.@dnsmasq[0].podkop_noresolv"
save_dnsmasq_config "dhcp.@dnsmasq[0].cachesize" "dhcp.@dnsmasq[0].podkop_cachesize"
uci -q delete dhcp.@dnsmasq[0].podkop_server uci -q delete dhcp.@dnsmasq[0].podkop_server
for server in $(uci get dhcp.@dnsmasq[0].server 2>/dev/null); do for server in $(uci get dhcp.@dnsmasq[0].server 2>/dev/null); do
@@ -380,6 +389,9 @@ dnsmasq_add_resolver() {
fi fi
done done
save_dnsmasq_config "dhcp.@dnsmasq[0].noresolv" "dhcp.@dnsmasq[0].podkop_noresolv"
save_dnsmasq_config "dhcp.@dnsmasq[0].cachesize" "dhcp.@dnsmasq[0].podkop_cachesize"
log "Configure dnsmasq for sing-box" log "Configure dnsmasq for sing-box"
uci set dhcp.@dnsmasq[0].noresolv="1" uci set dhcp.@dnsmasq[0].noresolv="1"
uci set dhcp.@dnsmasq[0].cachesize="0" uci set dhcp.@dnsmasq[0].cachesize="0"
@@ -410,11 +422,11 @@ dnsmasq_restore() {
local server=$(uci get dhcp.@dnsmasq[0].server 2>/dev/null) local server=$(uci get dhcp.@dnsmasq[0].server 2>/dev/null)
if [[ "$server" == "127.0.0.42" ]]; then if [[ "$server" == "127.0.0.42" ]]; then
uci -q delete dhcp.@dnsmasq[0].server uci -q delete dhcp.@dnsmasq[0].server 2>/dev/null
for server in $(uci get dhcp.@dnsmasq[0].podkop_server 2>/dev/null); do for server in $(uci get dhcp.@dnsmasq[0].podkop_server 2>/dev/null); do
uci add_list dhcp.@dnsmasq[0].server="$server" uci add_list dhcp.@dnsmasq[0].server="$server"
done done
uci delete dhcp.@dnsmasq[0].podkop_server uci delete dhcp.@dnsmasq[0].podkop_server 2>/dev/null
fi fi
uci delete dhcp.@dnsmasq[0].podkop_cachesize uci delete dhcp.@dnsmasq[0].podkop_cachesize
@@ -564,10 +576,19 @@ list_update() {
fi fi
for i in $(seq 1 60); do for i in $(seq 1 60); do
if curl -s -m 3 https://github.com >/dev/null; then config_get_bool detour "main" "detour" "0"
log "GitHub is available" if [ "$detour" -eq 1 ]; then
break if http_proxy="http://127.0.0.1:4534" https_proxy="http://127.0.0.1:4534" curl -s -m 3 https://github.com >/dev/null; then
log "GitHub is available"
break
fi
else
if curl -s -m 3 https://github.com >/dev/null; then
log "GitHub is available"
break
fi
fi fi
log "GitHub is unavailable [$i/60]" log "GitHub is unavailable [$i/60]"
sleep 3 sleep 3
done done
@@ -776,7 +797,7 @@ sing_box_create_bypass_ruleset() {
"rules": [ "rules": [
{ {
"domain_suffix": [ "domain_suffix": [
"ip.tech-domain.club" "ip.podkop.fyi"
] ]
} }
] ]
@@ -922,6 +943,9 @@ sing_box_outdound() {
fi fi
fi fi
;; ;;
"block")
log "Block mode"
;;
*) *)
log "Requires *vpn* or *proxy* value" log "Requires *vpn* or *proxy* value"
return return
@@ -1012,7 +1036,7 @@ sing_box_config_outbound_json() {
if [ $? -eq 0 ]; then if [ $? -eq 0 ]; then
log "Outbound config updated successfully" log "Outbound config updated successfully"
else else
log "Error: Invalid JSON config generated" log "Error: Outbound invalid JSON config generated"
return 1 return 1
fi fi
} }
@@ -1073,9 +1097,9 @@ sing_box_config_shadowsocks() {
)' $SING_BOX_CONFIG >/tmp/sing-box-config-tmp.json && mv /tmp/sing-box-config-tmp.json $SING_BOX_CONFIG )' $SING_BOX_CONFIG >/tmp/sing-box-config-tmp.json && mv /tmp/sing-box-config-tmp.json $SING_BOX_CONFIG
if [ $? -eq 0 ]; then if [ $? -eq 0 ]; then
log "Config updated successfully" log "Config Shadowsocks updated successfully"
else else
log "Error: Invalid JSON config generated" log "Error: Shadowsocks invalid JSON config generated"
return 1 return 1
fi fi
} }
@@ -1200,10 +1224,10 @@ sing_box_config_vless() {
if [ $? -eq 0 ]; then if [ $? -eq 0 ]; then
log "Config created successfully" log "Config VLESS created successfully"
else else
log "Error: Invalid JSON config generated" log "[critical] Error: VLESS invalid JSON config generated"
return 1 exit 1
fi fi
} }
@@ -1356,6 +1380,7 @@ sing_box_ruleset_remote() {
local tag=$1 local tag=$1
local type=$2 local type=$2
local update_interval=$3 local update_interval=$3
local detour=$4
url="$SRS_MAIN_URL/$tag.srs" url="$SRS_MAIN_URL/$tag.srs"
@@ -1371,15 +1396,19 @@ sing_box_ruleset_remote() {
--arg type "$type" \ --arg type "$type" \
--arg url "$url" \ --arg url "$url" \
--arg update_interval "$update_interval" \ --arg update_interval "$update_interval" \
--arg detour "$detour" \
' '
.route.rule_set += [ .route.rule_set += [
{ (
"tag": $tag, {
"type": $type, "tag": $tag,
"format": "binary", "type": $type,
"url": $url, "format": "binary",
"update_interval": $update_interval "url": $url,
} "update_interval": $update_interval
} +
(if $detour == "1" then {"download_detour": "main"} else {} end)
)
]' "$SING_BOX_CONFIG" > /tmp/sing-box-config-tmp.json && mv /tmp/sing-box-config-tmp.json "$SING_BOX_CONFIG" ]' "$SING_BOX_CONFIG" > /tmp/sing-box-config-tmp.json && mv /tmp/sing-box-config-tmp.json "$SING_BOX_CONFIG"
log "Added new ruleset with tag $tag" log "Added new ruleset with tag $tag"
@@ -1414,7 +1443,13 @@ list_subnets_download() {
esac esac
local filename=$(basename "$URL") local filename=$(basename "$URL")
wget -O "/tmp/podkop/$filename" "$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 while IFS= read -r subnet; do
if [ "$service" = "discord" ]; then if [ "$service" = "discord" ]; then
@@ -1430,27 +1465,54 @@ sing_box_rules() {
local rule_set="$1" local rule_set="$1"
local outbound="$2" local outbound="$2"
# Check if there is an outbound rule for "tproxy-in" config_get mode "$section" "mode"
local rule_exists=$(jq -r '.route.rules[] | select(.outbound == "'"$outbound"'" and .inbound == ["tproxy-in"])' "$SING_BOX_CONFIG")
if [[ -n "$rule_exists" ]]; then if [[ "$mode" == "block" ]]; then
# If a rule for tproxy-in exists, add a new rule_set to the existing rule # Action reject
jq \ # Check if there is an rule with reject"
--arg rule_set "$rule_set" \ local rule_exists=$(jq -r '.route.rules[] | select(.inbound == ["tproxy-in"] and .action == "reject")' "$SING_BOX_CONFIG")
--arg outbound "$outbound" \
'(.route.rules[] | select(.outbound == $outbound and .inbound == ["tproxy-in"]) .rule_set) += [$rule_set]' \ if [[ -n "$rule_exists" ]]; then
"$SING_BOX_CONFIG" >/tmp/sing-box-config-tmp.json && mv /tmp/sing-box-config-tmp.json "$SING_BOX_CONFIG" # If a rule for rejectexists, add a new rule_set to the existing rule
jq \
--arg rule_set "$rule_set" \
'(.route.rules[] | select(.inbound == ["tproxy-in"] and .action == "reject") .rule_set) += [$rule_set]' \
"$SING_BOX_CONFIG" > /tmp/sing-box-config-tmp.json && mv /tmp/sing-box-config-tmp.json "$SING_BOX_CONFIG"
else
# If there is no rule for reject, create a new one with rule_set
jq \
--arg rule_set "$rule_set" \
'.route.rules += [{
"inbound": ["tproxy-in"],
"rule_set": [$rule_set],
"action": "reject"
}]' "$SING_BOX_CONFIG" > /tmp/sing-box-config-tmp.json && mv /tmp/sing-box-config-tmp.json "$SING_BOX_CONFIG"
fi
return
else else
# If there is no rule for tproxy-in, create a new one with rule_set # Action route
jq \ # Check if there is an outbound rule for "tproxy-in"
--arg rule_set "$rule_set" \ local rule_exists=$(jq -r '.route.rules[] | select(.outbound == "'"$outbound"'" and .inbound == ["tproxy-in"])' "$SING_BOX_CONFIG")
--arg outbound "$outbound" \
'.route.rules += [{ if [[ -n "$rule_exists" ]]; then
"inbound": ["tproxy-in"], # If a rule for tproxy-in exists, add a new rule_set to the existing rule
"rule_set": [$rule_set], jq \
"outbound": $outbound, --arg rule_set "$rule_set" \
"action": "route" --arg outbound "$outbound" \
}]' "$SING_BOX_CONFIG" >/tmp/sing-box-config-tmp.json && mv /tmp/sing-box-config-tmp.json "$SING_BOX_CONFIG" '(.route.rules[] | select(.outbound == $outbound and .inbound == ["tproxy-in"]) .rule_set) += [$rule_set]' \
"$SING_BOX_CONFIG" >/tmp/sing-box-config-tmp.json && mv /tmp/sing-box-config-tmp.json "$SING_BOX_CONFIG"
else
# If there is no rule for tproxy-in, create a new one with rule_set
jq \
--arg rule_set "$rule_set" \
--arg outbound "$outbound" \
'.route.rules += [{
"inbound": ["tproxy-in"],
"rule_set": [$rule_set],
"outbound": $outbound,
"action": "route"
}]' "$SING_BOX_CONFIG" >/tmp/sing-box-config-tmp.json && mv /tmp/sing-box-config-tmp.json "$SING_BOX_CONFIG"
fi
fi fi
} }
@@ -1476,8 +1538,9 @@ sing_box_quic_reject() {
process_remote_ruleset_srs() { process_remote_ruleset_srs() {
config_get_bool domain_list_enabled "$section" "domain_list_enabled" "0" config_get_bool domain_list_enabled "$section" "domain_list_enabled" "0"
if [ "$domain_list_enabled" -eq 1 ]; then if [ "$domain_list_enabled" -eq 1 ]; then
config_get_bool detour "main" "detour" "0"
log "Adding a srs list for $section" log "Adding a srs list for $section"
config_list_foreach "$section" domain_list "sing_box_ruleset_remote" "remote" "1d" config_list_foreach "$section" domain_list "sing_box_ruleset_remote" "remote" "1d" "$detour"
fi fi
} }
@@ -1543,13 +1606,24 @@ list_custom_url_domains_create() {
local section="$2" local section="$2"
local URL="$1" local URL="$1"
local filename=$(basename "$URL") local filename=$(basename "$URL")
local filepath="/tmp/podkop/${filename}"
wget -q -O "/tmp/podkop/${filename}" "$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 "$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 while IFS= read -r domain; do
log "From downloaded file: $domain" log "From downloaded file: $domain"
sing_box_ruleset_domains_json $domain $section sing_box_ruleset_domains_json $domain $section
done <"/tmp/podkop/$filename" done <"$filepath"
} }
process_domains_list_url() { process_domains_list_url() {
@@ -1583,14 +1657,25 @@ list_custom_url_subnets_create() {
local section="$2" local section="$2"
local URL="$1" local URL="$1"
local filename=$(basename "$URL") local filename=$(basename "$URL")
local filepath="/tmp/podkop/${filename}"
wget -q -O "/tmp/podkop/${filename}" "$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 "$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 subnet; do while IFS= read -r subnet; do
log "From local file: $subnet" log "From local file: $subnet"
sing_box_ruleset_subnets_json $subnet $section sing_box_ruleset_subnets_json $subnet $section
nft add element inet PodkopTable podkop_subnets { $subnet } nft add element inet PodkopTable podkop_subnets { $subnet }
done <"/tmp/podkop/$filename" done <"$filepath"
} }
process_subnet_for_section_remote() { process_subnet_for_section_remote() {
@@ -1643,6 +1728,31 @@ sing_box_rules_source_ip_cidr() {
fi fi
} }
detour_mixed() {
local section="main"
local port="4534"
local tag="detour"
log "Adding detour Socks5 for $section on port $port"
jq \
--arg tag "$tag" \
--arg port "$port" \
--arg section "$section" \
'.inbounds += [{
"tag": $tag,
"type": "mixed",
"listen": "127.0.0.1",
"listen_port": ($port|tonumber),
"set_system_proxy": false
}] |
.route.rules += [{
"inbound": [$tag],
"outbound": $section,
"action": "route"
}]' $SING_BOX_CONFIG >/tmp/sing-box-config-tmp.json && mv /tmp/sing-box-config-tmp.json $SING_BOX_CONFIG
}
## nftables ## nftables
list_all_traffic_from_ip() { list_all_traffic_from_ip() {
local ip="$1" local ip="$1"
@@ -1777,7 +1887,14 @@ check_github() {
for url in "$DOMAINS_RU_INSIDE" "$DOMAINS_RU_OUTSIDE" "$DOMAINS_UA" "$DOMAINS_YOUTUBE" \ for url in "$DOMAINS_RU_INSIDE" "$DOMAINS_RU_OUTSIDE" "$DOMAINS_UA" "$DOMAINS_YOUTUBE" \
"$SUBNETS_TWITTER" "$SUBNETS_META" "$SUBNETS_DISCORD"; do "$SUBNETS_TWITTER" "$SUBNETS_META" "$SUBNETS_DISCORD"; do
local list_name=$(basename "$url") local list_name=$(basename "$url")
wget -q -O /dev/null "$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 -q -O /dev/null "$url"
else
wget -q -O /dev/null "$url"
fi
if [ $? -eq 0 ]; then if [ $? -eq 0 ]; then
nolog "- $list_name: available" nolog "- $list_name: available"
else else
@@ -2310,6 +2427,9 @@ case "$1" in
reload) reload)
reload reload
;; ;;
restart)
restart
;;
main) main)
main main
;; ;;
@@ -2371,7 +2491,7 @@ case "$1" in
global_check global_check
;; ;;
*) *)
echo "Usage: $0 {start|stop|reload|enable|disable|main|list_update|check_proxy|check_nft|check_github|check_logs|check_sing_box_connections|check_sing_box_logs|check_fakeip|check_dnsmasq|show_config|show_version|show_sing_box_config|show_luci_version|show_sing_box_version|show_system_info|get_status|get_sing_box_status|check_dns_available|global_check}" echo "Usage: $0 {start|stop|reload|restart|enable|disable|main|list_update|check_proxy|check_nft|check_github|check_logs|check_sing_box_connections|check_sing_box_logs|check_fakeip|check_dnsmasq|show_config|show_version|show_sing_box_config|show_luci_version|show_sing_box_version|show_system_info|get_status|get_sing_box_status|check_dns_available|global_check}"
exit 1 exit 1
;; ;;
esac esac