A framework for black-box security testing of WireGuard implementations. Individual test cases are in test_cases/. See also the paper.
# Install dependencies
uv sync
# Run tests against a target
sudo uv run run_tests.py wireguard-go-dockerThe framework itself does not require special privileges, but reset.sh often does, for example, to configure network interfaces or manage containers. Running with sudo ensures reset.sh has the necessary permissions.
The configuration for each WireGuard implementation under test lives in targets/<name>/ and contains:
| File | Purpose |
|---|---|
target.py |
TestTarget instance describing the test setup configuration |
reset.sh |
Script executed before each test run to reset the target to a clean state |
teardown.sh |
(optional) Script executed after all tests complete, e.g. to stop a Docker container |
The test host requires two IP addresses (server_physical_ip_1 and server_physical_ip_2) that can both reach the target at target_physical_ip:target_wg_port. The two host addresses are used to simulate endpoint changes in the roaming tests.
The target must run WireGuard with a single configured peer, using the configuration defined in target.py.
The target must run a UDP echo service that:
- Listens on all interfaces (
target_physical_ip,target_wg_ipandlocalhost) onecho_port - Echoes every received UDP packet back to
server_wg_ip:echo_portthrough the WireGuard tunnel
The echo service always replies via the tunnel regardless of which interface the probe arrived on. When the test host sends a probe to target_physical_ip:echo_port on the physical interface and no tunnel session exists yet, the target must establish a handshake in order to deliver the reply , this is the mechanism used to trigger the target to act as the WireGuard initiator.
Ready-to-use implementations are in targets/shared/ (udp_echo.c for Linux/BSD, udp_echo.ps1 for Windows).
- Create
targets/<name>/and copy an existingtarget.pyas a starting point. - Fill in
target.pywith the network addresses, ports, and key pairs for the target. - Write
reset.sh, it must reset the existing WireGuard configuration, ensure the echo service is running, and exit 0 on success. Two common approaches:- VM snapshots: take a snapshot after applying the WireGuard configuration from
target.pyand starting the echo service, then havereset.shrestore that snapshot. - Docker: run the target in a container and have
reset.shrestart it. See the existing Docker targets intargets/for examples.
- VM snapshots: take a snapshot after applying the WireGuard configuration from
- Optionally write
teardown.sh, called once after all tests finish. Setteardown_scriptintarget.pyto point to it. Useful for stopping containers or other cleanup that should happen at the end. - Run the tests:
sudo uv run run_tests.py <name>.