Skip to content

Commit 7e96ac7

Browse files
kennytmmhilsautofix-ci[bot]
authored
tun: allow using a pre-configured persistent tun interface (#262)
* tun: allow using a pre-configured persistent tun interface * simplify logic * [autofix.ci] apply automated fixes --------- Co-authored-by: Maximilian Hils <[email protected]> Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
1 parent 6fa311d commit 7e96ac7

File tree

2 files changed

+33
-7
lines changed

2 files changed

+33
-7
lines changed

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: 32 additions & 7 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;
@@ -54,14 +54,40 @@ pub fn create_tun_device(tun_name: Option<String>) -> Result<(tun::AsyncDevice,
5454
// config.netmask("0.0.0.0");
5555
// config.destination("169.254.0.1");
5656
config.up();
57-
if let Some(tun_name) = tun_name {
58-
config.tun_name(&tun_name);
57+
if let Some(tun_name) = &tun_name {
58+
config.tun_name(tun_name);
5959
}
6060

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

64-
if let Err(e) = disable_rp_filter(&tun_name) {
89+
fn configure_device(tun_name: &str) {
90+
if let Err(e) = disable_rp_filter(tun_name) {
6591
log::error!("failed to set rp_filter: {e}");
6692
}
6793
if let Err(e) = fs::write(
@@ -78,7 +104,6 @@ pub fn create_tun_device(tun_name: Option<String>) -> Result<(tun::AsyncDevice,
78104
) {
79105
log::error!("Failed to enable accept_local: {e}");
80106
}
81-
Ok((device, tun_name))
82107
}
83108

84109
pub struct TunTask {

0 commit comments

Comments
 (0)