A more user-friendly transparent mode, based on WireGuard
28 Oct 2022, Fabio Valentini @decathorpe
mitmproxy 9 ships with a new experimental proxy mode based on WireGuard®. This new mode makes it incredibly easy to set up proxying for other devices in your network, and allows you to only proxy specific apps on Android.
Connecting another device and routing its traffic through mitmproxy becomes as simple as connecting to a WireGuard VPN — either by using the WireGuard configuration file generated by mitmproxy, or by scanning the QR code that is shown in the mitmweb interface:
How it works
The core of this feature is implemented in the mitmproxy-wireguard Python package, which provides a user space implementation of a WireGuard server. It is a new dependency of mitmproxy and ships with the latest release.
Because the WireGuard protocol operates by transmitting IP packets, this also involves running a network stack in user space to parse incoming IP packets, construct outgoing IP packets, handle TCP connections, and process UDP datagrams.
The interfaces which are provided by
mitmproxy_wireguard were designed to match the APIs in the Python standard
asyncio module as closely as possible. In particular, the
TcpStream provided by the package implements all methods of
asyncio.StreamWriter which are expected by mitmproxy, and can be used as a
Oh, and did I mention this already? The
mitmproxy_wireguard project is written in Rust,
utilizing some great Rust libraries to implement this functionality — most notably:
- Tokio, an asynchronous runtime for Rust.
- boringtun, an implementation of the WireGuard protocol.
- smoltcp, a standalone, user space TCP/IP stack.
- PyO3, a project that provides Python ↔ Rust interoperability, including support for bridging
asyncruntimes of the two languages.
- Maturin, a build tool for Python packages that are implemented in Rust.
How to use it
In contrast to the existing
transparent mode, the
wireguard mode requires little to no manual configuration. Running
mitmproxy in this mode should be as simple as specifying
--mode wireguard. By default, mitmproxy will try to load
existing WireGuard encryption keys from
~/.mitmproxy and generate new keys if they don’t exist yet.
As most WireGuard servers, it will listen on 51820/udp by default.
For example, running
mitmweb with the new
wireguard mode and using default settings is as simple as running:
mitmweb --mode wireguard
The mitmweb interface then shows the configuration file and QR code that can be used to connect clients to mitmproxy via the WireGuard tunnel:
Support for this WireGuard mode is still very new, and there are likely still bugs, but it should already work for basic use cases. We’re still collecting feedback (unless you’re reading this in the far future).
There’s also a few things that do not work yet in this mode — in particular, the support for IPv6 is still incomplete, which is why the provided configuration for WireGuard clients will only route IPv4 traffic to mitmproxy.
This project would not have been possible if not for the great ecosystem of existing Rust libraries for asynchronous programming (tokio), low-level networking functionality (boringtun, smoltcp), and bridging the gap between Rust and Python (pyo3, pyo3-log, pyo3-asyncio, maturin).
I would also like to thank Maximilian Hils for his mentorship and for supervising my Bachelor thesis — which, coincidentally, will be about “Implementing a WireGuard frontend for mitmproxy”. 🙃
“WireGuard” and the “WireGuard” logo are registered trademarks of Jason A. Donenfeld.