157 lines
5.7 KiB
Markdown
157 lines
5.7 KiB
Markdown
# 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` |
|
|
| `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.
|
|
|
|
## 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.
|