mirror of
https://github.com/andrey271192/kaskad_web_vpn.git
synced 2026-09-20 11:55:35 +00:00
docs: drop third-party names; simplify system panel
README turnkey focus; remove BOT fields and yaskad label; neutral iptables comments; default SERVICE_UNITS without bot units. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
128
README.md
128
README.md
@@ -1,109 +1,95 @@
|
|||||||
# kaskad_web_vpn — Kaskad Web UI v2
|
# kaskad_web_vpn — каскад NAT под ключ
|
||||||
|
|
||||||
Веб-панель на **Flask** с **HTTP Basic Auth**: дашборд в духе «каскада» — **Система**, **Сервисы**, CRUD **NAT / DNAT**, сырые **iptables**. Это **отдельная открытая реализация** под ваш GitHub: без пунктов меню PROMO/«anten-ka», без телеграм-бота и терминального `gokaskad` из коммерческого Kaskad PRO; зато установка **curl | bash** с этого репозитория и понятный NAT для AmneziaWG / TCP по схеме «RU VPS → DNAT → зарубежный сервер».
|
Веб-панель на **Flask** с **HTTP Basic Auth**: **каскад** (DNAT UDP/TCP с российского VPS на зарубежный узел AmneziaWG / WireGuard / TCP), блок **Система**, **Сервисы**, таблица правил и вывод **iptables**. Установка одной командой с **вашего** репозитория GitHub.
|
||||||
|
|
||||||
Репозиторий: [github.com/andrey271192/kaskad_web_vpn](https://github.com/andrey271192/kaskad_web_vpn)
|
Репозиторий: [github.com/andrey271192/kaskad_web_vpn](https://github.com/andrey271192/kaskad_web_vpn)
|
||||||
|
|
||||||
|
## Установка под ключ
|
||||||
|
|
||||||
|
На VPS под root:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl -fsSL https://raw.githubusercontent.com/andrey271192/kaskad_web_vpn/main/install.sh | sudo bash
|
||||||
|
```
|
||||||
|
|
||||||
|
Дальше откройте в браузере **`http://<IP сервера>:8088/setup`** и задайте пароль (или передайте **`ADMIN_PASSWORD`** при установке — см. ниже).
|
||||||
|
|
||||||
|
Удаление:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl -fsSL https://raw.githubusercontent.com/andrey271192/kaskad_web_vpn/main/uninstall.sh | sudo bash
|
||||||
|
```
|
||||||
|
|
||||||
## Первый вход и пароль
|
## Первый вход и пароль
|
||||||
|
|
||||||
- **По умолчанию** скрипт установки **не** задаёт пароль в Docker: откройте в браузере **`http://<ваш_IP>:<порт>/setup`**, придумайте пароль (≥ 8 символов) и логин (часто оставляют `user1`). Хэш сохраняется в томе: **`/var/lib/kaskad/web_auth.json`**.
|
- По умолчанию пароль **не** подставляется в Docker: страница **`/setup`** (файл **`/var/lib/kaskad/web_auth.json`** на томе).
|
||||||
- Чтобы задать пароль сразу при установке: **`ADMIN_PASSWORD`** или файл **`PASSWORD_FILE`** — тогда шаг `/setup` не нужен.
|
- Готовый пароль при установке: **`ADMIN_PASSWORD='…'`** или файл **`PASSWORD_FILE`**.
|
||||||
- Как раньше (случайный пароль в файл): **`FORCE_RANDOM_PASSWORD=1`**.
|
- Случайный пароль в файл: **`FORCE_RANDOM_PASSWORD=1`**.
|
||||||
- Если заданы **`BASIC_AUTH_PASSWORD`** / **`ADMIN_PASSWORD`**, используется они (перекрывают файл).
|
|
||||||
|
|
||||||
## NAT и совместимость с логикой Kaskad PRO
|
## Как устроен NAT
|
||||||
|
|
||||||
Переменная **`KASKAD_IPTABLES_MODE`** (в контейнере, install передаёт **`compat`** по умолчанию):
|
Переменная **`KASKAD_IPTABLES_MODE`** (install передаёт **`compat`** по умолчанию):
|
||||||
|
|
||||||
| Значение | Поведение |
|
| Значение | Описание |
|
||||||
|----------|-----------|
|
|----------|----------|
|
||||||
| **`compat`** (по умолчанию) | Как в Kaskad PRO: **DNAT** в **`nat/PREROUTING`**, **INPUT** и парой правил **FORWARD** с комментарием **`kaskad:PORT:proto`**, один раз **MASQUERADE** на исходящем интерфейсе (авто через `ip route` или **`KASKAD_OUT_IFACE`**). |
|
| **`compat`** | Классический каскад: **DNAT** в **`nat/PREROUTING`**, правила **INPUT** и **FORWARD** с меткой **`kaskad:PORT:proto`**, **MASQUERADE** на исходящем интерфейсе (авто или **`KASKAD_OUT_IFACE`**). |
|
||||||
| **`chain`** | Старая схема: отдельная цепочка **`nat/KASKAD_WEB`** и jump из PREROUTING. |
|
| **`chain`** | Отдельная цепочка **`nat/KASKAD_WEB`** и переход из PREROUTING. |
|
||||||
|
|
||||||
Нужны **`net.ipv4.ip_forward=1`** и достаточное место на диске под Docker.
|
Нужно **`net.ipv4.ip_forward=1`**. Контейнер обычно с **`--network host`** и **`--privileged`**. Данные правил: **`KASKAD_DATA_DIR`** → `rules.json`.
|
||||||
|
|
||||||
Установка по умолчанию: **`--network host`** и **`--privileged`**, чтобы правила применялись к **таблицам хоста**. Данные правил: **`KASKAD_DATA_DIR`** → `/var/lib/kaskad/rules.json`.
|
|
||||||
|
|
||||||
Попытка сохранить правила в **`netfilter-persistent`**, если установлен на хосте.
|
|
||||||
|
|
||||||
## Требования
|
## Требования
|
||||||
|
|
||||||
- Linux с Docker (для NAT — host network + privileged).
|
- Linux, Docker, для NAT — host network и privileged (как в скрипте).
|
||||||
- По желанию **`/var/run/docker.sock`** (чтобы в «Сервисах» отображался статус контейнера как `kaskad-web.service`).
|
|
||||||
|
|
||||||
## Сборка образа: «invalid signature» / apt
|
## Если сборка образа ругается на подпись apt
|
||||||
|
|
||||||
См. блок в Wiki ниже — типично кэш builder, прокси, диск, время.
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
docker builder prune -af
|
docker builder prune -af
|
||||||
docker build --pull --no-cache -t kaskad-web-vpn:test .
|
docker build --pull --no-cache -t kaskad-web-vpn:test .
|
||||||
```
|
```
|
||||||
|
|
||||||
В Dockerfile зеркала APT переведены на HTTPS.
|
Проверьте время (`date -u`), место на диске, при необходимости отключите HTTP-прокси при сборке.
|
||||||
|
|
||||||
## Установка одной командой
|
## Переменные install.sh
|
||||||
|
|
||||||
```bash
|
| Переменная | По умолчанию | Описание |
|
||||||
curl -fsSL https://raw.githubusercontent.com/andrey271192/kaskad_web_vpn/main/install.sh | sudo bash
|
|------------|--------------|----------|
|
||||||
```
|
| `HOST_PORT` | `8088` | порт HTTP |
|
||||||
|
| `KASKAD_HOST_NETWORK` | `1` | host network / или `-p` |
|
||||||
|
| `KASKAD_DATA_DIR` | `/var/lib/kaskad` | том данных |
|
||||||
|
| `KASKAD_IPTABLES_MODE` | `compat` | `compat` или `chain` |
|
||||||
|
| `ADMIN_PASSWORD` | — | пароль без `/setup` |
|
||||||
|
| `FORCE_RANDOM_PASSWORD` | `0` | автопароль в `PASSWORD_FILE` |
|
||||||
|
| `PASSWORD_FILE` | `/root/kaskad_web.initial-password` | чтение/запись пароля |
|
||||||
|
| `BASIC_AUTH_USER` | `user1` | логин при env-пароле |
|
||||||
|
| `PANEL_URL` | — | доп. ссылка в шапке |
|
||||||
|
| `KASKAD_REPO` | `andrey271192/kaskad_web_vpn` | откуда качать исходники |
|
||||||
|
|
||||||
Затем откройте **`/setup`** или задайте пароль при установке:
|
## Переменные приложения
|
||||||
|
|
||||||
|
| Переменная | Описание |
|
||||||
|
|------------|----------|
|
||||||
|
| `KASKAD_UI_VERSION` | строка версии в блоке «Система» |
|
||||||
|
| `SERVICE_UNITS` | CSV systemd-юнитов для таблицы (первые строки); третья строка может быть статусом Docker-контейнера панели |
|
||||||
|
| `DOCKER_WEB_CONTAINER`, `DOCKER_WEB_DISPLAY_UNIT` | контейнер и подпись в «Сервисах» |
|
||||||
|
| `KASKAD_OUT_IFACE` | интерфейс для MASQUERADE |
|
||||||
|
| `WEB_AUTH_JSON` | путь к файлу пароля веб-интерфейса |
|
||||||
|
|
||||||
|
Примеры установки с паролем и портом:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
curl -fsSL https://raw.githubusercontent.com/andrey271192/kaskad_web_vpn/main/install.sh | sudo \
|
curl -fsSL https://raw.githubusercontent.com/andrey271192/kaskad_web_vpn/main/install.sh | sudo \
|
||||||
env ADMIN_PASSWORD='ваш_секрет' HOST_PORT=8443 bash
|
env ADMIN_PASSWORD='ваш_секрет' HOST_PORT=8443 bash
|
||||||
```
|
```
|
||||||
|
|
||||||
Старый режим «случайный пароль в файл»:
|
## Локальный запуск (без Docker)
|
||||||
|
|
||||||
```bash
|
|
||||||
curl -fsSL https://raw.githubusercontent.com/andrey271192/kaskad_web_vpn/main/install.sh | sudo \
|
|
||||||
env FORCE_RANDOM_PASSWORD=1 bash
|
|
||||||
```
|
|
||||||
|
|
||||||
### Переменные install.sh
|
|
||||||
|
|
||||||
| Переменная | По умолчанию | Описание |
|
|
||||||
|------------|--------------|----------|
|
|
||||||
| `HOST_PORT` | `8088` | HTTP-порт |
|
|
||||||
| `KASKAD_HOST_NETWORK` | `1` | host network / или `-p` |
|
|
||||||
| `KASKAD_DATA_DIR` | `/var/lib/kaskad` | том данных |
|
|
||||||
| `KASKAD_IPTABLES_MODE` | `compat` | `compat` или `chain` |
|
|
||||||
| `ADMIN_PASSWORD` | — | пароль без `/setup` |
|
|
||||||
| `FORCE_RANDOM_PASSWORD` | `0` | `1` — автопароль в `PASSWORD_FILE` |
|
|
||||||
| `PASSWORD_FILE` | `/root/kaskad_web.initial-password` | при FORCE_RANDOM или чтении пароля |
|
|
||||||
| `BASIC_AUTH_USER` | `user1` | логин при использовании env-пароля |
|
|
||||||
| `PANEL_URL` | — | ссылка в шапке |
|
|
||||||
|
|
||||||
### Переменные приложения
|
|
||||||
|
|
||||||
| Переменная | Описание |
|
|
||||||
|------------|----------|
|
|
||||||
| `BOT_TOKEN`, `BOT_CHAT_ID` | только отображение в «Системе» (маска), без бота |
|
|
||||||
| `SERVICE_UNITS`, `DOCKER_WEB_*` | таблица сервисов |
|
|
||||||
| `KASKAD_OUT_IFACE` | исходящий интерфейс для MASQUERADE |
|
|
||||||
| `WEB_AUTH_JSON` | путь к `web_auth.json` |
|
|
||||||
|
|
||||||
## Удаление контейнера
|
|
||||||
|
|
||||||
```bash
|
|
||||||
curl -fsSL https://raw.githubusercontent.com/andrey271192/kaskad_web_vpn/main/uninstall.sh | sudo bash
|
|
||||||
```
|
|
||||||
|
|
||||||
Режим **`chain`**: удаление jump в **`KASKAD_WEB`** см. исторический README в git.
|
|
||||||
|
|
||||||
Режим **`compat`**: проще снять правила через панель (удалить клиентов) или выборочно удалять строки с **`kaskad:`** в `iptables -S INPUT/FORWARD` и соответствующие DNAT в `iptables -t nat -S PREROUTING`.
|
|
||||||
|
|
||||||
## Локально без Docker
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
pip install -r requirements.txt
|
pip install -r requirements.txt
|
||||||
# либо задайте BASIC_AUTH_PASSWORD, либо откройте /setup
|
|
||||||
sudo -E env PATH="$PATH" flask --app app run --host 0.0.0.0 --port 8088
|
sudo -E env PATH="$PATH" flask --app app run --host 0.0.0.0 --port 8088
|
||||||
```
|
```
|
||||||
|
|
||||||
`GET /health` без авторизации. `GET /api/meta` — флаг `needs_setup`, режим iptables.
|
`GET /health` без авторизации. `GET /api/meta` — `needs_setup`, режим iptables.
|
||||||
|
|
||||||
## API
|
## API
|
||||||
|
|
||||||
|
|||||||
@@ -123,5 +123,5 @@ if [[ "${#AUTH_ENV_ARGS[@]}" -gt 0 ]]; then
|
|||||||
else
|
else
|
||||||
echo " Пароль: создайте на странице /setup при первом открытии."
|
echo " Пароль: создайте на странице /setup при первом открытии."
|
||||||
fi
|
fi
|
||||||
echo " NAT: режим ${KASKAD_IPTABLES_MODE:-compat} (compat ≈ Kaskad PRO), данные: ${KASKAD_DATA_DIR}"
|
echo " NAT: режим ${KASKAD_IPTABLES_MODE:-compat}, данные: ${KASKAD_DATA_DIR}"
|
||||||
echo " Рекомендуется: sysctl net.ipv4.ip_forward=1."
|
echo " Рекомендуется: sysctl net.ipv4.ip_forward=1."
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
"""Хранение правил NAT и синхронизация с iptables.
|
"""Хранение правил NAT и синхронизация с iptables.
|
||||||
|
|
||||||
Режимы (KASKAD_IPTABLES_MODE):
|
Режимы (KASKAD_IPTABLES_MODE):
|
||||||
- compat — как Kaskad PRO: DNAT в PREROUTING, INPUT/FORWARD с комментарием kaskad:PORT:proto, MASQUERADE.
|
- compat — DNAT в PREROUTING, INPUT/FORWARD с комментарием kaskad:PORT:proto, MASQUERADE.
|
||||||
- chain — отдельная цепочка nat/KASKAD_WEB (старый вариант).
|
- chain — отдельная цепочка nat/KASKAD_WEB (альтернатива).
|
||||||
"""
|
"""
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
@@ -184,7 +184,7 @@ def _flush_matching_rules(table: str | None, chain: str, predicate) -> None:
|
|||||||
|
|
||||||
|
|
||||||
def compat_remove_rule(proto: str, in_port: int) -> None:
|
def compat_remove_rule(proto: str, in_port: int) -> None:
|
||||||
"""Убирает DNAT + INPUT/FORWARD как в Kaskad PRO."""
|
"""Удаляет связанные DNAT и правила filter по порту/протоколу."""
|
||||||
cm = _kaskad_comment(in_port, proto)
|
cm = _kaskad_comment(in_port, proto)
|
||||||
|
|
||||||
def pred_nat(rest: str) -> bool:
|
def pred_nat(rest: str) -> bool:
|
||||||
|
|||||||
@@ -35,9 +35,7 @@
|
|||||||
["Load", data.load],
|
["Load", data.load],
|
||||||
["Mem", data.mem],
|
["Mem", data.mem],
|
||||||
["IPADDR / IPv4", String(data.ipaddrs || "").replace(/\n/g, "<br>")],
|
["IPADDR / IPv4", String(data.ipaddrs || "").replace(/\n/g, "<br>")],
|
||||||
["yaskad", data.yaskad_version],
|
["Версия UI", data.ui_version],
|
||||||
["BOT_TOKEN", data.bot_token_masked],
|
|
||||||
["BOT_CHAT_ID", data.bot_chat_masked],
|
|
||||||
];
|
];
|
||||||
g.innerHTML = cells
|
g.innerHTML = cells
|
||||||
.map(function (kv) {
|
.map(function (kv) {
|
||||||
|
|||||||
@@ -89,12 +89,17 @@ def _ipv4_addrs() -> str:
|
|||||||
return "\n".join(lines) if lines else "—"
|
return "\n".join(lines) if lines else "—"
|
||||||
|
|
||||||
|
|
||||||
def mask_secret(val: str, keep_start: int = 4, keep_end: int = 2) -> str:
|
def collect_system_payload() -> dict:
|
||||||
if not val:
|
ui_ver = os.environ.get("KASKAD_UI_VERSION", "v2.2").strip()
|
||||||
return "—"
|
|
||||||
if len(val) <= keep_start + keep_end:
|
return {
|
||||||
return "***"
|
"machine_id": _read_machine_id(),
|
||||||
return val[:keep_start] + "…" + val[-keep_end:]
|
"uptime": _uptime_h(),
|
||||||
|
"load": _load_avg(),
|
||||||
|
"mem": _mem_mb(),
|
||||||
|
"ipaddrs": _ipv4_addrs(),
|
||||||
|
"ui_version": ui_ver,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
def systemd_unit_row(unit: str) -> dict[str, str]:
|
def systemd_unit_row(unit: str) -> dict[str, str]:
|
||||||
@@ -156,27 +161,10 @@ def docker_container_row(name: str, display_unit: str | None = None) -> dict[str
|
|||||||
return row
|
return row
|
||||||
|
|
||||||
|
|
||||||
def collect_system_payload() -> dict:
|
|
||||||
ui_ver = os.environ.get("KASKAD_UI_VERSION", "v2.2").strip()
|
|
||||||
bot_token = os.environ.get("BOT_TOKEN", "")
|
|
||||||
bot_chat = os.environ.get("BOT_CHAT_ID", "")
|
|
||||||
|
|
||||||
return {
|
|
||||||
"machine_id": _read_machine_id(),
|
|
||||||
"uptime": _uptime_h(),
|
|
||||||
"load": _load_avg(),
|
|
||||||
"mem": _mem_mb(),
|
|
||||||
"ipaddrs": _ipv4_addrs(),
|
|
||||||
"yaskad_version": ui_ver,
|
|
||||||
"bot_token_masked": mask_secret(bot_token),
|
|
||||||
"bot_chat_masked": mask_secret(bot_chat, 2, 2),
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
def collect_services_rows() -> list[dict[str, str]]:
|
def collect_services_rows() -> list[dict[str, str]]:
|
||||||
units_csv = os.environ.get(
|
units_csv = os.environ.get(
|
||||||
"SERVICE_UNITS",
|
"SERVICE_UNITS",
|
||||||
"kaskad_bot.service,kaskad-monitor.service,kaskad-web.service",
|
"dbus.service,systemd-networkd.service,kaskad-web.service",
|
||||||
)
|
)
|
||||||
units = [u.strip() for u in units_csv.split(",") if u.strip()]
|
units = [u.strip() for u in units_csv.split(",") if u.strip()]
|
||||||
docker_web = os.environ.get("DOCKER_WEB_CONTAINER", "").strip()
|
docker_web = os.environ.get("DOCKER_WEB_CONTAINER", "").strip()
|
||||||
|
|||||||
@@ -91,7 +91,7 @@
|
|||||||
{% if iptables_mode == 'chain' %}
|
{% if iptables_mode == 'chain' %}
|
||||||
Kaskad Web UI · <a href="https://github.com/andrey271192/kaskad_web_vpn">kaskad_web_vpn</a> · HTTP Basic Auth · DNAT в цепочке <span class="mono">KASKAD_WEB</span> (nat).
|
Kaskad Web UI · <a href="https://github.com/andrey271192/kaskad_web_vpn">kaskad_web_vpn</a> · HTTP Basic Auth · DNAT в цепочке <span class="mono">KASKAD_WEB</span> (nat).
|
||||||
{% else %}
|
{% else %}
|
||||||
Kaskad Web UI · <a href="https://github.com/andrey271192/kaskad_web_vpn">kaskad_web_vpn</a> · HTTP Basic Auth · режим как у Kaskad PRO: DNAT в <span class="mono">PREROUTING</span>, метки <span class="mono">kaskad:PORT:proto</span> в INPUT/FORWARD, <span class="mono">MASQUERADE</span> на исходящем интерфейсе (<span class="mono">KASKAD_IPTABLES_MODE=compat</span>).
|
Kaskad Web UI · <a href="https://github.com/andrey271192/kaskad_web_vpn">kaskad_web_vpn</a> · HTTP Basic Auth · каскад: DNAT в <span class="mono">PREROUTING</span>, метки <span class="mono">kaskad:PORT:proto</span> в INPUT/FORWARD, <span class="mono">MASQUERADE</span> (<span class="mono">KASKAD_IPTABLES_MODE=compat</span>).
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</footer>
|
</footer>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user