WireGuard Configuration API Client
Project description
wg-api-client
Python client library and CLI for the WireGuard Configuration Distribution API. Version 0.2.1.
Installation
Requires Python 3.9 or later.
pip install wg-api-client
From source:
git clone https://github.com/tiiuae/wg-api-client-lib.git
cd wg-api-client-lib
pip install -e .
Key generation requires wireguard-tools:
# Debian / Ubuntu
sudo apt install -y wireguard-tools
Runtime dependency: requests >=2.25.0, <3.0.0.
Configuration
Settings are read from environment variables, CLI flags, or a config file. No credentials or URLs are hardcoded.
Environment variables
| Variable | Required | Description |
|---|---|---|
WG_API_URL |
yes | API base URL (e.g. https://host:8080/api/v1) |
WG_API_CREDENTIAL |
yes | SHA-256 hashed credential |
WG_API_CA_CERT |
no | Path to CA certificate bundle for TLS |
Config file
Stored at ~/.wg_api_config (permissions 0600). Created on
first successful auth. Uses INI format with [General] and
[Auth] sections.
.env.example
Copy .env.example to .env and fill in the values. Generate a
credential hash with:
echo -n "your-secret" | sha256sum | awk '{print $1}'
CLI usage
wg-api-client [global flags] <command> [command flags]
Global flags
| Flag | Env variable | Description |
|---|---|---|
--api-url URL |
WG_API_URL |
API base URL |
--hashed-credential HASH |
WG_API_CREDENTIAL |
Hashed credential |
--ca-cert PATH |
WG_API_CA_CERT |
CA certificate bundle |
--config-file PATH |
Config file (default: ~/.wg_api_config) |
|
--output-format {text,json} |
Output format (default: text) |
Commands
auth
Authenticate and store the JWT token in the config file.
wg-api-client auth
get-config
Register a device and write a WireGuard configuration file.
wg-api-client get-config \
--cluster-id CLUSTER_ID \
--role node \
--output wg.conf
| Flag | Description |
|---|---|
--cluster-id |
Cluster to join (required) |
--role |
Device role: node, controller, or uxu (default: node) |
--device-id |
Custom device ID (auto-generated from hardware if omitted) |
--public-key |
WireGuard public key (generated if omitted) |
--output |
Output file path (default: wg.conf) |
--allowed-ips |
Additional allowed IP ranges (repeatable) |
--table |
Routing table for the WireGuard interface |
--listen-port |
Listen port for the WireGuard interface |
list-devices
List all registered devices (admin).
wg-api-client list-devices
wg-api-client --output-format json list-devices
get-device
Get details for a single device (admin).
wg-api-client get-device DEVICE_ID
delete-device
Delete a device (admin). Supports --dry-run.
wg-api-client delete-device DEVICE_ID --dry-run
wg-api-client delete-device DEVICE_ID
delete-all-devices
Delete all devices (admin). Requires --confirm or interactive
confirmation. Supports --dry-run.
wg-api-client delete-all-devices --dry-run
wg-api-client delete-all-devices --confirm
create-cluster
Create a new cluster (admin).
wg-api-client create-cluster production
wg-api-client --output-format json create-cluster staging
list-clusters
List all clusters (admin).
wg-api-client list-clusters
get-cluster
Get cluster details (admin).
wg-api-client get-cluster CLUSTER_ID
delete-cluster
Delete a cluster and all its devices (admin). Supports
--dry-run.
wg-api-client delete-cluster CLUSTER_ID --dry-run
wg-api-client delete-cluster CLUSTER_ID
get-controller
Get the controller device for a cluster (admin).
wg-api-client get-controller CLUSTER_ID
list-cluster-devices
List all devices in a cluster (admin).
wg-api-client list-cluster-devices CLUSTER_ID
get-uxu
Get the UXU device (admin).
wg-api-client get-uxu
add-credential
Add a new API credential (admin).
wg-api-client add-credential \
--hashed-credential HASH \
--role admin
--role accepts user (default) or admin.
health
Check API server health. Does not require authentication.
wg-api-client health
wg-api-client --output-format json health
version
Get API server version.
wg-api-client version
completion
Generate shell completion scripts.
# bash
eval "$(wg-api-client completion bash)"
# zsh
eval "$(wg-api-client completion zsh)"
Device roles
| Role | Description | Default |
|---|---|---|
node |
Spoke device, can only reach controller | yes |
controller |
Hub device, one per cluster | no |
uxu |
Operator device, global access, unique | no |
Python library
Basic usage
from wg_api_client import WireGuardAPI, WireGuardHelper
from wg_api_client.unique_id import get_unique_device_id
api = WireGuardAPI(
api_url="https://vpn.example.com/api/v1",
hashed_credential="your-sha256-hash",
)
success, message = api.authenticate()
private_key, public_key = WireGuardHelper.generate_keypair()
device_id = get_unique_device_id()
success, config_data = api.request_wireguard_config(
device_id=device_id,
role="node",
public_key=public_key,
cluster_id="your-cluster-id",
)
if success:
WireGuardHelper.create_client_config(
config_data, "wg.conf", private_key=private_key
)
Factory from environment
from wg_api_client import WireGuardAPI
api = WireGuardAPI.from_env()
Reads WG_API_URL (required), WG_API_CREDENTIAL, and
WG_API_CA_CERT from the environment.
Context manager
with WireGuardAPI.from_env() as api:
api.authenticate()
success, devices = api.list_devices()
The session is closed automatically on exit.
Cluster operations
success, data = api.create_cluster("production")
cluster_id = data["id"]
success, clusters = api.list_clusters()
success, devices = api.list_cluster_devices(cluster_id)
success, controller = api.get_cluster_controller(cluster_id)
success, message = api.delete_cluster(cluster_id)
Admin operations
success, devices = api.list_devices()
success, device = api.get_device("device-id")
success, message = api.delete_device("device-id")
success, uxu = api.get_uxu_device()
success, message = api.add_credential("sha256-hash", "admin")
API reference
WireGuardAPI
Constructor parameters:
| Parameter | Type | Default | Description |
|---|---|---|---|
api_url |
str |
(required) | Base URL for the API |
hashed_credential |
str |
None |
Pre-hashed credential |
token |
str |
None |
Existing JWT token |
verify |
bool | str |
True |
TLS verify flag or CA bundle path |
timeout |
int |
10 |
Request timeout in seconds |
max_retries |
int |
3 |
Retries on 502/503/504 |
backoff_factor |
float |
0.5 |
Exponential backoff factor |
Methods:
| Method | Returns | Pagination | Auth | Description |
|---|---|---|---|---|
health_check() |
InfoResponse |
no | none | Check server health |
get_version() |
InfoResponse |
no | none | Get server version |
authenticate() |
MessageResponse |
no | cred | Login, obtain JWT |
refresh_auth_token() |
MessageResponse |
no | token | Refresh JWT |
ensure_authenticated() |
bool |
no | auto | Auto-refresh if needed |
request_wireguard_config(device_id, role, public_key, cluster_id) |
DeviceResponse |
no | JWT | Register device, get config |
list_devices(limit, offset) |
ListResponse |
yes | admin | List all devices |
get_device(device_id) |
DeviceResponse |
no | admin | Get device details |
delete_device(device_id) |
MessageResponse |
no | admin | Delete a device |
delete_all_devices() |
MessageResponse |
no | admin | Delete all devices |
create_cluster(name) |
ClusterResponse |
no | admin | Create a cluster |
list_clusters(limit, offset) |
ListResponse |
yes | admin | List clusters |
get_cluster(cluster_id) |
ClusterResponse |
no | admin | Get cluster details |
delete_cluster(cluster_id) |
MessageResponse |
no | admin | Delete cluster + devices |
get_cluster_controller(cluster_id) |
DeviceResponse |
no | admin | Get cluster controller |
list_cluster_devices(cluster_id, limit, offset) |
ListResponse |
yes | admin | List devices in cluster |
get_uxu_device() |
DeviceResponse |
no | admin | Get UXU device |
add_credential(hashed_credential, role) |
MessageResponse |
no | admin | Add API credential |
from_env() |
WireGuardAPI |
-- | -- | Class method: create from env vars |
close() |
None |
-- | -- | Close HTTP session |
WireGuardHelper
| Method | Description |
|---|---|
generate_keypair() |
Generate a WireGuard private/public key pair. Returns (private_key, public_key). |
create_client_config(config_data, output_file, additional_allowed_ips, table, listen_port, private_key) |
Write a WireGuard config file with 0600 permissions. Validates IP format, key length, endpoint format, and path traversal. |
ConfigManager
| Method | Description |
|---|---|
ConfigManager(config_file) |
Constructor. Default path: ~/.wg_api_config. |
load_config() |
Load config from file. |
save_config() |
Save config to file with 0600 permissions. |
get_api_url() / set_api_url(url) |
Read/write API URL. |
get_hashed_credential() / set_hashed_credential(c) |
Read/write credential. |
get_token() / set_token(token) |
Read/write JWT token. |
get_refresh_token() / set_refresh_token(token) |
Read/write refresh token. |
get_token_expires_at() / set_token_expires_at(ts) |
Read/write token expiry timestamp. |
Exception hierarchy
WireGuardAPIError
+-- AuthenticationError
+-- APIConnectionError
+-- TLSError
+-- APITimeoutError
All exceptions accept message and original_error parameters.
The base WireGuardAPIError stores the original exception as
original_error.
Type aliases
Defined in wg_api_client.types:
| Alias | Definition | Used by |
|---|---|---|
InfoResponse |
Tuple[bool, Dict[str, Any]] |
health_check, get_version |
MessageResponse |
Tuple[bool, str] |
authenticate, delete_device, add_credential, etc. |
DeviceResponse |
Tuple[bool, Dict[str, Any]] |
request_wireguard_config, get_device, etc. |
ListResponse |
Tuple[bool, List[Dict[str, Any]]] |
list_devices, list_clusters, list_cluster_devices |
ClusterResponse |
Tuple[bool, Dict[str, Any]] |
create_cluster, get_cluster |
Security
- No hardcoded credentials or API URLs.
- Credentials accepted via environment variable (not visible in process listings when using env vars).
- TLS verification enabled by default; custom CA bundle supported
via
--ca-certorWG_API_CA_CERT. - Config and key files written atomically with
0600permissions. - Private keys passed explicitly through the call chain, not stored as class-level state.
- API response data (IP addresses, public keys, endpoints) validated before writing config files.
- URL path parameters are percent-encoded.
- SSL errors are classified into specific, actionable messages.
- Retry with exponential backoff on transient HTTP errors (502, 503, 504).
- Output path validated against directory traversal.
Development
Install dev dependencies:
pip install -r requirements-dev.txt
pip install -e .
Test
pytest -v
Lint
black --check wg_api_client/ tests/
isort --check wg_api_client/ tests/
flake8 wg_api_client/ tests/ --max-line-length 120
bandit -r wg_api_client/ -c pyproject.toml
CI runs lint, tests (Python 3.9--3.12), build verification, and publishes to PyPI on version tags.
License
Apache 2.0
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file wg_api_client-0.2.1.tar.gz.
File metadata
- Download URL: wg_api_client-0.2.1.tar.gz
- Upload date:
- Size: 32.5 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.11.15
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
0ba74ea8c7990ce7e87f4560c438fd358e98cae94ecca2be64817aa12822308c
|
|
| MD5 |
63377ef3f9acfa8c852b8c80962de164
|
|
| BLAKE2b-256 |
7f777983781aad76ca2357434cd18e08f3abbf500867ab81e87b5f6eac63e261
|
File details
Details for the file wg_api_client-0.2.1-py3-none-any.whl.
File metadata
- Download URL: wg_api_client-0.2.1-py3-none-any.whl
- Upload date:
- Size: 32.9 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.11.15
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
d250e58aa6cb24c94aa75f7f80d01b3a6373637e508f6f95504df09f0982a74a
|
|
| MD5 |
04b67874d3d763f14d29e5118f4f75c8
|
|
| BLAKE2b-256 |
662bde5304bf7bd4ca3209e5493905d2908cc3c49d93796bf883a7292dd44111
|