Skip to content

Commit 2b26ddd

Browse files
committed
tun: allow using a pre-configured persistent tun interface
1 parent 6fa311d commit 2b26ddd

2 files changed

Lines changed: 44 additions & 22 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
## Unreleased: mitmproxy_rs next
22

3+
- tun mode: allow using a pre-configured persistent tun interface, to avoid requiring CAP_NET_ADMIN.
34

45
## 29 April 2025: mitmproxy_rs 0.12.3
56

src/packet_sources/tun.rs

Lines changed: 43 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ use crate::packet_sources::{PacketSourceConf, PacketSourceTask};
66
use crate::shutdown;
77
use anyhow::{Context, Result};
88
use std::cmp::max;
9-
use std::fs;
9+
use std::{fs, io::ErrorKind};
1010
use tokio::sync::mpsc::Sender;
1111
use tokio::sync::mpsc::{Permit, Receiver, UnboundedReceiver};
1212
use tun::AbstractDevice;
@@ -47,36 +47,57 @@ impl PacketSourceConf for TunConf {
4747
}
4848

4949
pub fn create_tun_device(tun_name: Option<String>) -> Result<(tun::AsyncDevice, String)> {
50-
let mut config = tun::Configuration::default();
50+
let mut persistent_config = tun::Configuration::default();
51+
if let Some(tun_name) = &tun_name {
52+
persistent_config.tun_name(tun_name);
53+
}
54+
let mut config = persistent_config.clone();
5155
config.mtu(MAX_PACKET_SIZE as u16);
5256
// Setting a local address and a destination is required on Linux.
5357
config.address("169.254.0.1");
5458
// config.netmask("0.0.0.0");
5559
// config.destination("169.254.0.1");
5660
config.up();
57-
if let Some(tun_name) = tun_name {
58-
config.tun_name(&tun_name);
59-
}
6061

61-
let device = tun::create_as_async(&config).context("Failed to create TUN device")?;
62+
let (device_result, is_new_device) = match tun::create_as_async(&config) {
63+
// If we are instructed to create a tun device with a specific name, it is possible that the
64+
// user wants us to reuse an existing persistent tun interface. A persistent tun interface
65+
// is usually pre-configured, so that mitmproxy does not need to perform configuration, and
66+
// therefore does not need CAP_NET_ADMIN or sudo.
67+
//
68+
// The default `config` will set MTU and address etc which *do* require CAP_NET_ADMIN, which
69+
// will result in PermissionDenied in the non-privileged context. To deal with the case of
70+
// pre-configured persistent interface, we retry `create_as_async` without the MTU/address
71+
// settings.
72+
Err(tun::Error::Io(e)) if e.kind() == ErrorKind::PermissionDenied && tun_name.is_some() => {
73+
(tun::create_as_async(&persistent_config), false)
74+
}
75+
result => (result, true),
76+
};
77+
let device = device_result.context("Failed to create TUN device")?;
6278
let tun_name = device.tun_name().context("Failed to get TUN name")?;
6379

64-
if let Err(e) = disable_rp_filter(&tun_name) {
65-
log::error!("failed to set rp_filter: {e}");
66-
}
67-
if let Err(e) = fs::write(
68-
format!("/proc/sys/net/ipv4/conf/{tun_name}/route_localnet"),
69-
"1",
70-
) {
71-
log::error!("Failed to enable route_localnet: {e}");
72-
}
73-
// Update accept_local so that injected packets with a local address (e.g. 127.0.0.1)
74-
// as source address are accepted.
75-
if let Err(e) = fs::write(
76-
format!("/proc/sys/net/ipv4/conf/{tun_name}/accept_local"),
77-
"1",
78-
) {
79-
log::error!("Failed to enable accept_local: {e}");
80+
if is_new_device {
81+
// In case of a persistent tun device, these are supposed to be pre-configured,
82+
// and we should not have permission to write these. So don't try.
83+
84+
if let Err(e) = disable_rp_filter(&tun_name) {
85+
log::error!("failed to set rp_filter: {e}");
86+
}
87+
if let Err(e) = fs::write(
88+
format!("/proc/sys/net/ipv4/conf/{tun_name}/route_localnet"),
89+
"1",
90+
) {
91+
log::error!("Failed to enable route_localnet: {e}");
92+
}
93+
// Update accept_local so that injected packets with a local address (e.g. 127.0.0.1)
94+
// as source address are accepted.
95+
if let Err(e) = fs::write(
96+
format!("/proc/sys/net/ipv4/conf/{tun_name}/accept_local"),
97+
"1",
98+
) {
99+
log::error!("Failed to enable accept_local: {e}");
100+
}
80101
}
81102
Ok((device, tun_name))
82103
}

0 commit comments

Comments
 (0)