try with fork instead of clone and fix wireguard inicialization
This commit is contained in:
		
							parent
							
								
									af6c2027d1
								
							
						
					
					
						commit
						e22e4587a4
					
				
							
								
								
									
										50
									
								
								src/main.rs
									
									
									
									
									
								
							
							
						
						
									
										50
									
								
								src/main.rs
									
									
									
									
									
								
							| @ -2,10 +2,9 @@ mod namespace; | ||||
| mod manage_interfaces; | ||||
| mod wireguard_manager; | ||||
| mod config; | ||||
| use std::io::Result; | ||||
| 
 | ||||
| use rtnetlink::NetworkNamespace; | ||||
| use futures::executor::block_on; | ||||
| use base64::prelude::*; | ||||
| 
 | ||||
| fn main() { | ||||
|     env_logger::Builder::from_default_env() | ||||
| @ -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(); | ||||
|     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(); | ||||
|             }, | ||||
|         &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.endpoint.ip.clone(), | ||||
|                 vpn.prefix as u8, | ||||
| @ -48,35 +52,3 @@ pub fn create_namespace(vpn: config::ConsumableVPNConfig) { | ||||
|         &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 netlink_packet_route::link::LinkMessage; | ||||
| use std::net::IpAddr; | ||||
| use std::os::fd::AsRawFd; | ||||
| use futures::TryStreamExt; | ||||
| 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_ip: String, | ||||
|     peer_ip: String, | ||||
| @ -14,7 +29,7 @@ pub fn create_wireguard_interface( | ||||
|     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?; | ||||
|         //netlink::create_wireguard_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?; | ||||
|         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(()) | ||||
| } | ||||
| 
 | ||||
|  | ||||
| @ -1,43 +1,160 @@ | ||||
| use std::fs::create_dir; | ||||
| use std::io::{Result, ErrorKind}; | ||||
| use std::fs::File; | ||||
| use crate::namespace::consts::{ | ||||
|     NET_NS_DIR, | ||||
|     PROC_NS_DIR | ||||
| }; | ||||
| use nix::mount::{ | ||||
|     mount, | ||||
|     MsFlags | ||||
| }; | ||||
| use nix::sched::{CloneFlags, unshare, setns}; | ||||
| use crate::namespace::consts::NET_NS_DIR; | ||||
| 
 | ||||
| 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(()) | ||||
| use nix::fcntl::{open, OFlag}; | ||||
| use nix::mount::{mount, MsFlags}; | ||||
| use nix::unistd::{fork, ForkResult, Pid}; | ||||
| use nix::sys::wait::{waitpid, WaitStatus}; | ||||
| use nix::sys::stat::Mode; | ||||
| use nix::sys::statvfs::{statvfs, FsFlags}; | ||||
| 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(()); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
| fn create_ns_dir() -> Result<()> { | ||||
|     match create_dir(NET_NS_DIR) { | ||||
|         Err(e) if e.kind() != ErrorKind::AlreadyExists => { | ||||
|             Err(e) | ||||
|         }, | ||||
| } | ||||
| 
 | ||||
| fn run_parent(child: Pid) -> Result<(), ()> { | ||||
|     log::trace!("[Parent] Child PID: {}", child); | ||||
|     match waitpid(child, None) { | ||||
|         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 bind_ns_file(ns_file: String) -> Result<()> { | ||||
|     match create_mount(ns_file) { | ||||
|         Ok(_mount) => { | ||||
|             Ok(()) | ||||
|         }, | ||||
|         Err(e) => Err(e), | ||||
| } | ||||
| 
 | ||||
| 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 create_mount(ns_file: String) -> Result<()> { | ||||
|     Ok(mount::<str, str, str, str>(Some(PROC_NS_DIR), ns_file.as_str(), None, MsFlags::MS_BIND, None)?) | ||||
| pub fn get_ns_fd(ns_name: &String) -> Result<File,()> { | ||||
|     // Open NS path
 | ||||
|     let ns_path = format!("{}/{}", NET_NS_DIR, ns_name); | ||||
|     let mut open_flags = OFlag::empty(); | ||||
|     open_flags.insert(OFlag::O_RDONLY); | ||||
|     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 split_namespace(ns_name: &String) -> Result<(), ()> { | ||||
|     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