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(()); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 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(()); | ||||||
|  |             } | ||||||
|  |             _ => { | ||||||
|  |                 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) | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 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); | ||||||
|  |         () | ||||||
|  |     } | ||||||
| 
 | 
 | ||||||
| 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(()) |     Ok(()) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| fn create_ns_dir() -> Result<()> { |  | ||||||
|     match create_dir(NET_NS_DIR) { |  | ||||||
|         Err(e) if e.kind() != ErrorKind::AlreadyExists => { |  | ||||||
|             Err(e) |  | ||||||
|         }, |  | ||||||
|         _ => { |  | ||||||
|             Ok(()) |  | ||||||
|         }, |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 | 
 | ||||||
| fn bind_ns_file(ns_file: String) -> Result<()> { | // Cargo cult from the definition in rtnetlink
 | ||||||
|     match create_mount(ns_file) { | fn prep_for_fork() -> Result<(), ()> { | ||||||
|         Ok(_mount) => { |     Ok(()) | ||||||
|             Ok(()) |  | ||||||
|         }, |  | ||||||
|         Err(e) => Err(e), |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| 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)?) |  | ||||||
| } | } | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user