Bring back the external API keys
Commit0dbb13fremoved the whole feature: the apikeys tab, the /ui/apikeys routes, requireApiKey, the /api/v1 router, the ApiKey type, the better-sqlite3 dependency and UI_DB_FILE in the CLI and the installer. Only the untracked awg-ui/API.md survived, so the README went on to claim there was no key-based API at all. Restores all of it: ui.db with the api_keys table, awgk_ key generation storing only the SHA-256 and a display prefix, the JWT-guarded CRUD, and the /api/v1 router mounted before the JWT proxy on /api so the key auth is not swallowed. The tab comes back as its own folder and the registry picks it up on its own. UI_DB_FILE points at /etc/amnezia/amneziawg/ui.db, outside the install tree, so reinstalling keeps the keys; a fresh install moves the old one aside with its WAL sidecars. better-sqlite3 takes the slot of jsonwebtoken, which arrived with the MA7 integration and has been unused since068cab4removed it. One defect is not reproduced: the /api/v1 handlers called ctrl() without a try/catch, and Express 4 does not catch async rejections, so a single request with awg-ctrl down killed the panel process. They now answer 502 like proxy() already did. The API contract is documented in the README rather than linked: .gitignore excludes every markdown but README.md, so awg-ui/API.md is not in the repo and the link would break on a clone. Checked against a running server: login, key create/list/delete, 400 on a bad label, 401 without a JWT, "API key required" rather than "Unauthorized" on /api/v1, "Invalid API key" for a deleted key, last_used updating, and all three /api/v1 routes returning 502 with the process still alive.
This commit is contained in:
@@ -9,7 +9,7 @@
|
||||
| Компонент | Роль |
|
||||
|---|---|
|
||||
| **awg-ctrl** | Привилегированный REST-бэкенд. Управляет пирами через `awg`/`awg-quick`, ведёт SQLite-базу, генерирует `vpn://` ключи. Слушает только loopback. |
|
||||
| **awg-ui** | Express + React. Веб-панель, аутентификация (JWT), прокси к awg-ctrl. Единственный сервис, доступный по сети. |
|
||||
| **awg-ui** | Express + React. Веб-панель, аутентификация (JWT), прокси к awg-ctrl, публичный API `/api/v1` и своя база его ключей. Единственный сервис, доступный по сети. |
|
||||
| **cli** | Менеджер процессов: `start`/`stop`/`restart`/`status`, смена логина/пароля панели, интерактивное TUI-меню. |
|
||||
|
||||
Внутренняя связь awg-ui → awg-ctrl подписывается асимметрично (Ed25519): приватный ключ только у awg-ui, awg-ctrl проверяет подпись публичным.
|
||||
@@ -49,16 +49,53 @@ AWG_GEN=auto bash <(curl -Ls https://git.ma7neko.ru/maeneko/forgetting/raw/branc
|
||||
|
||||
## API
|
||||
|
||||
Отдельного публичного API с ключами нет: наружу смотрит только панель, и всё,
|
||||
что она умеет, доступно по HTTP. Схема одна — логин отдаёт JWT на 24 часа,
|
||||
дальше с ним дёргаются те же роуты, что и из браузера. Базовый URL — адрес
|
||||
сервера и порт, выбранный при установке.
|
||||
Наружу смотрят две независимые поверхности. **Публичный API** `/api/v1` —
|
||||
стабильный контракт для сторонних программ, авторизация по API-ключу.
|
||||
**Роуты панели** — всё, что умеет веб-интерфейс, авторизация по JWT. Базовый
|
||||
URL в обоих случаях — адрес сервера и порт, выбранный при установке.
|
||||
|
||||
> [!WARNING]
|
||||
> Панель отдаёт приватные поля пиров (`vpn_key`, `psk_key`) — это не публичный
|
||||
> контракт. Не выставляй её порт в интернет без TLS и ограничения по адресам.
|
||||
> Роуты панели отдают приватные поля пиров (`vpn_key`, `psk_key`) — это не
|
||||
> публичный контракт, в отличие от `/api/v1`. Не выставляй её порт в интернет
|
||||
> без TLS и ограничения по адресам.
|
||||
|
||||
### Авторизация
|
||||
### Публичный API (`/api/v1`)
|
||||
|
||||
Ключ создаётся во вкладке «API-ключи» и уходит в заголовке `X-Api-Key`.
|
||||
Открытое значение показывается один раз при создании — на сервере лежит только
|
||||
SHA-256 и префикс для показа в списке. Наружу отдаются только публичные поля:
|
||||
`psk_key` и `pub_key` не покидают сервер.
|
||||
|
||||
```bash
|
||||
curl -s -X POST http://HOST:PORT/api/v1/users \
|
||||
-H "X-Api-Key: awgk_xxx" -H "Content-Type: application/json" \
|
||||
-d '{"name":"alice"}' | jq -r .vpn_key
|
||||
```
|
||||
|
||||
| Метод | Что делает |
|
||||
|---|---|
|
||||
| `POST /api/v1/users` | Создаёт пира. Тело `{ "name": "alice" }`. `201` с `{ name, ip, gen, vpn_key }`; `400` — имя не прошло валидацию, `409` — уже есть |
|
||||
| `GET /api/v1/users` | Список со счётчиками: `{ users: [{ name, ip, key_gen, online, lastHandshake, rx, tx }] }` |
|
||||
| `GET /api/v1/users/:name` | `{ name, ip, gen, vpn_key }`; `404` — не найден |
|
||||
| `DELETE /api/v1/users/:name` | `{ "success": true, "name": "alice" }` |
|
||||
|
||||
`gen` — поколение AmneziaWG, на параметрах которого собран ключ (`"2"` или
|
||||
`"3.1"`). Операции только CRD: у пира нет изменяемых полей, «перевыпустить»
|
||||
ключ = удалить и создать заново.
|
||||
|
||||
| Код | Когда |
|
||||
|---|---|
|
||||
| `401 {"error":"API key required"}` | заголовка нет или он не начинается с `awgk_` |
|
||||
| `401 {"error":"Invalid API key"}` | ключ не найден — удалён или неверен |
|
||||
| `502 {"error":"awg-ctrl недоступен"}` | awg-ui посредничает, а awg-ctrl не отвечает |
|
||||
|
||||
Ключи лежат в собственной БД панели (`/etc/amnezia/amneziawg/ui.db`) — вне
|
||||
каталога установки, поэтому переустановка их не трогает. Сама awg-ctrl про
|
||||
внешние ключи ничего не знает: awg-ui ходит к ней своей внутренней авторизацией.
|
||||
Управлять ключами можно и без панели, по JWT: `GET/POST /ui/apikeys` и
|
||||
`DELETE /ui/apikeys/:id`.
|
||||
|
||||
### Авторизация панели
|
||||
|
||||
```bash
|
||||
TOKEN=$(curl -s -X POST http://HOST:PORT/login \
|
||||
@@ -71,7 +108,8 @@ TOKEN=$(curl -s -X POST http://HOST:PORT/login \
|
||||
| `POST /login` | `{ "token": "<JWT>" }`, срок 24 часа. `401` — неверная пара; `429` — больше 5 неудачных попыток с одного IP за 15 минут |
|
||||
| `POST /logout` | `{ "ok": true }`, токен отзывается немедленно |
|
||||
|
||||
Дальше в каждый запрос: `Authorization: Bearer $TOKEN`.
|
||||
Дальше в каждый запрос: `Authorization: Bearer $TOKEN`. Ниже — роуты, которые
|
||||
дёргает сама панель; в отличие от `/api/v1` их форма может меняться.
|
||||
|
||||
### Состояние
|
||||
|
||||
|
||||
Reference in New Issue
Block a user