|
| 1 | +--- |
| 2 | +title: "Intercepting Linux Applications" |
| 3 | +date: 2025-01-12 |
| 4 | +weight: 10 |
| 5 | +tags: |
| 6 | + - releases |
| 7 | + - local-capture |
| 8 | +authors: |
| 9 | + - maximilian-hils |
| 10 | + - emanuele-micheletti |
| 11 | + - gaurav-jain |
| 12 | + - fabio-valentini |
| 13 | +--- |
| 14 | + |
| 15 | +Following Windows and macOS, mitmproxy's local capture mode is now available on Linux as well! |
| 16 | +mitmproxy 11.1 can seamlessly intercept local applications on all three major platforms. |
| 17 | + |
| 18 | +<!--more--> |
| 19 | + |
| 20 | +## Windows, macOS, ...and now Linux! |
| 21 | + |
| 22 | +After debuting local capture mode for [macOS] |
| 23 | +and [Windows] about a year ago, we're happy to report that our eBPF-based Linux implementation |
| 24 | +is now ready for testing. On all three platforms, users can now intercept specific applications without |
| 25 | +setting system-wide proxy settings. |
| 26 | + |
| 27 | +[macOS]: {{< relref "./macos" >}} |
| 28 | +[Windows]: {{< relref "./windows" >}} |
| 29 | + |
| 30 | +You can try it out as follows: |
| 31 | + |
| 32 | +```shell |
| 33 | +# Capture all local traffic |
| 34 | +mitmproxy --mode local |
| 35 | +# Capture cURL only |
| 36 | +mitmproxy --mode local:curl |
| 37 | +``` |
| 38 | + |
| 39 | +## How it works |
| 40 | + |
| 41 | +mitmproxy's traditional proxying modes (HTTP/SOCKS/reverse proxy) are platform-independent, but |
| 42 | +local capture mode requires a unique approach for every operating system. |
| 43 | +macOS tormented us with hard-to-debug code-signing requirements for system extensions, |
| 44 | +but it provided us with relatively nice [APIs for traffic redirection] in return. |
| 45 | +Windows did not bring such comforts, but it had [external libraries] to make up for it. |
| 46 | +Finally, Linux offered us a plethora of solutions that *almost* met our requirements, but |
| 47 | +most of them turned out to have one or two crucial gaps. |
| 48 | + |
| 49 | +#### Road to eBPF |
| 50 | + |
| 51 | +We initially looked at iptables/nftables to redirect traffic. While both tools work great to capture connections |
| 52 | +for a particular IP address or pid, they fall short of redirecting traffic for a particular |
| 53 | +process name. However, we wanted to capture by process name to have the same capture mechanism across platforms. |
| 54 | +Network namespaces would have been equally wonderful to use, but we found them very hard to package into a |
| 55 | +good user experience. |
| 56 | +To cut a long story short, we eventually decided to write an [eBPF] program to redirect traffic. |
| 57 | + |
| 58 | +eBPF is a technology that allows us to run a program within the Linux kernel at a low level, |
| 59 | +but --- in contrast to a kernel extension --- does not require us to modify the kernel. |
| 60 | +This is promising in theory, but development proved to be harder than expected. Official documentation for eBPF is sparse |
| 61 | +and the kernel's eBPF verifier, which ensures that our program is safe to run, likes to complain in |
| 62 | +mysterious ways. With [kernel/bpf/verifier.c] clocking in at 22.801 lines of C code at the time of writing, making sense |
| 63 | +of errors sometimes isn't easy. Fortunately, tools like [Aya] make eBPF development significantly easier and |
| 64 | +Isovalent's [docs.ebpf.io] fills a lot of the documentation gaps. |
| 65 | + |
| 66 | +#### Redirection |
| 67 | + |
| 68 | +{{< |
| 69 | +figure src="architecture.png" |
| 70 | +caption="mitmproxy_rs architecture" |
| 71 | +width="90%" |
| 72 | +>}} |
| 73 | +
|
| 74 | +To implement the actual redirection, mitmproxy does the following: |
| 75 | + |
| 76 | +1. mitmproxy_rs spawns the redirector as a privileged subprocess with `sudo`. |
| 77 | + This is necessary so that the redirector can invoke the `bpf()` syscall. |
| 78 | +2. The redirector... |
| 79 | + 1. Creates a new virtual network device (typically `/dev/tun0`). |
| 80 | + 2. Loads our [BPF_PROG_TYPE_CGROUP_SOCK] BPF [program]. |
| 81 | + 3. Connects back to mitmproxy_rs to learn which programs to intercept. |
| 82 | + 4. Passes this information to the BPF program via a shared map. |
| 83 | +3. Every time a new socket is created, the BPF program determines whether it should be intercepted. |
| 84 | + If that's the case, it re-routes the socket to mitmproxy by assigning it to our virtual network device. |
| 85 | +4. The redirector now reads all packets from `/dev/tun0` and forwards them to mitmproxy_rs (and vice versa). |
| 86 | +5. mitmproxy_rs reassembles raw packets into TCP/UDP streams using our existing user-space network stack and then |
| 87 | + passes them on to mitmproxy. |
| 88 | + |
| 89 | +This method is considerably more complex than a simple iptables rule, but we gain fine-grained control |
| 90 | +over what to intercept. |
| 91 | + |
| 92 | +[APIs for traffic redirection]: {{< relref "./macos#how-does-it-work" >}} |
| 93 | +[external libraries]: {{< relref "./windows#how-it-works" >}} |
| 94 | +[eBPF]: https://docs.ebpf.io/ |
| 95 | +[WireGuard® mode]: {{< relref "wireguard-mode" >}} |
| 96 | +[WinDivert]: https://github.com/Rubensei/windivert-rust |
| 97 | +[kernel/bpf/verifier.c]: https://github.com/torvalds/linux/blame/master/kernel/bpf/verifier.c |
| 98 | +[Aya]: https://aya-rs.dev/ |
| 99 | +[docs.ebpf.io]: https://docs.ebpf.io/ |
| 100 | +[BPF_PROG_TYPE_CGROUP_SOCK]: https://docs.ebpf.io/linux/program-type/BPF_PROG_TYPE_CGROUP_SOCK/ |
| 101 | +[program]: https://github.com/mitmproxy/mitmproxy_rs/blob/8f9372fc5bde5eb1910fc7e773a60132a17319b5/mitmproxy-linux-ebpf/src/main.rs#L17-L27 |
| 102 | + |
| 103 | +#### Limitations |
| 104 | + |
| 105 | +Local capture mode on Linux comes with a few limitations: |
| 106 | + |
| 107 | +- **Egress only:** mitmproxy will capture outbound connections only. |
| 108 | + For inbound connections, we recommend reverse proxy mode. |
| 109 | +- **Root privileges:** To load the BPF program, mitmproxy needs to spawn a privileged subprocess using `sudo`. |
| 110 | + For the web UI, this means that mitmweb needs to be started directly with `--mode local` on the command line |
| 111 | + to get a sudo password prompt. |
| 112 | +- **Kernel compatibility:** Our eBPF instrumentation requires a reasonably recent kernel. |
| 113 | + We officially support Linux 6.8 and above, which matches Ubuntu 22.04. |
| 114 | +- **Intercept specs:** Program names are matched on the first 16 characters only (based on the kernel's [TASK_COMM_LEN]). |
| 115 | +- **Containers:** Capturing traffic from containers will fail unless they use the host network. |
| 116 | + For example, containers can be started with `docker/podman run --network host`. |
| 117 | +- **Windows Subsystem for Linux (WSL 1/2):** WSL is unsupported as eBPF is disabled by default. |
| 118 | + |
| 119 | + |
| 120 | +[TASK_COMM_LEN]: https://github.com/torvalds/linux/blob/fbfd64d25c7af3b8695201ebc85efe90be28c5a3/include/linux/sched.h#L306 |
| 121 | + |
| 122 | +## Next Steps |
| 123 | + |
| 124 | +Local redirect mode is now available for Windows, Linux, and macOS users in mitmproxy 11.1. |
| 125 | +We have more plans to improve the user experience, |
| 126 | +but we already want to make the current state available for everyone to try. |
| 127 | +If you are curious about contributing, please join us on [GitHub]! |
| 128 | + |
| 129 | +[GitHub]: https://github.com/mitmproxy/mitmproxy/issues/6531 |
| 130 | + |
| 131 | +## Acknowledgments |
| 132 | + |
| 133 | +This work was supported by the [NGI0 Entrust fund] established by [NLnet]. |
| 134 | + |
| 135 | +[NGI0 Entrust fund]: https://nlnet.nl/entrust/ |
| 136 | +[NLnet]: https://nlnet.nl/ |
0 commit comments