Files
wireguard-admin/README.md
T
2026-10-26 23:25:13 +02:00

209 lines
11 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# wireguard-admin
This repository contains a root-only native terminal interface for one existing
WireGuard interface and configurable systemd service. It connects the validated
settings loader, byte-preserving server configuration editor, encrypted client
vault, recoverable saved-file persistence, backups, Commit, previewed restore,
explicit restart, read-only runtime statistics, and client file/QR delivery.
The interface keeps draft, saved, and applied states distinct. Commit saves
files; restore replaces saved state; only an explicit, verified restart applies
saved changes to the running interface.
The package requires Rust 1.97.1 or newer and uses the Rust 2024 edition. From
the repository root, run:
```sh
cargo run -- --help
cargo run -- --version
```
`--help` and `--version` work without root, settings, or a terminal. Starting
with no arguments requires root and a suitable interactive terminal. Settings
are read from `wireguard-admin.toml` beside the executable. If terminal checks
or saved-state recovery fail, startup exits before entering raw mode or
performing setup mutations.
## Settings
Settings use TOML in the fixed file `wireguard-admin.toml` beside the
executable. Start from [`config/wireguard-admin.example.toml`](config/wireguard-admin.example.toml).
The required fields are:
- `interface_name`: existing WireGuard interface name (1–15 ASCII characters).
- `server_config_path`: path to the existing server configuration.
- `client_source_directory`: directory containing existing client files.
- `server_endpoint`: hostname or IPv4 address followed by `:port`, or IPv6 in
brackets followed by `:port` (for example, `[2001:db8::2]:51820`).
- `ipv4_allocation_range` and `ipv6_allocation_range`: CIDR ranges of the
matching address family.
- `dns_addresses`: non-empty list of IPv4 and/or IPv6 literal addresses.
- `systemd_unit`: service unit name ending in `.service`.
- `restart_timeout_seconds`: positive integer.
`data_root` is optional. It defaults to `Backup` beside the executable;
current internal data and the encrypted vault are under `data`, and archives
are under `archives`. Relative configured paths, including an explicit
`data_root`, resolve from the executable directory. Absolute paths remain
absolute. Settings validation checks syntax and values only; it does not
require the referenced files, directories, or service to exist and does not
perform DNS lookups.
For installation, backup custody, restart failure handling and manual recovery,
see [`docs/OPERATIONS.md`](docs/OPERATIONS.md). The qualified evidence and the
current host release-candidate assessment are in
[`docs/RELEASE_READINESS.md`](docs/RELEASE_READINESS.md); those records do not
claim a completed systemd VM run or real IPv6 qualification.
## Terminal workflows
The bordered **Keys** panel at the bottom of the screen shows shortcuts for the
current screen and form. On narrow list and details screens it prioritizes
navigation and Help, and indicates when additional actions are omitted; resize
the terminal to see more. Forms, QR disclosure and progress show only their
applicable controls. Press `?` from the list or details screen to open Help,
which contains the complete shortcut reference. The client list supports
arrow-key selection, Enter for details, and `q` to exit. Exit warns when a draft
would be discarded; it never commits implicitly. Uppercase shortcuts are
distinct from lowercase shortcuts (`R` previews restart, while `r` renames;
`B` creates a backup, while `b` lists backups):
| Key | Action |
| --- | --- |
| `n` | Create a client with automatic or manual dual-stack addresses |
| `r` / `d` | Rename or delete the selected supported peer by full public key |
| `p` / `x` | Review or discard the draft |
| `c` | Commit saved files after the mandatory backup |
| `R` | Preview explicit restart; confirm only after reviewing the choice |
| `o` / `O` | First file delivery / password-per-action reissue |
| `b` / `B` | List and preview backups / create a manual backup |
| `s` / `i` | Initial vault setup / import one selected client file |
| `!` | Inspect document and peer anomalies |
Forms use Enter to advance or submit, Tab to change fields/options, and Esc to
cancel. Backup restore previews identify the selected archive and require an
explicit confirmation. Restore does not prompt for the vault password and does
not restart WireGuard. Client configuration output is a disclosure: first
delivery requires a successful Commit in the same session; QR appears only
after file publication. Esc closes and clears a QR disclosure. Help in the
interface contains the full current shortcut list.
## Library configuration API
The library exposes `wireguard_admin::wireguard::ConfigDocument` for UTF-8
configuration bytes supplied by the caller. It retains the source and renders
unmodified input byte-for-byte, including line endings and a missing final
newline. Peer inspection exposes public identities, parsed `AllowedIPs`, spans,
and redacted issues; arbitrary directive values are not included in the view.
The edit methods `add_client`, `delete_client`, `rename_client`, and
`insert_legacy_metadata` return new in-memory documents. They do not read or
write files, save changes, or alter the running VPN. See
[`docs/CONFIG_FORMAT.md`](docs/CONFIG_FORMAT.md) for the managed metadata block
format.
Example:
```rust,ignore
use wireguard_admin::wireguard::ConfigDocument;
let document = ConfigDocument::parse(config_bytes)?;
let updated = document.rename_client(&public_key, "office laptop")?;
let bytes_to_review = updated.render(); // no persistence or VPN operation
```
Runtime and restart behavior has synthetic fixture and fake-adapter coverage;
the accepted Alpine milestone also exercised real IPv4 WireGuard traffic and
the Rustix reader. The Alpine fixture did not use systemd/D-Bus, and the
disposable systemd VM milestone has not been run. Real IPv6 capability and
traffic remain unverified.
## Saved state, Commit, and backups
wireguard_admin::app::Application opens saved files through
wireguard_admin::persistence::SavedStore, stages client and vault changes,
creates manual and mandatory operation backups, and previews/confirms a full
restore. Commit writes saved files only. Restore also changes saved files only;
neither operation reloads or restarts WireGuard. The server file is kept at its
trusted configured path, while internal state and the encrypted vault are kept
under Backup/data by default. Archives are stored under Backup/archives and
contain fixed logical members, not source client files or prior archives.
The persistence journal recovers interrupted file replacement on reopen and
returns a recovery error while earlier work remains unresolved. A confirmed
restart marks the saved generation applied only after a successful service job,
a matching complete runtime observation, and journaled verification-state write.
Restoring a backup does not restore runtime state: the running interface is
unknown until a later explicit restart and verification. Pending deleted-client
addresses remain reserved through Commit, reopen, and restore. See
[docs/BACKUP_FORMAT.md](docs/BACKUP_FORMAT.md) and
[docs/PERSISTENCE.md](docs/PERSISTENCE.md) for archive and recovery details,
and [docs/RUNTIME.md](docs/RUNTIME.md) for observation and restart boundaries.
## Vault and existing-client import library
`wireguard_admin::vault` provides WireGuard key handling, an encrypted versioned
vault bundle, caller-buffer client configuration parsing, and reconciliation
against a supplied server document. The source adapter can prepare an initial
encrypted bundle or explicitly import candidates into an existing prepared
bundle. These operations return in-memory data only: they do not install or save
a vault, edit source files or server configuration, Commit, or touch the VPN.
Directory scans use the resolved `client_source_directory` only. A candidate is
an immediate regular file whose name ends in `.conf` (case-insensitive); the
adapter does not recurse, follow symlinks, or read files larger than 1 MiB. A
scan is limited to 4,096 directory entries, 1,024 candidates, and 32 MiB total
input. Per-file failures are redacted and do not prevent other files from being
examined. Enumeration, read, or limit failures mark the scan incomplete. The
original files remain untouched. Adapter examples and behavior are covered with
synthetic temporary directories in `tests/vault_import.rs`.
## Client delivery and reissue
`Application::prepare_new_client` stages a dual-stack peer and encrypted key.
After a successful `commit`, `deliver_first_issue_file` can publish its first
configuration without a vault password during that same session; a failed file
publication can be retried to a newly selected destination. The first file is
mandatory. `deliver_first_issue_file_and_qr` optionally returns a QR matrix from
the same authorized bytes after file publication. Both success results carry an
activation warning unless this session has verified the exact installed saved
generation and its matching applied peer snapshot; historical verification and
unknown runtime state do not suppress it.
Later delivery uses `Application::reissue_client_file`, which takes the client
public key, a password for that action, a prepared destination, and an optional
QR choice. It freshly unlocks the installed saved vault each time, reads saved
server/settings bytes rather than draft state, preserves validated imported
parameters, and rejects blocked or stale records. It never reads original
plaintext client files as a fallback. The result reports a secret-free failure
stage and warns when the current saved peer set has not been verified as applied
in this live session. Historical verification timestamps do not suppress that
warning after reopen. File output uses mode `0600`, explicit overwrite
confirmation, and separate QR failure reporting. These APIs and the TUI
password prompt/terminal QR display have synthetic fixture coverage. No
production configuration or key is included in the repository.
## Test commands and integration limits
Run the default synthetic workflow and UI checks as an unprivileged user:
```sh
cargo test --locked --test tui_workflow
cargo test --locked tui::
```
The first exercises setup, client staging, Commit, delivery, backup listing,
and restore through the serialized worker with temporary files and a fake
statistics reader. The second covers navigation/forms/rendering, delayed worker
behavior, terminal lifecycle seams, and QR sizing. Neither command starts the
management UI or uses host service/runtime mutation. See
[`docs/TESTING.md`](docs/TESTING.md) for full gates and the isolated Alpine
(M8) and disposable systemd VM (M9) boundaries. An actual PTY smoke run using
synthetic application/runtime adapters is included in `tui_workflow`; it uses
`script` and a fake unavailable-statistics reader, without root or host settings.
It checks an 80×24 to 40×8 to 80×24 resize sequence and terminal cleanup after
`q`. The workflow also checks that Esc clears a returned QR disclosure; the
focused renderer test checks complete quiet-zone fit. Do not use the development
host's settings or VPN as a substitute.
M1–M8 qualification evidence and the remaining M9 limitations are summarized
in [`docs/RELEASE_READINESS.md`](docs/RELEASE_READINESS.md).