{"id":49546390,"url":"https://github.com/sqdzy/awg-rest","last_synced_at":"2026-05-26T23:06:27.325Z","repository":{"id":355085698,"uuid":"1226693389","full_name":"sqdzy/awg-rest","owner":"sqdzy","description":"Internal all-in-one REST control plane for AmneziaWG V2 peer provisioning with Go, embedded Postgres, Docker Compose, OpenAPI, and GHCR.","archived":false,"fork":false,"pushed_at":"2026-05-26T18:21:01.000Z","size":196,"stargazers_count":2,"open_issues_count":3,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-05-26T20:04:57.637Z","etag":null,"topics":["amneziawg","control-plane","docker","ghcr","go","openapi","postgres","rest-api","vpn","wireguard"],"latest_commit_sha":null,"homepage":null,"language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/sqdzy.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":"AGENTS.md","dco":null,"cla":null}},"created_at":"2026-05-01T18:10:03.000Z","updated_at":"2026-05-26T18:18:20.000Z","dependencies_parsed_at":null,"dependency_job_id":"b03b5dcf-bbce-4096-97f4-78096b6f0a23","html_url":"https://github.com/sqdzy/awg-rest","commit_stats":null,"previous_names":["sqdzy/awg-rest"],"tags_count":15,"template":false,"template_full_name":null,"purl":"pkg:github/sqdzy/awg-rest","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sqdzy%2Fawg-rest","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sqdzy%2Fawg-rest/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sqdzy%2Fawg-rest/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sqdzy%2Fawg-rest/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/sqdzy","download_url":"https://codeload.github.com/sqdzy/awg-rest/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sqdzy%2Fawg-rest/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":33542402,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"ssl_error","status_checked_at":"2026-05-26T15:22:15.568Z","response_time":63,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["amneziawg","control-plane","docker","ghcr","go","openapi","postgres","rest-api","vpn","wireguard"],"created_at":"2026-05-02T20:00:15.203Z","updated_at":"2026-05-26T23:06:27.320Z","avatar_url":"https://github.com/sqdzy.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# awg-rest\n\nInternal REST API for provisioning AmneziaWG V2 VPN peers from another backend\ncontainer.\n\n`awg-rest` is designed for a single private control-plane path:\n\n```text\nyour backend container -\u003e awg-rest REST API -\u003e embedded Postgres -\u003e embedded worker -\u003e awg/amneziawg-go\n```\n\nThe default distribution is an all-in-one Docker image. It contains the API,\nembedded worker, PostgreSQL, `amneziawg-tools`, and `amneziawg-go` userspace\nfallback, so the host does not need an AmneziaWG kernel module.\n\nDo not expose the REST API to the public internet. Publish only the VPN UDP\nport.\n\n## Requirements\n\n- Linux VPS with Docker Engine and Docker Compose.\n- `/dev/net/tun` available on the host.\n- UDP port `38823` open in the VPS firewall, or the port you configure.\n- Public IP or DNS name for generated client configs.\n\nThe all-in-one image still needs `NET_ADMIN` and `/dev/net/tun` because the VPN\ninterface is created inside the container.\n\n## Deploy\n\nClone the repository or place `compose.yaml` and `.env` in an empty directory:\n\n```bash\ncp .env.example .env\n```\n\nEdit `.env`:\n\n```dotenv\nBOOTSTRAP_NODE_ENDPOINT=203.0.113.10\nJWT_SECRET=\u003coutput-of-openssl-rand-base64-32\u003e\nCLIENT_DNS=1.1.1.1,1.0.0.1\nAWG_API_BIND=127.0.0.1:18080\nAWG_UDP_BIND=38823\nAWG_UDP_PORT=38823\n```\n\nGenerate `JWT_SECRET` with:\n\n```bash\nopenssl rand -base64 32\n```\n\nStart the stack:\n\n```bash\ndocker compose up -d\n```\n\nWhat starts:\n\n- `awg-rest` on the internal Docker network `awg-backend-internal`\n- REST API on `http://awg-rest:18080` inside Docker\n- optional host-local API access on `127.0.0.1:18080`\n- VPN UDP listener on `38823/udp`\n- persistent volumes `awg-postgres` and `awg-state`\n\nOn first start, the container creates:\n\n- tenant `default`\n- AmneziaWG V2 profile `default-v2`\n- node `awg-node-1`\n- address pool `10.200.0.0/24`\n- server private key and bootstrap interface config in `awg-state`\n\nThe server uses the first usable IPv4 address in the pool, so the default\nserver address is `10.200.0.1/24` and generated peers start from\n`10.200.0.2/32`.\n\nThe default `default-v2` profile uses AmneziaWG V2 parameters compatible with\ncurrent AmneziaVPN clients, including `Jmin=10`, `Jmax=50`, `S1-S4`, and\nnon-overlapping `H1-H4` ranges below `2147483647`. It also renders the same\ndefault `I1` special junk packet used by the Amnezia client for new AWG\nprofiles.\n\nBack up both Docker volumes. `awg-state` contains the server private key.\n\n## Connect Your Backend\n\nAttach your backend container to the same Docker network:\n\n```yaml\nservices:\n  backend:\n    image: your-backend-image\n    networks:\n      - awg-backend-internal\n\nnetworks:\n  awg-backend-internal:\n    external: true\n```\n\nUse this base URL from the backend container:\n\n```text\nhttp://awg-rest:18080\n```\n\nIf the backend runs in the same compose project, it can also use the alias:\n\n```text\nhttp://awg-api:18080\n```\n\n## Authentication\n\nEvery protected API call needs:\n\n```http\nAuthorization: Bearer \u003cJWT\u003e\n```\n\nDefault all-in-one auth uses `HS256` with `JWT_SECRET`. Your backend signs JWTs\nwith the same secret and these claims:\n\n- `iss`: value of `JWT_ISSUER`\n- `aud`: value of `JWT_AUDIENCE`\n- `exp`, `nbf`, `iat`\n- `roles`: include `platform_admin`, `tenant_admin`, or `automation_client`\n\nFor tenant-scoped roles, include `tenant_id`. `platform_admin` can access all\ntenants.\n\nFor a local smoke test, generate a temporary admin token inside the container:\n\n```bash\nJWT=\"$(docker compose exec -T awg-rest /awg-api -dev-token)\"\n```\n\n## API Usage\n\nCreate a peer:\n\n```bash\ncurl -sS -X POST \"http://127.0.0.1:18080/v1/tenants/default/peers\" \\\n  -H \"Authorization: Bearer $JWT\" \\\n  -H \"Content-Type: application/json\" \\\n  -H \"Idempotency-Key: user-123-create-v1\" \\\n  -d '{\n    \"external_id\": \"user-123\",\n    \"display_name\": \"User 123\",\n    \"profile_name\": \"default-v2\"\n  }'\n```\n\nThe first create response includes one-time secret material:\n\n- `private_key`\n- `client_config`\n- `preshared_key`\n\nStore `client_config` in your backend and deliver it to the user once. It is not\nreturned again on idempotency replay.\n\nPoll the operation:\n\n```bash\ncurl -sS \"http://127.0.0.1:18080/v1/operations/$OPERATION_ID\" \\\n  -H \"Authorization: Bearer $JWT\"\n```\n\nRead public peer metadata:\n\n```bash\ncurl -sS \"http://127.0.0.1:18080/v1/tenants/default/peers/$PEER_ID\" \\\n  -H \"Authorization: Bearer $JWT\"\n```\n\nRender the non-secret client config skeleton:\n\n```bash\ncurl -sS \"http://127.0.0.1:18080/v1/tenants/default/peers/$PEER_ID/configuration\" \\\n  -H \"Authorization: Bearer $JWT\"\n```\n\nRevoke a peer:\n\n```bash\ncurl -sS -X POST \"http://127.0.0.1:18080/v1/tenants/default/peers/$PEER_ID:revoke\" \\\n  -H \"Authorization: Bearer $JWT\" \\\n  -H \"Content-Type: application/json\" \\\n  -H \"Idempotency-Key: user-123-revoke-v1\" \\\n  -d '{\"reason\":\"user disabled\"}'\n```\n\nOpenAPI contract: [`api/openapi.yaml`](api/openapi.yaml).\n\n## Configuration\n\nImportant `.env` values:\n\n| Variable | Meaning |\n| --- | --- |\n| `AWG_REST_IMAGE` | all-in-one image tag |\n| `BOOTSTRAP_NODE_ENDPOINT` | public IP or DNS name placed into client configs |\n| `CLIENT_DNS` | comma-separated DNS servers rendered into generated client configs |\n| `JWT_SECRET` | HMAC signing secret shared only with your backend |\n| `AWG_API_BIND` | host binding for REST API, keep loopback-only |\n| `AWG_UDP_BIND` | host UDP binding for VPN traffic |\n| `AWG_UDP_PORT` | UDP listen port inside client configs |\n| `BOOTSTRAP_POOL_CIDR` | VPN client address pool |\n| `AWG_INTERNAL_NETWORK` | Docker network for backend-to-API traffic |\n\n## Security Notes\n\n- Keep `AWG_API_BIND=127.0.0.1:18080` unless a private reverse proxy or private\n  Docker network protects it.\n- Publish only the configured UDP VPN port to the internet.\n- Rotate `JWT_SECRET` if it was exposed.\n- Back up `awg-state`; losing it loses the server private key.\n- Do not delete volumes unless you want to recreate the VPN node and reissue\n  client configs.\n\n## License\n\nRepository code is MIT licensed. The all-in-one image bundles\n`amneziawg-tools`, which is GPL-2.0-only upstream software.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsqdzy%2Fawg-rest","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsqdzy%2Fawg-rest","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsqdzy%2Fawg-rest/lists"}