feat: first-run /setup password + PRO-style iptables

compat DNAT/INPUT/FORWARD/MASQUERADE; optional chain mode; auth_store hash; static absolute URLs; install skips env password by default; README.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Андрей Бобырев
2026-05-14 20:52:35 +03:00
parent a579612c9a
commit 43daf188c2
10 changed files with 565 additions and 107 deletions

110
README.md
View File

@@ -1,86 +1,89 @@
# kaskad_web_vpn — Kaskad Web UI v2
Веб-панель на **Flask** с **HTTP Basic Auth**: дашборд как у классического «каскада» — блок **Система**, таблица **Сервисы**, CRUD **клиентов (NAT / DNAT)** и просмотр цепочки **iptables**. В шапке — ваши ссылки на поддержку (GitHub, Boosty, Ozon СБП, Telegram), без чужой рекламы в подвале.
Веб-панель на **Flask** с **HTTP Basic Auth**: дашборд в духе «каскада» — **Система**, **Сервисы**, CRUD **NAT / DNAT**, сырые **iptables**. Это **отдельная открытая реализация** под ваш GitHub: без пунктов меню PROMO/«anten-ka», без телеграм-бота и терминального `gokaskad` из коммерческого Kaskad PRO; зато установка **curl | bash** с этого репозитория и понятный NAT для AmneziaWG / TCP по схеме «RU VPS → DNAT → зарубежный сервер».
Репозиторий: [github.com/andrey271192/kaskad_web_vpn](https://github.com/andrey271192/kaskad_web_vpn)
## Важно про NAT
## Первый вход и пароль
Правила записываются в **`nat`**, отдельная цепочка **`KASKAD_WEB`**, подключение к **`PREROUTING`** (DNAT ходящий_порт → target:порт`). Для рабочего каскада на VPS нужны **`net.ipv4.ip_forward=1`** и разрешения в **`filter/FORWARD`** (настраиваются на хосте отдельно).
- **По умолчанию** скрипт установки **не** задаёт пароль в Docker: откройте в браузере **`http://<ваш_IP>:<порт>/setup`**, придумайте пароль (≥ 8 символов) и логин (часто оставляют `user1`). Хэш сохраняется в томе: **`/var/lib/kaskad/web_auth.json`**.
- Чтобы задать пароль сразу при установке: **`ADMIN_PASSWORD`** или файл **`PASSWORD_FILE`** — тогда шаг `/setup` не нужен.
- Как раньше (случайный пароль в файл): **`FORCE_RANDOM_PASSWORD=1`**.
- Если заданы **`BASIC_AUTH_PASSWORD`** / **`ADMIN_PASSWORD`**, используется они (перекрывают файл).
По умолчанию установка поднимает контейнер с **`--network host`** и **`--privileged`**, чтобы **iptables менял таблицы самого хоста**. Режим только с `-p порт:8088` без host network применим для просмотра UI, но DNAT с контейнера на хост в таком виде обычно **не** используют.
## NAT и совместимость с логикой Kaskad PRO
Данные правил: **`KASKAD_DATA_DIR`** на хосте → `/var/lib/kaskad/rules.json` в контейнере.
Переменная **`KASKAD_IPTABLES_MODE`** (в контейнере, install передаёт **`compat`** по умолчанию):
| Значение | Поведение |
|----------|-----------|
| **`compat`** (по умолчанию) | Как в Kaskad PRO: **DNAT** в **`nat/PREROUTING`**, **INPUT** и парой правил **FORWARD** с комментарием **`kaskad:PORT:proto`**, один раз **MASQUERADE** на исходящем интерфейсе (авто через `ip route` или **`KASKAD_OUT_IFACE`**). |
| **`chain`** | Старая схема: отдельная цепочка **`nat/KASKAD_WEB`** и jump из PREROUTING. |
Нужны **`net.ipv4.ip_forward=1`** и достаточное место на диске под Docker.
Установка по умолчанию: **`--network host`** и **`--privileged`**, чтобы правила применялись к **таблицам хоста**. Данные правил: **`KASKAD_DATA_DIR`** → `/var/lib/kaskad/rules.json`.
Попытка сохранить правила в **`netfilter-persistent`**, если установлен на хосте.
## Требования
- Linux с Docker (для NAT — предпочтительно host network + privileged).
- Доступ к **`docker.sock`** на хосте (по умолчанию монтируется), чтобы в таблице «Сервисы» третья строка могла показывать статус **самого контейнера** как `kaskad-web.service`.
- Linux с Docker (для NAT — host network + privileged).
- По желанию **`/var/run/docker.sock`** (чтобы в «Сервисах» отображался статус контейнера как `kaskad-web.service`).
## Сборка образа: «invalid signature» / apt не подписан
## Сборка образа: «invalid signature» / apt
Чаще всего: **битый кэш Docker BuildKit**, **прокси по HTTP**, **место на диске**, **сбитые часы** на хосте.
На хосте:
См. блок в Wiki ниже — типично кэш builder, прокси, диск, время.
```bash
date -u # время должно быть разумным
df -h /var/lib/docker # есть ли свободное место
docker builder prune -af # сбросить кэш сборки
docker builder prune -af
docker build --pull --no-cache -t kaskad-web-vpn:test .
```
Если ошибка остаётся: временно отключите HTTP-прокси для сборки (`unset http_proxy https_proxy NO_PROXY`) или собирайте на другой сети.
В образе списки APT по умолчанию переводятся на **HTTPS** (`deb.debian.org`, `security.debian.org`), чтобы обходить «порченые» HTTP-кэши.
В Dockerfile зеркала APT переведены на HTTPS.
## Установка одной командой
Порт **8088**, логин **user1**, пароль в **`/root/kaskad_web.initial-password`** (или задайте **`ADMIN_PASSWORD`**):
```bash
curl -fsSL https://raw.githubusercontent.com/andrey271192/kaskad_web_vpn/main/install.sh | sudo bash
```
Свой пароль и порт:
Затем откройте **`/setup`** или задайте пароль при установке:
```bash
curl -fsSL https://raw.githubusercontent.com/andrey271192/kaskad_web_vpn/main/install.sh | sudo \
env ADMIN_PASSWORD=аш_секрет' HOST_PORT=8443 bash
```
Панель Amnezia (опционально, ссылка на странице):
Старый режим «случайный пароль в файл»:
```bash
curl -fsSL https://raw.githubusercontent.com/andrey271192/kaskad_web_vpn/main/install.sh | sudo \
env PANEL_URL='https://panel.example.com' bash
env FORCE_RANDOM_PASSWORD=1 bash
```
### Переменные установки (install.sh)
### Переменные install.sh
| Переменная | По умолчанию | Описание |
|------------|--------------|----------|
| `HOST_PORT` | `8088` | Порт HTTP (при host network = `PORT` внутри процесса) |
| `KASKAD_HOST_NETWORK` | `1` | `1``--network host`; `0` — проброс `-p HOST_PORT:8088` (NAT на хост может быть недоступен) |
| `KASKAD_DATA_DIR` | `/var/lib/kaskad` | Том с `rules.json` |
| `MOUNT_DOCKER_SOCK` | `1` | Монтировать `/var/run/docker.sock` для статуса контейнера в «Сервисы» |
| `BASIC_AUTH_USER` | `user1` | Логин Basic Auth |
| `ADMIN_PASSWORD` | — | Пароль |
| `PASSWORD_FILE` | `/root/kaskad_web.initial-password` | Файл пароля при автогенерации |
| `PANEL_URL` | пусто | Доп. ссылка в шапке |
| `KASKAD_REPO` | `andrey271192/kaskad_web_vpn` | Репозиторий |
| `KASKAD_REF` | `main` | Ветка |
| `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` | — | ссылка в шапке |
### Переменные внутри приложения (Docker `-e`)
### Переменные приложения
| Переменная | Описание |
|------------|----------|
| `BOT_TOKEN`, `BOT_CHAT_ID` | Отображаются в блоке «Система» в замаскированном виде |
| `SERVICE_UNITS` | CSV юнитов systemd для первых двух строк (по умолчанию `kaskad_bot.service,kaskad-monitor.service,kaskad-web.service`) |
| `DOCKER_WEB_CONTAINER` | Имя контейнера для третьей строки (install выставляет автоматически) |
| `DOCKER_WEB_DISPLAY_UNIT` | Подпись в таблице для этой строки (по умолчанию `kaskad-web.service`) |
| `KASKAD_UI_VERSION` | Версия в блоке «yaskad» (по умолчанию `v2.2`) |
| `KASKAD_NAT_CHAIN` | Имя цепочки в `nat` (по умолчанию `KASKAD_WEB`) |
| `BOT_TOKEN`, `BOT_CHAT_ID` | только отображение в «Системе» (маска), без бота |
| `SERVICE_UNITS`, `DOCKER_WEB_*` | таблица сервисов |
| `KASKAD_OUT_IFACE` | исходящий интерфейс для MASQUERADE |
| `WEB_AUTH_JSON` | путь к `web_auth.json` |
## Удаление контейнера
@@ -88,38 +91,23 @@ curl -fsSL https://raw.githubusercontent.com/andrey271192/kaskad_web_vpn/main/in
curl -fsSL https://raw.githubusercontent.com/andrey271192/kaskad_web_vpn/main/uninstall.sh | sudo bash
```
Образ:
Режим **`chain`**: удаление jump в **`KASKAD_WEB`** см. исторический README в git.
```bash
curl -fsSL https://raw.githubusercontent.com/andrey271192/kaskad_web_vpn/main/uninstall.sh | sudo env REMOVE_IMAGE=1 bash
```
Цепочку iptables после экспериментов можно убрать вручную (осторожно, на проде проверяйте порядок правил):
```bash
iptables -t nat -D PREROUTING -j KASKAD_WEB # повторять, пока не вернёт ошибку
iptables -t nat -F KASKAD_WEB
iptables -t nat -X KASKAD_WEB
```
Режим **`compat`**: проще снять правила через панель (удалить клиентов) или выборочно удалять строки с **`kaskad:`** в `iptables -S INPUT/FORWARD` и соответствующие DNAT в `iptables -t nat -S PREROUTING`.
## Локально без Docker
Нужны root-права для `iptables` и установленные `iptables` / `ip`.
```bash
python3 -m venv .venv && source .venv/bin/activate
pip install -r requirements.txt
export BASIC_AUTH_PASSWORD='secret'
# либо задайте BASIC_AUTH_PASSWORD, либо откройте /setup
sudo -E env PATH="$PATH" flask --app app run --host 0.0.0.0 --port 8088
```
Проверка без авторизации: `GET /health`.
`GET /health` без авторизации. `GET /api/meta` — флаг `needs_setup`, режим iptables.
## API (после Basic Auth)
## API
- `GET /api/system`, `/api/services`, `/api/clients`
- `POST /api/clients`, `PUT /api/clients/<id>`, `DELETE /api/clients/<id>`
- `GET /api/iptables/raw`, `POST /api/iptables/sync`
После Basic Auth: `/api/system`, `/api/services`, `/api/clients`, CRUD клиентов, `/api/iptables/raw`.
## Лицензия