Files
amnezia-fellow/README.md
T

157 lines
5.7 KiB
Markdown
Raw Normal View History

2026-06-29 15:50:25 +03:00
# amnezia-fellow
Amnezia VPN client manager written in Rust on top of the public [`cot`](https://cot.rs) framework.
The app uses SQLite as the source of truth, authenticates users through OIDC/SSO, renders AmneziaWG client peers into a Kubernetes Secret, and avoids updating that Secret when the rendered content is byte-for-byte identical.
## Quick Start
```bash
export AMNEZIA_FELLOW_DATABASE_URL=sqlite://amnezia-fellow.sqlite3?mode=rwc
cargo run -- --listen 127.0.0.1:8000
```
Open `http://localhost:8000/admin/setup` to create the first local admin account.
## Docker
The image is built by GitHub Actions and published to Docker Hub as
`ultradesu/amnezia-fellow`.
Required repository secrets:
- `DOCKERHUB_USERNAME`
- `DOCKERHUB_TOKEN`
Publishing runs for version tags matching `v*.*.*`. A tag like `v0.1.0` pushes:
- `ultradesu/amnezia-fellow:v0.1.0`
- `ultradesu/amnezia-fellow:0.1.0`
- `ultradesu/amnezia-fellow:latest`
Local image build:
```bash
docker build -t amnezia-fellow .
docker run --rm -p 8000:8000 -v "$PWD/data:/data" amnezia-fellow
```
## Roles
There are two roles:
- `admin`: full access, sees all client configs.
- `client`: sees and manages only their own configs.
OIDC provisioning is deny-by-default:
- users in `AMNEZIA_FELLOW_OIDC_ADMIN_GROUPS` become `admin`;
- users in `AMNEZIA_FELLOW_OIDC_CLIENT_GROUPS` become `client`;
- users outside both group lists cannot sign in.
The OIDC groups claim is expected to be `groups`.
## VPN Data Model
SQLite stores all client data needed to restore configs:
- owner user id
- display name
- assigned IPv4 address
- public key
- private key
- enabled flag
- created/updated timestamps
The Kubernetes Secret is derived from the database. Active clients are rendered into one configured Secret key, `peers.conf` by default.
## Kubernetes Sync
The app is intended to run inside Kubernetes with a ServiceAccount and RBAC that can read and update Secrets and list Pods in the Amnezia namespace.
It reads:
- server public key from `AMNEZIA_FELLOW_K8S_SERVER_SECRET`, key `server-public-key`;
- node endpoints from `AMNEZIA_FELLOW_K8S_ENDPOINTS_SECRET`; Secret data keys are used as Amnezia server names in generated `vpn://` links;
- AmneziaWG pods selected by `app=amneziawg` for config rollout status;
- writes rendered client peers to `AMNEZIA_FELLOW_K8S_CLIENTS_SECRET`.
It does not modify the server Secret. Argo/ExternalSecrets can keep managing `amneziawg-server`, while amnezia-fellow owns only the client Secret content.
## UI
The main user interface is `/configs`. It is an Alpine.js reactive page backed by JSON API endpoints:
- clients and admins use the same page;
- the backend filters data by role;
- admins get an extra manual Secret sync action.
- clients and admins see AmneziaWG pod rollout status and uptime.
## Environment Variables
All settings use `AMNEZIA_FELLOW_` prefix. Priority is:
`environment variable > database override > compiled default`
| Variable | Description | Default |
| --- | --- | --- |
| `AMNEZIA_FELLOW_DATABASE_URL` | SQLite connection URL | `sqlite://amnezia-fellow.sqlite3?mode=rwc` |
| `AMNEZIA_FELLOW_LOG_LEVEL` | Tracing filter | `info` |
| `AMNEZIA_FELLOW_AUTH_PASSWORD_ENABLED` | Enable password login | `true` |
| `AMNEZIA_FELLOW_AUTH_SSO_ENABLED` | Enable OIDC login | `false` |
| `AMNEZIA_FELLOW_OIDC_ISSUER` | OIDC issuer URL | empty |
| `AMNEZIA_FELLOW_OIDC_CLIENT_ID` | OIDC client ID | empty |
| `AMNEZIA_FELLOW_OIDC_CLIENT_SECRET` | OIDC client secret | empty |
| `AMNEZIA_FELLOW_OIDC_BUTTON_TEXT` | SSO button label | `Sign in with SSO` |
| `AMNEZIA_FELLOW_OIDC_ADMIN_GROUPS` | Comma-separated admin groups | empty |
| `AMNEZIA_FELLOW_OIDC_CLIENT_GROUPS` | Comma-separated client groups | empty |
| `AMNEZIA_FELLOW_K8S_NAMESPACE` | Amnezia namespace | `amnezia` |
| `AMNEZIA_FELLOW_K8S_CLIENTS_SECRET` | Client peers Secret | `amneziawg-clients` |
| `AMNEZIA_FELLOW_K8S_CLIENTS_SECRET_KEY` | Secret data key for rendered peers | `peers.conf` |
| `AMNEZIA_FELLOW_K8S_SERVER_SECRET` | Server config Secret | `amneziawg-server` |
| `AMNEZIA_FELLOW_K8S_ENDPOINTS_SECRET` | Node endpoints Secret | `amneziawg-endpoints` |
| `AMNEZIA_FELLOW_VPN_CLIENT_CIDR` | Client address pool | `10.8.0.0/16` |
| `AMNEZIA_FELLOW_VPN_DNS` | DNS servers in generated configs | `1.1.1.1, 8.8.8.8` |
| `AMNEZIA_FELLOW_VPN_MTU` | MTU in generated configs | `1376` |
| `AMNEZIA_FELLOW_SWAGGER_ENABLED` | Serve Swagger UI at `/swagger/` | `false` |
2026-06-30 18:36:53 +03:00
| `AMNEZIA_FELLOW_TELEGRAM_BOT_ENABLED` | Enable Telegram bot/Web App integration | `false` |
| `AMNEZIA_FELLOW_TELEGRAM_BOT_USERNAME` | Telegram bot username, with or without `@` | empty |
| `AMNEZIA_FELLOW_TELEGRAM_BOT_TOKEN` | Telegram bot token used to verify Web App `initData` | empty |
## Telegram Bot
Configure the bot through the admin Settings page or the matching environment variables:
```bash
AMNEZIA_FELLOW_TELEGRAM_BOT_ENABLED=true
AMNEZIA_FELLOW_TELEGRAM_BOT_USERNAME=<BOT_USERNAME>
AMNEZIA_FELLOW_TELEGRAM_BOT_TOKEN=<BOT_TOKEN>
```
In BotFather, create or reuse the same bot and set its Web App URL to
`https://<APP_HOST>/configs`. The bot should reply with the sender's numeric
Telegram ID, and users must open the bot and send `/start` once before
notifications can be delivered.
2026-06-29 15:50:25 +03:00
## API
The JSON API is session-authenticated:
- `GET /api/me`
- `GET /api/vpn-clients`
- `GET /api/vpn-status`
- `POST /api/vpn-clients`
- `POST /api/vpn-clients/{id}/enabled`
- `DELETE /api/vpn-clients/{id}`
- `GET /api/vpn-clients/{id}/config` returns `servers[]` with one raw AWG config and one Amnezia `vpn://` import link per registered endpoint
- `POST /api/vpn-clients/sync`
## Development
```bash
cargo fmt
cargo check
```
The project depends on public crates only; `cot = "0.6.0"` is pulled from crates.io.