A more user-friendly transparent mode, based on WireGuard

28 Oct 2022, Fabio Valentini

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:

Setting up an Android smartphone (right) to use mitmweb (left).
The mitmproxy CA certificate has been preinstalled for this demo.

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.


Architecture of the WireGuard server implemented in mitmproxy_wireguard

Architecture of the WireGuard server implemented in mitmproxy_wireguard


The interfaces which are provided by mitmproxy_wireguard were designed to match the APIs in the Python standard library’s asyncio module as closely as possible. In particular, the TcpStream provided by the package implements all methods of asyncio.StreamReader and asyncio.StreamWriter which are expected by mitmproxy, and can be used as a drop-in replacement.

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 async runtimes 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:

WireGuard configuration and scannable QR code as shown by mitmweb

WireGuard configuration and scannable QR code as shown by mitmweb

What’s next?

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.

Acknowledgements

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.