Start to build NAT configuration mode
This commit is contained in:
parent
6fdcc8c07c
commit
ed25eed31e
13
virtweb_backend/Cargo.lock
generated
13
virtweb_backend/Cargo.lock
generated
@ -1618,6 +1618,18 @@ dependencies = [
|
||||
"tempfile",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nix"
|
||||
version = "0.27.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2eb04e9c688eff1c89d72b407f168cf79bb9e867a9d3323ed6c01519eb9cc053"
|
||||
dependencies = [
|
||||
"bitflags 2.4.1",
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"memoffset",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ntapi"
|
||||
version = "0.4.1"
|
||||
@ -2659,6 +2671,7 @@ dependencies = [
|
||||
"light-openid",
|
||||
"log",
|
||||
"mime_guess",
|
||||
"nix",
|
||||
"num",
|
||||
"quick-xml",
|
||||
"rand",
|
||||
|
@ -44,3 +44,4 @@ num = "0.4.1"
|
||||
rust-embed = { version = "8.1.0" }
|
||||
mime_guess = "2.0.4"
|
||||
dotenvy = "0.15.7"
|
||||
nix = { version = "0.27.1", features = ["net"] }
|
@ -261,7 +261,7 @@ impl AppConfig {
|
||||
}
|
||||
|
||||
pub fn net_nat_path(&self, name: &NetworkName) -> PathBuf {
|
||||
self.nat_path().join(format!("nat-{}.json", name.0))
|
||||
self.nat_path().join(name.nat_file_name())
|
||||
}
|
||||
|
||||
pub fn net_filter_definition_path(&self, name: &NetworkFilterName) -> PathBuf {
|
||||
|
@ -83,3 +83,6 @@ pub const NETWORK_CHAINS: [&str; 8] = ["root", "mac", "stp", "vlan", "arp", "rar
|
||||
|
||||
/// Directory where nat rules are stored, inside storage directory
|
||||
pub const STORAGE_NAT_DIR: &str = "nat";
|
||||
|
||||
/// Environment variable that is set to run VirtWeb in NAT configuration mode
|
||||
pub const NAT_MODE_ENV_VAR_NAME: &str = "NAT_MODE";
|
||||
|
@ -5,8 +5,9 @@ use crate::constants::{DISK_NAME_MAX_LEN, DISK_NAME_MIN_LEN, DISK_SIZE_MAX, DISK
|
||||
use crate::controllers::{HttpResult, LibVirtReq};
|
||||
use crate::extractors::local_auth_extractor::LocalAuthEnabled;
|
||||
use crate::libvirt_rest_structures::hypervisor::HypervisorInfo;
|
||||
use crate::utils::net_utils;
|
||||
use actix_web::{HttpResponse, Responder};
|
||||
use sysinfo::{NetworksExt, System, SystemExt};
|
||||
use sysinfo::{System, SystemExt};
|
||||
|
||||
#[derive(serde::Serialize)]
|
||||
struct StaticConfig {
|
||||
@ -137,14 +138,5 @@ pub async fn number_vcpus() -> HttpResult {
|
||||
}
|
||||
|
||||
pub async fn networks_list() -> HttpResult {
|
||||
let mut system = System::new();
|
||||
system.refresh_networks_list();
|
||||
|
||||
Ok(HttpResponse::Ok().json(
|
||||
system
|
||||
.networks()
|
||||
.iter()
|
||||
.map(|n| n.0.to_string())
|
||||
.collect::<Vec<_>>(),
|
||||
))
|
||||
Ok(HttpResponse::Ok().json(net_utils::net_list()))
|
||||
}
|
||||
|
@ -61,6 +61,13 @@ pub struct IPV6Config {
|
||||
#[derive(serde::Serialize, serde::Deserialize, Debug, Clone)]
|
||||
pub struct NetworkName(pub String);
|
||||
|
||||
impl NetworkName {
|
||||
/// Get the name of the file that will store the NAT configuration of this network
|
||||
pub fn nat_file_name(&self) -> String {
|
||||
format!("nat-{}.json", self.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl NetworkName {
|
||||
pub fn is_valid(&self) -> bool {
|
||||
regex!("^[a-zA-Z0-9]+$").is_match(&self.0)
|
||||
|
@ -27,12 +27,19 @@ use virtweb_backend::controllers::{
|
||||
};
|
||||
use virtweb_backend::libvirt_client::LibVirtClient;
|
||||
use virtweb_backend::middlewares::auth_middleware::AuthChecker;
|
||||
use virtweb_backend::nat::nat_conf_mode;
|
||||
use virtweb_backend::utils::files_utils;
|
||||
|
||||
#[actix_web::main]
|
||||
async fn main() -> std::io::Result<()> {
|
||||
env_logger::init_from_env(env_logger::Env::new().default_filter_or("info"));
|
||||
|
||||
// Run in NAT configuration mode, if requested
|
||||
if std::env::var(constants::NAT_MODE_ENV_VAR_NAME).is_ok() {
|
||||
nat_conf_mode::sub_main().await.unwrap();
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
// Load additional config from file, if requested
|
||||
AppConfig::parse_env_file().unwrap();
|
||||
|
||||
|
@ -1,2 +1,3 @@
|
||||
pub mod nat_conf_mode;
|
||||
pub mod nat_definition;
|
||||
pub mod nat_lib;
|
||||
|
79
virtweb_backend/src/nat/nat_conf_mode.rs
Normal file
79
virtweb_backend/src/nat/nat_conf_mode.rs
Normal file
@ -0,0 +1,79 @@
|
||||
use crate::constants;
|
||||
use crate::libvirt_rest_structures::net::NetworkName;
|
||||
use crate::nat::nat_definition::NetNat;
|
||||
use crate::utils::net_utils;
|
||||
use clap::Parser;
|
||||
use std::collections::HashMap;
|
||||
use std::net::IpAddr;
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
/// VirtWeb NAT configuration mode. This executable should never be executed manually
|
||||
#[derive(Parser, Debug, Clone)]
|
||||
#[clap(author, version, about, long_about = None)]
|
||||
struct NatArgs {
|
||||
/// Storage directory
|
||||
#[clap(short, long)]
|
||||
storage: String,
|
||||
|
||||
/// Network name
|
||||
#[clap(short, long)]
|
||||
network_name: String,
|
||||
|
||||
/// Operation
|
||||
#[clap(short, long)]
|
||||
operation: String,
|
||||
|
||||
/// Sub operation
|
||||
#[clap(long)]
|
||||
sub_operation: String,
|
||||
}
|
||||
|
||||
impl NatArgs {
|
||||
pub fn network_file(&self) -> PathBuf {
|
||||
let network_name = NetworkName(self.network_name.to_string());
|
||||
Path::new(&self.storage)
|
||||
.join(constants::STORAGE_NAT_DIR)
|
||||
.join(network_name.nat_file_name())
|
||||
}
|
||||
}
|
||||
|
||||
/// NAT sub main function
|
||||
pub async fn sub_main() -> anyhow::Result<()> {
|
||||
let args = NatArgs::parse();
|
||||
|
||||
if !args.network_file().exists() {
|
||||
log::warn!("Cannot do anything for the network, because the NAT configuration file does not exixsts!");
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let conf_json = std::fs::read_to_string(args.network_file())?;
|
||||
let conf: NetNat = serde_json::from_str(&conf_json)?;
|
||||
|
||||
let ips = net_utils::net_list_and_ips()?;
|
||||
|
||||
match (args.operation.as_str(), args.sub_operation.as_str()) {
|
||||
("started", "begin") => network_started_begin(&conf, &ips).await?,
|
||||
("stopped", "end") => network_stopped_end(&conf, &ips).await?,
|
||||
_ => log::debug!(
|
||||
"Operation {} - {} not supported",
|
||||
args.operation,
|
||||
args.sub_operation
|
||||
),
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn network_started_begin(
|
||||
conf: &NetNat,
|
||||
ips: &HashMap<String, Vec<IpAddr>>,
|
||||
) -> anyhow::Result<()> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
pub async fn network_stopped_end(
|
||||
conf: &NetNat,
|
||||
ips: &HashMap<String, Vec<IpAddr>>,
|
||||
) -> anyhow::Result<()> {
|
||||
todo!()
|
||||
}
|
@ -1,5 +1,8 @@
|
||||
use nix::sys::socket::{AddressFamily, SockaddrLike};
|
||||
use std::collections::HashMap;
|
||||
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
|
||||
use std::str::FromStr;
|
||||
use sysinfo::{NetworksExt, System, SystemExt};
|
||||
|
||||
pub fn extract_ipv4(ip: IpAddr) -> Ipv4Addr {
|
||||
match ip {
|
||||
@ -51,6 +54,88 @@ pub fn is_net_interface_name_valid<D: AsRef<str>>(int: D) -> bool {
|
||||
lazy_regex::regex!("^[a-zA-Z0-9]+$").is_match(int.as_ref())
|
||||
}
|
||||
|
||||
/// Get the list of available network interfaces
|
||||
pub fn net_list() -> Vec<String> {
|
||||
let mut system = System::new();
|
||||
system.refresh_networks_list();
|
||||
|
||||
system
|
||||
.networks()
|
||||
.iter()
|
||||
.map(|n| n.0.to_string())
|
||||
.collect::<Vec<_>>()
|
||||
}
|
||||
|
||||
/// Get the list of available network interfaces associated with their IP address
|
||||
pub fn net_list_and_ips() -> anyhow::Result<HashMap<String, Vec<IpAddr>>> {
|
||||
let addrs = nix::ifaddrs::getifaddrs().unwrap();
|
||||
|
||||
let mut res = HashMap::new();
|
||||
|
||||
for ifaddr in addrs {
|
||||
let address = match ifaddr.address {
|
||||
Some(address) => address,
|
||||
None => {
|
||||
log::debug!(
|
||||
"Interface {} has an unsupported address family",
|
||||
ifaddr.interface_name
|
||||
);
|
||||
continue;
|
||||
}
|
||||
};
|
||||
|
||||
let addr_str = match address.family() {
|
||||
Some(AddressFamily::Inet) => {
|
||||
let address = address.to_string();
|
||||
address
|
||||
.split_once(':')
|
||||
.map(|a| a.0)
|
||||
.unwrap_or(&address)
|
||||
.to_string()
|
||||
}
|
||||
Some(AddressFamily::Inet6) => {
|
||||
let address = address.to_string();
|
||||
let address = address
|
||||
.split_once(']')
|
||||
.map(|a| a.0)
|
||||
.unwrap_or(&address)
|
||||
.to_string();
|
||||
|
||||
let address = address
|
||||
.split_once('%')
|
||||
.map(|a| a.0)
|
||||
.unwrap_or(&address)
|
||||
.to_string();
|
||||
|
||||
address.strip_prefix('[').unwrap_or(&address).to_string()
|
||||
}
|
||||
_ => {
|
||||
log::debug!(
|
||||
"Interface {} has an unsupported address family {:?}",
|
||||
ifaddr.interface_name,
|
||||
address.family()
|
||||
);
|
||||
continue;
|
||||
}
|
||||
};
|
||||
|
||||
log::debug!(
|
||||
"Process ip {addr_str} for interface {}",
|
||||
ifaddr.interface_name
|
||||
);
|
||||
|
||||
let ip = IpAddr::from_str(&addr_str)?;
|
||||
|
||||
if !res.contains_key(&ifaddr.interface_name) {
|
||||
res.insert(ifaddr.interface_name.to_string(), Vec::with_capacity(1));
|
||||
}
|
||||
|
||||
res.get_mut(&ifaddr.interface_name).unwrap().push(ip);
|
||||
}
|
||||
|
||||
Ok(res)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::utils::net_utils::{
|
||||
|
Loading…
Reference in New Issue
Block a user