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",
|
"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]]
|
[[package]]
|
||||||
name = "ntapi"
|
name = "ntapi"
|
||||||
version = "0.4.1"
|
version = "0.4.1"
|
||||||
@ -2659,6 +2671,7 @@ dependencies = [
|
|||||||
"light-openid",
|
"light-openid",
|
||||||
"log",
|
"log",
|
||||||
"mime_guess",
|
"mime_guess",
|
||||||
|
"nix",
|
||||||
"num",
|
"num",
|
||||||
"quick-xml",
|
"quick-xml",
|
||||||
"rand",
|
"rand",
|
||||||
|
@ -44,3 +44,4 @@ num = "0.4.1"
|
|||||||
rust-embed = { version = "8.1.0" }
|
rust-embed = { version = "8.1.0" }
|
||||||
mime_guess = "2.0.4"
|
mime_guess = "2.0.4"
|
||||||
dotenvy = "0.15.7"
|
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 {
|
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 {
|
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
|
/// Directory where nat rules are stored, inside storage directory
|
||||||
pub const STORAGE_NAT_DIR: &str = "nat";
|
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::controllers::{HttpResult, LibVirtReq};
|
||||||
use crate::extractors::local_auth_extractor::LocalAuthEnabled;
|
use crate::extractors::local_auth_extractor::LocalAuthEnabled;
|
||||||
use crate::libvirt_rest_structures::hypervisor::HypervisorInfo;
|
use crate::libvirt_rest_structures::hypervisor::HypervisorInfo;
|
||||||
|
use crate::utils::net_utils;
|
||||||
use actix_web::{HttpResponse, Responder};
|
use actix_web::{HttpResponse, Responder};
|
||||||
use sysinfo::{NetworksExt, System, SystemExt};
|
use sysinfo::{System, SystemExt};
|
||||||
|
|
||||||
#[derive(serde::Serialize)]
|
#[derive(serde::Serialize)]
|
||||||
struct StaticConfig {
|
struct StaticConfig {
|
||||||
@ -137,14 +138,5 @@ pub async fn number_vcpus() -> HttpResult {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub async fn networks_list() -> HttpResult {
|
pub async fn networks_list() -> HttpResult {
|
||||||
let mut system = System::new();
|
Ok(HttpResponse::Ok().json(net_utils::net_list()))
|
||||||
system.refresh_networks_list();
|
|
||||||
|
|
||||||
Ok(HttpResponse::Ok().json(
|
|
||||||
system
|
|
||||||
.networks()
|
|
||||||
.iter()
|
|
||||||
.map(|n| n.0.to_string())
|
|
||||||
.collect::<Vec<_>>(),
|
|
||||||
))
|
|
||||||
}
|
}
|
||||||
|
@ -61,6 +61,13 @@ pub struct IPV6Config {
|
|||||||
#[derive(serde::Serialize, serde::Deserialize, Debug, Clone)]
|
#[derive(serde::Serialize, serde::Deserialize, Debug, Clone)]
|
||||||
pub struct NetworkName(pub String);
|
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 {
|
impl NetworkName {
|
||||||
pub fn is_valid(&self) -> bool {
|
pub fn is_valid(&self) -> bool {
|
||||||
regex!("^[a-zA-Z0-9]+$").is_match(&self.0)
|
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::libvirt_client::LibVirtClient;
|
||||||
use virtweb_backend::middlewares::auth_middleware::AuthChecker;
|
use virtweb_backend::middlewares::auth_middleware::AuthChecker;
|
||||||
|
use virtweb_backend::nat::nat_conf_mode;
|
||||||
use virtweb_backend::utils::files_utils;
|
use virtweb_backend::utils::files_utils;
|
||||||
|
|
||||||
#[actix_web::main]
|
#[actix_web::main]
|
||||||
async fn main() -> std::io::Result<()> {
|
async fn main() -> std::io::Result<()> {
|
||||||
env_logger::init_from_env(env_logger::Env::new().default_filter_or("info"));
|
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
|
// Load additional config from file, if requested
|
||||||
AppConfig::parse_env_file().unwrap();
|
AppConfig::parse_env_file().unwrap();
|
||||||
|
|
||||||
|
@ -1,2 +1,3 @@
|
|||||||
|
pub mod nat_conf_mode;
|
||||||
pub mod nat_definition;
|
pub mod nat_definition;
|
||||||
pub mod nat_lib;
|
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::net::{IpAddr, Ipv4Addr, Ipv6Addr};
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
use sysinfo::{NetworksExt, System, SystemExt};
|
||||||
|
|
||||||
pub fn extract_ipv4(ip: IpAddr) -> Ipv4Addr {
|
pub fn extract_ipv4(ip: IpAddr) -> Ipv4Addr {
|
||||||
match ip {
|
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())
|
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)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use crate::utils::net_utils::{
|
use crate::utils::net_utils::{
|
||||||
|
Loading…
Reference in New Issue
Block a user