From 44c032b37609ab88c9b6cf9c1aebc5319e131ce5 Mon Sep 17 00:00:00 2001 From: Ivan Vasilev Date: Sat, 29 Aug 2026 03:45:27 +0300 Subject: [PATCH] Document the panel API in the README MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The public /api/v1 surface and its docs went away with the API keys, leaving nothing written down about what the panel exposes — even though every route it uses is reachable over HTTP with a login token. Replaces the install-source section with an API chapter: the JWT flow and its 401/429 cases, the unauthenticated /ui/brand, health and interface status, the user routes including why fetching a key is a POST, reissue, and the 2.0 to 3.1 upgrade with its 409 and 500 cases. What is left of the install-source notes is one line pointing at the comments in install.sh and .env. Shapes and status codes are taken from the handlers rather than from memory, and the auth behaviour was checked against a running server. --- README.md | 90 ++++++++++++++++++++++++++++++++++++++----------------- 1 file changed, 63 insertions(+), 27 deletions(-) diff --git a/README.md b/README.md index 6598218..f8553ee 100644 --- a/README.md +++ b/README.md @@ -43,45 +43,81 @@ AWG_GEN=auto bash <(curl -Ls https://git.ma7neko.ru/maeneko/forgetting/raw/branc переустановка не нужна, пользователи сохраняются, ключи перевыпускаются автоматически. -### Свой источник и своё имя +Источник архива и имя панели не зашиты в код — задаются переменными +`REPO_BASE`, `REPO_FALLBACK`, `ARCHIVE_URL`, `BRAND` перед запуском установщика +(подробности — в комментариях `install.sh` и в `.env`). -В коде не зашиты ни репозиторий, ни бренд — всё задаётся окружением до запуска: +## API -| Переменная | По умолчанию | Зачем | -|---|---|---| -| `REPO_BASE` | `https://git.ma7neko.ru/maeneko/forgetting` | основной источник архива релиза | -| `REPO_FALLBACK` | `https://github.com/maeneko/forgetting` | запасной, если в основном релиза ещё нет | -| `ARCHIVE_URL` | — | полный URL архива, перекрывает оба варианта | -| `BRAND` | из `.env` | имя продукта в баннерах, systemd-юните и панели | +Отдельного публичного API с ключами нет: наружу смотрит только панель, и всё, +что она умеет, доступно по HTTP. Схема одна — логин отдаёт JWT на 24 часа, +дальше с ним дёргаются те же роуты, что и из браузера. Базовый URL — адрес +сервера и порт, выбранный при установке. -Путь релиза у Gitea и GitHub одинаковый (`/releases/download/v<версия>/awgcontrol-<версия>.tar.gz`), -так что подходит любой из них. Если архив не найден в основном источнике, -установщик предупреждает и берёт его из запасного. +> [!WARNING] +> Панель отдаёт приватные поля пиров (`vpn_key`, `psk_key`) — это не публичный +> контракт. Не выставляй её порт в интернет без TLS и ограничения по адресам. -Архив собирается по тегу `v*`: на GitHub — `.github/workflows/release.yml`, в -Gitea — `.gitea/workflows/release.yml` (сборка та же, публикация через API -Gitea). Для запуска в Gitea репозиторий не должен быть pull-зеркалом, Actions -включены, а runner зарегистрирован с меткой `ubuntu-latest`. +### Авторизация ```bash -REPO_BASE=https://git.example.org/me/vpn BRAND="My VPN" \ - bash <(curl -Ls https://git.example.org/me/vpn/raw/branch/main/install.sh) +TOKEN=$(curl -s -X POST http://HOST:PORT/login \ + -H "Content-Type: application/json" \ + -d '{"user":"admin","pass":"***"}' | jq -r .token) ``` -Имя панели, канал и версия лежат в `.env` в корне проекта (едет в архиве -релиза, на сервере — `/opt/awg-control/.env`): +| Метод | Ответ и ошибки | +|---|---| +| `POST /login` | `{ "token": "" }`, срок 24 часа. `401` — неверная пара; `429` — больше 5 неудачных попыток с одного IP за 15 минут | +| `POST /logout` | `{ "ok": true }`, токен отзывается немедленно | -```env -BRAND=Forgetting -CHANNEL=Beta -VERSION=0.2.0 +Дальше в каждый запрос: `Authorization: Bearer $TOKEN`. + +### Состояние + +| Метод | Ответ | +|---|---| +| `GET /ui/brand` | `{ "brand": "Forgetting", "channel": "Beta", "version": "0.2.0" }` — **без авторизации**, панель берёт отсюда вордмарк на экране логина | +| `GET /health` | `{ "status", "server", "ip", "gen", "awg": { "status", "peers", "module", "tools" } }`. `200`, если интерфейс поднят, иначе `503` и `status: "degraded"` | +| `GET /awg/status` | `{ "up": true, "peers": 3, "publicKey": "…" }` | + +`gen` — поколение AmneziaWG, на параметрах которого сейчас работает интерфейс: +`"2"` или `"3.1"`. Оно выводится из `awg1.conf`, а не хранится отдельно. + +### Пользователи + +| Метод | Что делает | +|---|---| +| `POST /api/users` | Создаёт пира. Тело `{ "name": "alice" }`, имя — `a–z A–Z 0–9 _ -`, до 32 символов. `201` с `{ name, ip, pub_key, psk_key, vpn_key, key_gen, vpn_key_prev }`; `400` — имя не прошло валидацию, `409` — уже есть | +| `GET /api/users` | Список: `{ users: [{ name, ip, pub_key, vpn_key, key_gen, online, lastHandshake }] }` | +| `GET /api/users/stats` | То же без ключей, но со счётчиками: `{ users: [{ name, ip, key_gen, online, lastHandshake, rx, tx }] }` | +| `POST /api/users/:name` | Возвращает пользователя целиком, включая `vpn_key`. Метод именно `POST`, чтобы ключ не оседал в логах и истории как параметр `GET`. `404` — не найден | +| `DELETE /api/users/:name` | `{ "success": true, "name": "alice" }` | +| `POST /api/users/reissue` | Пересобирает `vpn://` всем на текущих параметрах: `{ total, reissued, regenerated: [], backup }`. IP и ключевая пара сохраняются, клиентам нужно заново импортировать ключ; `backup` — путь к снимку базы, снятому перед проходом | + +```bash +curl -s -X POST http://HOST:PORT/api/users \ + -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \ + -d '{"name":"alice"}' | jq -r .vpn_key ``` -Оттуда их читают CLI (баннер меню) и панель (вордмарк, `GET /ui/brand`). -`BRAND=` при установке перекрывает значение из файла. +Имея `vpn_key`, конфиг `.conf` можно получить локально: убрать префикс `vpn://`, +base64url-декодировать, отбросить первые 4 байта (длина), распаковать +`zlib inflate` → JSON; текст конфига лежит в `last_config.config`. + +### Интерфейс AWG + +| Метод | Что делает | +|---|---| +| `POST /awg/start` | Поднимает интерфейс, если он лежит; возвращает его статус | +| `POST /awg/restart` | `awg-quick down/up` + ресинк пиров, `{ "success": true }`. Соединения клиентов кратковременно рвутся | +| `POST /awg/upgrade` | Переводит сервер с 2.0 на 3.1: правит `awg1.conf`, перезапускает интерфейс и перевыпускает все ключи. `{ gen, backup, bumpedS: [], reissue: { total, reissued, … } }` | + +`POST /awg/upgrade` отвечает `409`, если сервер уже на 3.1, модуль в ядре не +3.x или ядро старше 5.5, и `500` с текстом причины, если ядро не приняло +3.1-конфиг — в этом случае прежний конфиг возвращается из копии, а интерфейс +поднимается обратно на 2.0. -Сама панель и её API к домену не привязаны: работают по адресу того сервера, -куда установлены, внешних сервисов не требуют. ## Управление ```bash