Compare commits

..

No commits in common. "main" and "bins" have entirely different histories.
main ... bins

6 changed files with 58 additions and 119 deletions

View file

@ -27,10 +27,8 @@ cd nikovpn_openwrt
echo 'login:password' > /root/creds
```
Бинарники при установке качаются из [релизов репозитория]
(https://git.n1ko.dev/Niko/nikovpn_openwrt/releases/tag/bins) — каталог
`bin/` в клон не входит; если он есть рядом с install.sh — берётся из него.
f.n1ko.dev остаётся резервным источником.
Бинарники при установке качаются с f.n1ko.dev (каталог `bin/` ~150 MB
в репозиторий не входит; если он есть рядом с install.sh — берётся из него).
## Команды

View file

@ -18,9 +18,8 @@
# Требования:
# - креды "login:password" (без кавычек): файл /root/creds
# на роутере или переменная окружения NIKOVPN_CREDS
# - даунлоадер: curl, wget или uclient-fetch
# - доступ в интернет (бинарники — из релизов git.n1ko.dev,
# резервом f.n1ko.dev; если рядом с install.sh есть bin/ — из него)
# - доступ в интернет (скрипт тянет всё только с f.n1ko.dev и vpnd.n1ko.dev;
# если рядом с install.sh есть каталог bin/ — бинарники берутся из него)
#
# Скрипт сам определяет архитектуру (openwrt_release/apk/opkg/uname),
# выбирает лучший протокол и сервер (Hysteria2 > TUIC > Naive > VLESS > SS)
@ -28,7 +27,6 @@
# ============================================================================
BASE_URL="https://f.n1ko.dev/nikovpn_openwrt"
REL_URL="https://git.n1ko.dev/Niko/nikovpn_openwrt/releases/download/bins"
API_URL="https://vpnd.n1ko.dev/api/v1"
WORK_DIR="/etc/nikovpn"
CREDS_FILE="/root/creds"
@ -54,8 +52,8 @@ need_root() { [ "$(id -u)" = "0" ] || die "нужны права root"; }
# --- downloader ---
DL=""
for t in curl wget uclient-fetch; do have "$t" && DL="$t" && break; done
[ -n "$DL" ] || die "нужен curl, wget или uclient-fetch"
for t in curl wget; do have "$t" && DL="$t" && break; done
[ -n "$DL" ] || die "нужен curl или wget"
dl() { # $1=url $2=out ; резерв на http:// для f.n1ko.dev
local url="$1" out="$2" ok=""
@ -63,47 +61,14 @@ dl() { # $1=url $2=out ; резерв на http:// для f.n1ko.dev
curl -fsSL --max-time 120 -o "$out" "$url" && ok=1
[ -z "$ok" ] && case "$url" in https://f.n1ko.dev/*)
curl -fsSL --max-time 120 -o "$out" "http://${url#https://}" && ok=1 ;; esac
elif [ "$DL" = wget ]; then
else
wget -q -T 120 -O "$out" "$url" && ok=1
[ -z "$ok" ] && case "$url" in https://f.n1ko.dev/*)
wget -q -T 120 -O "$out" "http://${url#https://}" && ok=1 ;; esac
else
uclient-fetch -q --timeout=120 -O "$out" "$url" && ok=1
[ -z "$ok" ] && case "$url" in https://f.n1ko.dev/*)
uclient-fetch -q --timeout=120 -O "$out" "http://${url#https://}" && ok=1 ;; esac
fi
[ -n "$ok" ]
}
pipe_dl() { # $1=url -> поток в stdout (для больших файлов: без staging в tmpfs,
# /tmp на OpenWRT — это RAM, крупный бинарник туда класть нельзя)
if [ "$DL" = curl ]; then
curl -fsSL --max-time 600 "$1"
elif [ "$DL" = wget ]; then
wget -q -T 600 -O - "$1"
else
uclient-fetch -q --timeout=600 -O - "$1"
fi
}
fetch_gz() { # $1=имя-файла $2=dest ; источники: локальный bin/ -> релиз -> f.n1ko.dev
local f="$1" out="$2" ok=0
if [ -f "$REPO_DIR/bin/$f" ]; then
gzip -dc "$REPO_DIR/bin/$f" > "$out" 2>/dev/null && [ -s "$out" ] && ok=1
fi
if [ $ok != 1 ]; then
log "Качаю $f из релизов git.n1ko.dev ..."
pipe_dl "$REL_URL/$f" | gzip -dc > "$out" 2>/dev/null
[ -s "$out" ] && ok=1
fi
if [ $ok != 1 ]; then
log "Качаю $f с f.n1ko.dev ..."
pipe_dl "$BASE_URL/bin/$f" | gzip -dc > "$out" 2>/dev/null
[ -s "$out" ] && ok=1
fi
[ $ok = 1 ]
}
# --- API (HTTP Basic auth) ---
API_AUTH=""; API_AUTH_V=""
api_init_auth() {
@ -133,13 +98,11 @@ api_get() {
}
read_creds() {
# ВАЖНО: не использовать tr '[:space:]' — старые busybox не знают
# POSIX-классы и вырезают литеральные буквы из кредов
if [ -n "$NIKOVPN_CREDS" ]; then
CRED=$(printf '%s' "$NIKOVPN_CREDS" | tr -d ' \t\r\n')
CRED=$(printf '%s' "$NIKOVPN_CREDS" | tr -d '[:space:]')
else
[ -f "$CREDS_FILE" ] || die "нет кредов: запишите 'login:password' в $CREDS_FILE или задайте env NIKOVPN_CREDS"
CRED=$(grep -v '^[[:space:]]*#' "$CREDS_FILE" 2>/dev/null | grep -v '^[[:space:]]*$' | head -1 | tr -d ' \t\r\n')
CRED=$(grep -v '^[[:space:]]*#' "$CREDS_FILE" 2>/dev/null | grep -v '^[[:space:]]*$' | head -1 | tr -d '[:space:]')
[ -n "$CRED" ] || die "$CREDS_FILE пуст — запишите 'login:password'"
fi
case "$CRED" in *:*) ;; *) die "в кредах нет ':' — формат login:password" ;; esac
@ -496,16 +459,26 @@ install_bins() {
if [ "$skip_sb" != 1 ]; then
log "Получаю sing-box ($SB_ARCH) ..."
fetch_gz "sing-box-$SB_ARCH.gz" "$SB_PATH" || die "не удалось скачать sing-box"
chmod +x "$SB_PATH"
"$SB_PATH" version >/dev/null 2>&1 || die "sing-box не запускается на этой системе (архитектура?)"
dl "$BASE_URL/bin/sing-box-$SB_ARCH.gz" "$TMP/sing-box.gz" || \
cp "$REPO_DIR/bin/sing-box-$SB_ARCH.gz" "$TMP/sing-box.gz" 2>/dev/null
[ -s "$TMP/sing-box.gz" ] || die "не удалось скачать sing-box"
gzip -dc "$TMP/sing-box.gz" > "$TMP/sing-box" 2>/dev/null || die "повреждённый архив sing-box"
chmod +x "$TMP/sing-box"
"$TMP/sing-box" version >/dev/null 2>&1 || die "sing-box не запускается на этой системе (архитектура?)"
mv "$TMP/sing-box" "$SB_PATH"
rm -f "$TMP/sing-box.gz"
fi
if [ "$TUNNEL" = "ipt2socks" ] && [ "$skip_ipt" != 1 ]; then
log "Получаю ipt2socks ($IPT_ARCH) ..."
fetch_gz "ipt2socks-$IPT_ARCH.gz" "$IPT_PATH" || die "не удалось скачать ipt2socks"
chmod +x "$IPT_PATH"
"$IPT_PATH" -V >/dev/null 2>&1 || die "ipt2socks не запускается на этой системе (архитектура?)"
dl "$BASE_URL/bin/ipt2socks-$IPT_ARCH.gz" "$TMP/ipt2socks.gz" || \
cp "$REPO_DIR/bin/ipt2socks-$IPT_ARCH.gz" "$TMP/ipt2socks.gz" 2>/dev/null
[ -s "$TMP/ipt2socks.gz" ] || die "не удалось скачать ipt2socks"
gzip -dc "$TMP/ipt2socks.gz" > "$TMP/ipt2socks" 2>/dev/null || die "повреждённый архив ipt2socks"
chmod +x "$TMP/ipt2socks"
"$TMP/ipt2socks" -V >/dev/null 2>&1 || die "ipt2socks не запускается на этой системе (архитектура?)"
mv "$TMP/ipt2socks" "$IPT_PATH"
rm -f "$TMP/ipt2socks.gz"
fi
}
@ -767,10 +740,9 @@ status() {
echo "Сервер: $HOST:$PORT"
echo "Архитектура: $ARCH (sing-box $SB_VER, ipt2socks $IPT_VER)"
echo "Режим: $MODE / $TUNNEL / $FW"
# pgrep без -x: busybox с argv[0]=/usr/bin/sing-box не матчит -x
pgrep sing-box >/dev/null 2>&1 && echo "sing-box: RUNNING" || echo "sing-box: STOPPED"
pgrep -x sing-box >/dev/null 2>&1 && echo "sing-box: RUNNING" || echo "sing-box: STOPPED"
if [ "$TUNNEL" = ipt2socks ]; then
pgrep ipt2socks >/dev/null 2>&1 && echo "ipt2socks: RUNNING" || echo "ipt2socks: STOPPED"
pgrep -x ipt2socks >/dev/null 2>&1 && echo "ipt2socks: RUNNING" || echo "ipt2socks: STOPPED"
fi
if [ -r /var/log/nikovpn.log ]; then
echo "--- последние строки /var/log/nikovpn.log ---"
@ -782,7 +754,7 @@ status() {
tu=$(sed -n 's/.*"traffic_used": *\([0-9]*\).*/\1/p' "$TMP/me")
tt=$(sed -n 's/.*"traffic_total": *\([0-9]*\).*/\1/p' "$TMP/me")
td=$(sed -n 's/.*"traffic_day": *\([0-9]*\).*/\1/p' "$TMP/me")
echo "Трафик: использовано $tu байт, за сутки $td, всего $tt"
echo "Трафик: использовано $tu, за сутки $td, лимит $tt байт"
fi
}
@ -795,8 +767,6 @@ uninstall() {
/etc/init.d/nikovpn stop 2>/dev/null
/etc/init.d/nikovpn disable 2>/dev/null
fi
kill $(pgrep sing-box) 2>/dev/null
kill $(pgrep ipt2socks) 2>/dev/null
nft delete table inet nikovpn 2>/dev/null
rm -f /etc/nftables.d/10-nikovpn.nft
for c in NIKOVPN NIKOVPN_TP NIKOVPN_UDP; do
@ -828,11 +798,9 @@ install() {
mkdir -p "$WORK_DIR" "$TMP"
log "Проверяю обновления ..."
dl "$REL_URL/versions" "$TMP/versions" || \
dl "$BASE_URL/versions" "$TMP/versions" || \
cp "$REPO_DIR/versions" "$TMP/versions" 2>/dev/null
[ -s "$TMP/versions" ] || die "не могу скачать versions (нет интернета?)"
log "Проверяю обновления с f.n1ko.dev ..."
dl "$BASE_URL/versions" "$TMP/versions" || cp "$REPO_DIR/versions" "$TMP/versions" 2>/dev/null
[ -s "$TMP/versions" ] || die "не могу скачать $BASE_URL/versions (нет интернета?)"
SB_VER=""; IPT_VER=""
while IFS='=' read -r k v; do
case "$k" in sing-box) SB_VER="$v" ;; ipt2socks) IPT_VER="$v" ;; esac
@ -871,14 +839,10 @@ install() {
esac
fi
# остановить старый сервис и процессы вне procd (иначе Text file busy
# при перезаписи бинарников и занятые порты после старта)
# остановить старый сервис перед перезаписью
if [ -x /etc/init.d/nikovpn ]; then
/etc/init.d/nikovpn stop 2>/dev/null
fi
kill $(pgrep sing-box) 2>/dev/null
kill $(pgrep ipt2socks) 2>/dev/null
sleep 1
install_bins
log "Генерирую конфиг ..."
@ -891,14 +855,11 @@ install() {
chmod +x "$WORK_DIR/install.sh"
log "Запускаю сервис ..."
kill $(pgrep sing-box) 2>/dev/null
kill $(pgrep ipt2socks) 2>/dev/null
sleep 1
/etc/init.d/nikovpn enable 2>/dev/null
/etc/init.d/nikovpn start 2>/dev/null
sleep 5
sleep 3
if pgrep sing-box >/dev/null 2>&1; then
if pgrep -x sing-box >/dev/null 2>&1; then
log "sing-box запущен. Прозрачный прокси активен."
else
warn "sing-box не запустился. Смотрите: cat /var/log/nikovpn.log"

View file

@ -35,7 +35,6 @@ from pathlib import Path
from urllib.parse import parse_qs, unquote, urlsplit
BASE_URL = "https://f.n1ko.dev/nikovpn_openwrt"
REL_URL = "https://git.n1ko.dev/Niko/nikovpn_openwrt/releases/download/bins"
API_URL = "https://vpnd.n1ko.dev/api/v1"
WORK_DIR = "/etc/nikovpn"
CREDS_FILE = "/root/creds"
@ -636,10 +635,9 @@ def install_bins(rt: Router, sb_arch: str, ipt_arch: str, tunnel: str,
if local.exists():
push_gz(rt, local, SB_PATH, "{b} version", "sing-box")
else:
log(f"скачиваю sing-box ({sb_arch}) на роутере ...")
log(f"скачиваю sing-box ({sb_arch}) на роутере с f.n1ko.dev ...")
gz = f"/tmp/nikovpn.{os.getpid()}.sb.gz"
if not (router_download(rt, f"{REL_URL}/sing-box-{sb_arch}.gz", gz) or
router_download(rt, f"{BASE_URL}/bin/sing-box-{sb_arch}.gz", gz)):
if not router_download(rt, f"{BASE_URL}/bin/sing-box-{sb_arch}.gz", gz):
die("не удалось скачать sing-box")
rt.run(f"gzip -dc {gz} > {SB_PATH} && chmod +x {SB_PATH}", check=True)
if rt.run(f"{SB_PATH} version")[0] != 0:
@ -651,10 +649,9 @@ def install_bins(rt: Router, sb_arch: str, ipt_arch: str, tunnel: str,
if local.exists():
push_gz(rt, local, IPT_PATH, "{b} -V", "ipt2socks")
else:
log(f"скачиваю ipt2socks ({ipt_arch}) на роутере ...")
log(f"скачиваю ipt2socks ({ipt_arch}) на роутере с f.n1ko.dev ...")
gz = f"/tmp/nikovpn.{os.getpid()}.ipt.gz"
if not (router_download(rt, f"{REL_URL}/ipt2socks-{ipt_arch}.gz", gz) or
router_download(rt, f"{BASE_URL}/bin/ipt2socks-{ipt_arch}.gz", gz)):
if not router_download(rt, f"{BASE_URL}/bin/ipt2socks-{ipt_arch}.gz", gz):
die("не удалось скачать ipt2socks")
rt.run(f"gzip -dc {gz} > {IPT_PATH} && chmod +x {IPT_PATH}", check=True)
if rt.run(f"{IPT_PATH} -V")[0] != 0:
@ -821,9 +818,7 @@ def do_install(rt: Router, args, update=False):
IPT_VER = v
if not SB_VER:
rc, out, _ = rt.run(
f"(curl -fsSL {REL_URL}/versions 2>/dev/null || "
f"wget -q -O - {REL_URL}/versions 2>/dev/null || "
f"curl -fsSL {BASE_URL}/versions 2>/dev/null || "
f"(curl -fsSL {BASE_URL}/versions 2>/dev/null || "
f"wget -q -O - {BASE_URL}/versions 2>/dev/null)")
if rc == 0:
for line in out.decode(errors="replace").splitlines():
@ -952,7 +947,7 @@ def do_status(rt: Router, args):
me = json.loads(body)
print(f"Трафик: использовано {human(me.get('traffic_used'))}, "
f"за сутки {human(me.get('traffic_day'))}, "
f"всего {human(me.get('traffic_total'))}")
f"лимит {human(me.get('traffic_total'))}")
except SystemExit:
pass

View file

@ -2,17 +2,12 @@
# NikoVPN: статус VPN
[ -f /etc/nikovpn/install.sh ] && exec /etc/nikovpn/install.sh status
TMP=/tmp/nikovpn-status.sh
for URL in \
"https://git.n1ko.dev/Niko/nikovpn_openwrt/releases/download/bins/install.sh" \
"https://f.n1ko.dev/nikovpn_openwrt/install.sh"; do
if command -v curl >/dev/null 2>&1; then
curl -fsSL "$URL" -o "$TMP" 2>/dev/null
elif command -v wget >/dev/null 2>&1; then
wget -q "$URL" -O "$TMP" 2>/dev/null
fi
[ -s "$TMP" ] && break
rm -f "$TMP"
done
URL="https://f.n1ko.dev/nikovpn_openwrt/install.sh"
if command -v curl >/dev/null 2>&1; then
curl -fsSL "$URL" -o "$TMP" 2>/dev/null
elif command -v wget >/dev/null 2>&1; then
wget -q "$URL" -O "$TMP" 2>/dev/null
fi
if [ -s "$TMP" ]; then
chmod +x "$TMP"
exec "$TMP" status

View file

@ -2,17 +2,12 @@
# NikoVPN: удаление VPN (останавливает сервисы и чистит firewall)
[ -f /etc/nikovpn/install.sh ] && exec /etc/nikovpn/install.sh uninstall
TMP=/tmp/nikovpn-uninstall.sh
for URL in \
"https://git.n1ko.dev/Niko/nikovpn_openwrt/releases/download/bins/install.sh" \
"https://f.n1ko.dev/nikovpn_openwrt/install.sh"; do
if command -v curl >/dev/null 2>&1; then
curl -fsSL "$URL" -o "$TMP" 2>/dev/null
elif command -v wget >/dev/null 2>&1; then
wget -q "$URL" -O "$TMP" 2>/dev/null
fi
[ -s "$TMP" ] && break
rm -f "$TMP"
done
URL="https://f.n1ko.dev/nikovpn_openwrt/install.sh"
if command -v curl >/dev/null 2>&1; then
curl -fsSL "$URL" -o "$TMP" 2>/dev/null
elif command -v wget >/dev/null 2>&1; then
wget -q "$URL" -O "$TMP" 2>/dev/null
fi
if [ -s "$TMP" ]; then
chmod +x "$TMP"
exec "$TMP" uninstall

View file

@ -2,17 +2,12 @@
# NikoVPN: обновление (переустановка) — скачивает свежий install.sh с f.n1ko.dev
[ -f /etc/nikovpn/install.sh ] && exec /etc/nikovpn/install.sh update
TMP=/tmp/nikovpn-update.sh
for URL in \
"https://git.n1ko.dev/Niko/nikovpn_openwrt/releases/download/bins/install.sh" \
"https://f.n1ko.dev/nikovpn_openwrt/install.sh"; do
if command -v curl >/dev/null 2>&1; then
curl -fsSL "$URL" -o "$TMP" 2>/dev/null
elif command -v wget >/dev/null 2>&1; then
wget -q "$URL" -O "$TMP" 2>/dev/null
fi
[ -s "$TMP" ] && break
rm -f "$TMP"
done
URL="https://f.n1ko.dev/nikovpn_openwrt/install.sh"
if command -v curl >/dev/null 2>&1; then
curl -fsSL "$URL" -o "$TMP" 2>/dev/null
elif command -v wget >/dev/null 2>&1; then
wget -q "$URL" -O "$TMP" 2>/dev/null
fi
if [ -s "$TMP" ]; then
chmod +x "$TMP"
exec "$TMP" update