try with fork instead of clone and fix wireguard inicialization
This commit is contained in:
parent
af6c2027d1
commit
e22e4587a4
52
src/main.rs
52
src/main.rs
@ -2,12 +2,11 @@ mod namespace;
|
|||||||
mod manage_interfaces;
|
mod manage_interfaces;
|
||||||
mod wireguard_manager;
|
mod wireguard_manager;
|
||||||
mod config;
|
mod config;
|
||||||
use std::io::Result;
|
|
||||||
use rtnetlink::NetworkNamespace;
|
use rtnetlink::NetworkNamespace;
|
||||||
use futures::executor::block_on;
|
use futures::executor::block_on;
|
||||||
use base64::prelude::*;
|
|
||||||
|
|
||||||
fn main(){
|
fn main() {
|
||||||
env_logger::Builder::from_default_env()
|
env_logger::Builder::from_default_env()
|
||||||
.format_timestamp_secs()
|
.format_timestamp_secs()
|
||||||
.filter(None, log::LevelFilter::Debug)
|
.filter(None, log::LevelFilter::Debug)
|
||||||
@ -27,18 +26,23 @@ fn main(){
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
_ => {},
|
_ => {},
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn create_namespace(vpn: config::ConsumableVPNConfig) {
|
fn create_namespace(vpn: config::ConsumableVPNConfig) {
|
||||||
let ns_name = vpn.namespace_name.clone();
|
let ns_name = vpn.namespace_name.clone();
|
||||||
block_on(NetworkNamespace::add(ns_name.clone())).unwrap();
|
block_on(NetworkNamespace::add(ns_name.clone())).unwrap();
|
||||||
namespace::bind_interface::run_in_namespace(|| {
|
namespace::create_ns::run_in_namespace(|| {
|
||||||
manage_interfaces::set_interface_lo_up().unwrap();
|
manage_interfaces::set_interface_lo_up().unwrap();
|
||||||
},
|
},
|
||||||
&ns_name).unwrap();
|
&ns_name).unwrap();
|
||||||
namespace::bind_interface::run_in_namespace(|| {
|
|
||||||
manage_interfaces::create_wireguard_interface(vpn.interface_name.clone(),
|
manage_interfaces::create_and_move_wireguard_interface(
|
||||||
|
vpn.interface_name.clone(),
|
||||||
|
ns_name.clone()
|
||||||
|
).unwrap();
|
||||||
|
namespace::create_ns::run_in_namespace(|| {
|
||||||
|
manage_interfaces::configure_wireguard_interface(vpn.interface_name.clone(),
|
||||||
vpn.ip.clone(),
|
vpn.ip.clone(),
|
||||||
vpn.endpoint.ip.clone(),
|
vpn.endpoint.ip.clone(),
|
||||||
vpn.prefix as u8,
|
vpn.prefix as u8,
|
||||||
@ -48,35 +52,3 @@ pub fn create_namespace(vpn: config::ConsumableVPNConfig) {
|
|||||||
&ns_name).unwrap();
|
&ns_name).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
/*fn main2() {
|
|
||||||
env_logger::Builder::from_default_env()
|
|
||||||
.format_timestamp_secs()
|
|
||||||
.filter(None, log::LevelFilter::Debug)
|
|
||||||
.init();
|
|
||||||
|
|
||||||
let priv_key_dirty = BASE64_STANDARD.decode(b"k1").unwrap();
|
|
||||||
let pub_key_dirty = BASE64_STANDARD.decode(b"k2").unwrap();
|
|
||||||
let mut priv_key: [u8; 32] = Default::default();
|
|
||||||
let mut pub_key: [u8; 32] = Default::default();
|
|
||||||
priv_key.copy_from_slice(&priv_key_dirty[0..32]);
|
|
||||||
pub_key.copy_from_slice(&pub_key_dirty[0..32]);
|
|
||||||
|
|
||||||
//namespace::create_ns::create_ns();
|
|
||||||
let ns_name = "test-newns".to_string();
|
|
||||||
block_on(NetworkNamespace::add(ns_name.clone())).unwrap();
|
|
||||||
namespace::bind_interface::run_in_namespace(|| {
|
|
||||||
manage_interfaces::set_interface_lo_up().unwrap();
|
|
||||||
},
|
|
||||||
&ns_name).unwrap();
|
|
||||||
namespace::bind_interface::run_in_namespace(|| {
|
|
||||||
manage_interfaces::create_wireguard_interface(String::from("wgzurich"),
|
|
||||||
String::from("ip1"),
|
|
||||||
String::from("ip2"),
|
|
||||||
24,
|
|
||||||
pub_key,
|
|
||||||
priv_key).unwrap();
|
|
||||||
},
|
|
||||||
&ns_name).unwrap();
|
|
||||||
//println!("{}",wireguard_manager::add_properties::set_params(pub_key, priv_key))
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
@ -1,10 +1,25 @@
|
|||||||
use rtnetlink::{new_connection, Error, Handle};
|
use rtnetlink::{new_connection, Error, Handle};
|
||||||
use netlink_packet_route::link::LinkMessage;
|
use netlink_packet_route::link::LinkMessage;
|
||||||
use std::net::IpAddr;
|
use std::net::IpAddr;
|
||||||
|
use std::os::fd::AsRawFd;
|
||||||
use futures::TryStreamExt;
|
use futures::TryStreamExt;
|
||||||
mod netlink;
|
mod netlink;
|
||||||
|
|
||||||
pub fn create_wireguard_interface(
|
pub fn create_and_move_wireguard_interface(
|
||||||
|
interface_name: String,
|
||||||
|
namespace_name: String,
|
||||||
|
) -> Result<(), Error> {
|
||||||
|
tokio::runtime::Runtime::new().unwrap().handle().block_on( async {
|
||||||
|
let (connection, handle, _) = new_connection().unwrap();
|
||||||
|
tokio::spawn(connection);
|
||||||
|
netlink::create_wireguard_interface(handle.clone(), interface_name.clone()).await?;
|
||||||
|
let link = netlink::get_link_interface(handle.clone(), interface_name.clone()).await?;
|
||||||
|
handle.link().set(link.header.index).setns_by_fd(crate::namespace::create_ns::get_ns_fd(&namespace_name).unwrap().as_raw_fd()).execute().await?;
|
||||||
|
Ok(())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn configure_wireguard_interface(
|
||||||
interface_name: String,
|
interface_name: String,
|
||||||
interface_ip: String,
|
interface_ip: String,
|
||||||
peer_ip: String,
|
peer_ip: String,
|
||||||
@ -14,7 +29,7 @@ pub fn create_wireguard_interface(
|
|||||||
tokio::runtime::Runtime::new().unwrap().handle().block_on( async {
|
tokio::runtime::Runtime::new().unwrap().handle().block_on( async {
|
||||||
let (connection, handle, _) = new_connection().unwrap();
|
let (connection, handle, _) = new_connection().unwrap();
|
||||||
tokio::spawn(connection);
|
tokio::spawn(connection);
|
||||||
netlink::create_wireguard_interface(handle.clone(), interface_name.clone()).await?;
|
//netlink::create_wireguard_interface(handle.clone(), interface_name.clone()).await?;
|
||||||
let link = netlink::get_link_interface(handle.clone(), interface_name.clone()).await?;
|
let link = netlink::get_link_interface(handle.clone(), interface_name.clone()).await?;
|
||||||
netlink::assign_ip(handle.clone(), link.clone(), interface_ip, prefix).await?;
|
netlink::assign_ip(handle.clone(), link.clone(), interface_ip, prefix).await?;
|
||||||
crate::wireguard_manager::add_properties::set_params(wg_pub_key, wg_priv_key, peer_ip, interface_name.clone());
|
crate::wireguard_manager::add_properties::set_params(wg_pub_key, wg_priv_key, peer_ip, interface_name.clone());
|
||||||
@ -93,3 +108,4 @@ pub fn get_inferfaces() -> Result<(), Error> {
|
|||||||
});
|
});
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,43 +1,160 @@
|
|||||||
use std::fs::create_dir;
|
use nix::sched::{CloneFlags, unshare, setns};
|
||||||
use std::io::{Result, ErrorKind};
|
use crate::namespace::consts::NET_NS_DIR;
|
||||||
use std::fs::File;
|
|
||||||
use crate::namespace::consts::{
|
use nix::fcntl::{open, OFlag};
|
||||||
NET_NS_DIR,
|
use nix::mount::{mount, MsFlags};
|
||||||
PROC_NS_DIR
|
use nix::unistd::{fork, ForkResult, Pid};
|
||||||
};
|
use nix::sys::wait::{waitpid, WaitStatus};
|
||||||
use nix::mount::{
|
use nix::sys::stat::Mode;
|
||||||
mount,
|
use nix::sys::statvfs::{statvfs, FsFlags};
|
||||||
MsFlags
|
use std::os::unix::io::RawFd;
|
||||||
};
|
|
||||||
|
use std::path::{Path, PathBuf};
|
||||||
|
use std::process::exit;
|
||||||
|
use std::fs::{File, OpenOptions};
|
||||||
|
use std::os::fd::FromRawFd;
|
||||||
|
|
||||||
|
pub fn run_in_namespace<F>(f: F,ns_name: &String) -> Result<(), ()> where F:FnMut() + Copy {
|
||||||
|
prep_for_fork()?;
|
||||||
|
// Configure networking in the child namespace:
|
||||||
|
// Fork a process that is set to the newly created namespace
|
||||||
|
// Here set the veth ip addr, routing tables etc.
|
||||||
|
// Unfortunately the NetworkNamespace interface of rtnetlink does
|
||||||
|
// not offer these functionalities
|
||||||
|
match unsafe { fork() } {
|
||||||
|
Ok(ForkResult::Parent { child, .. }) => {
|
||||||
|
// Parent process
|
||||||
|
log::debug!("Net configuration PID: {}", child.as_raw());
|
||||||
|
run_parent(child)
|
||||||
|
}
|
||||||
|
Ok(ForkResult::Child) => {
|
||||||
|
// Child process
|
||||||
|
// Move the child to the target namespace
|
||||||
|
run_child(f, ns_name)
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
log::error!("Can not fork() for ns creation: {}", e);
|
||||||
|
return Err(());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn create_ns() -> Result<()> {
|
|
||||||
create_ns_dir()?;
|
|
||||||
let nsdir = format!("{}/{}",NET_NS_DIR,"fake-net");
|
|
||||||
File::create(nsdir.clone())?;
|
|
||||||
bind_ns_file(nsdir)?;
|
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create_ns_dir() -> Result<()> {
|
fn run_parent(child: Pid) -> Result<(), ()> {
|
||||||
match create_dir(NET_NS_DIR) {
|
log::trace!("[Parent] Child PID: {}", child);
|
||||||
Err(e) if e.kind() != ErrorKind::AlreadyExists => {
|
match waitpid(child, None) {
|
||||||
Err(e)
|
Ok(wait_status) => match wait_status {
|
||||||
},
|
WaitStatus::Exited(_, res) => {
|
||||||
|
log::trace!("Child exited with: {}", res);
|
||||||
|
if res == 0 {
|
||||||
|
return Ok(());
|
||||||
|
} else {
|
||||||
|
log::error!("Child exited with status {}", res);
|
||||||
|
return Err(());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
WaitStatus::Signaled(_, signal, coredump) => {
|
||||||
|
log::error!("Child process killed by signal");
|
||||||
|
return Err(());
|
||||||
|
}
|
||||||
_ => {
|
_ => {
|
||||||
Ok(())
|
log::error!("Unknown child process status: {:?}", wait_status);
|
||||||
},
|
return Err(());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
log::error!("wait error : {}", e);
|
||||||
|
return Err(());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run_child<F>(mut f: F, ns_name: &String) -> Result<(), ()> where F:FnMut() {
|
||||||
|
let res = split_namespace(ns_name);
|
||||||
|
|
||||||
|
match res {
|
||||||
|
Err(_) => {
|
||||||
|
log::error!("Child process crashed");
|
||||||
|
std::process::abort()
|
||||||
|
}
|
||||||
|
Ok(()) => {
|
||||||
|
log::debug!("Child exited normally");
|
||||||
|
f();
|
||||||
|
exit(0)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn bind_ns_file(ns_file: String) -> Result<()> {
|
pub fn get_ns_fd(ns_name: &String) -> Result<File,()> {
|
||||||
match create_mount(ns_file) {
|
// Open NS path
|
||||||
Ok(_mount) => {
|
let ns_path = format!("{}/{}", NET_NS_DIR, ns_name);
|
||||||
Ok(())
|
let mut open_flags = OFlag::empty();
|
||||||
},
|
open_flags.insert(OFlag::O_RDONLY);
|
||||||
Err(e) => Err(e),
|
open_flags.insert(OFlag::O_CLOEXEC);
|
||||||
|
|
||||||
|
Ok(match open(Path::new(&ns_path), open_flags, Mode::empty()) {
|
||||||
|
Ok(raw_fd) => unsafe {
|
||||||
|
File::from_raw_fd(raw_fd)
|
||||||
}
|
}
|
||||||
|
Err(e) => {
|
||||||
|
log::error!("Can not open network namespace: {}", e);
|
||||||
|
return Err(());
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create_mount(ns_file: String) -> Result<()> {
|
fn split_namespace(ns_name: &String) -> Result<(), ()> {
|
||||||
Ok(mount::<str, str, str, str>(Some(PROC_NS_DIR), ns_file.as_str(), None, MsFlags::MS_BIND, None)?)
|
let fd = get_ns_fd(ns_name)?;
|
||||||
|
// Switch to network namespace with CLONE_NEWNET
|
||||||
|
if let Err(e) = setns(fd, CloneFlags::CLONE_NEWNET) {
|
||||||
|
log::error!("Can not set namespace to target {}: {}", ns_name, e);
|
||||||
|
return Err(());
|
||||||
|
}
|
||||||
|
// unshare with CLONE_NEWNS
|
||||||
|
if let Err(e) = unshare(CloneFlags::CLONE_NEWNS) {
|
||||||
|
log::error!("Can not unshare: {}", e);
|
||||||
|
return Err(());
|
||||||
|
}
|
||||||
|
// mount blind the fs
|
||||||
|
// let's avoid that any mount propagates to the parent process
|
||||||
|
// mount_directory(None, &PathBuf::from("/"), vec![MsFlags::MS_REC, MsFlags::MS_PRIVATE])?;
|
||||||
|
let mut mount_flags = MsFlags::empty();
|
||||||
|
mount_flags.insert(MsFlags::MS_REC);
|
||||||
|
mount_flags.insert(MsFlags::MS_PRIVATE);
|
||||||
|
if let Err(_e) = mount::<PathBuf, PathBuf, str, PathBuf>(None, &PathBuf::from("/"), None, mount_flags, None) {
|
||||||
|
log::error!("Can not remount root directory");
|
||||||
|
()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now unmount /sys
|
||||||
|
let sys_path = PathBuf::from("/sys");
|
||||||
|
mount_flags = MsFlags::empty();
|
||||||
|
// Needed to respect the trait for NixPath
|
||||||
|
let ns_name_path = PathBuf::from(ns_name);
|
||||||
|
|
||||||
|
// TODO do not exit for EINVAL error
|
||||||
|
// unmount_path(&sys_path)?;
|
||||||
|
// consider the case that a sysfs is not present
|
||||||
|
let stat_sys = statvfs(&sys_path)
|
||||||
|
.map_err(|e| {
|
||||||
|
log::error!("Can not stat sys: {}", e);
|
||||||
|
}).unwrap();
|
||||||
|
if stat_sys.flags().contains(FsFlags::ST_RDONLY) {
|
||||||
|
mount_flags.insert(MsFlags::MS_RDONLY);
|
||||||
|
}
|
||||||
|
|
||||||
|
// and remount a version of /sys that describes the network namespace
|
||||||
|
if let Err(e) = mount::<PathBuf, PathBuf, str, PathBuf>(Some(&ns_name_path), &sys_path, Some("sysfs"), mount_flags, None) {
|
||||||
|
log::error!("Can not remount /sys to namespace: {}", e);
|
||||||
|
()
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Cargo cult from the definition in rtnetlink
|
||||||
|
fn prep_for_fork() -> Result<(), ()> {
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user