From c7f7bfe67c1115cae4892a8c8caf92ab121f1c7c Mon Sep 17 00:00:00 2001 From: Pierre Hubert Date: Tue, 12 Dec 2023 01:52:46 +0100 Subject: [PATCH] Add a system to restrict untrusted IPs --- virtweb_backend/src/app_config.rs | 24 +++++++++++++++++++ .../src/middlewares/auth_middleware.rs | 14 +++++++++++ virtweb_frontend/src/routes/IsoFilesRoute.tsx | 2 +- .../src/routes/NetworksListRoute.tsx | 2 +- virtweb_frontend/src/routes/VMListRoute.tsx | 4 +++- 5 files changed, 43 insertions(+), 3 deletions(-) diff --git a/virtweb_backend/src/app_config.rs b/virtweb_backend/src/app_config.rs index c639ff1..96c94fc 100644 --- a/virtweb_backend/src/app_config.rs +++ b/virtweb_backend/src/app_config.rs @@ -1,6 +1,8 @@ use crate::libvirt_lib_structures::XMLUuid; use clap::Parser; +use std::net::IpAddr; use std::path::{Path, PathBuf}; +use std::str::FromStr; /// VirtWeb backend API #[derive(Parser, Debug, Clone)] @@ -85,6 +87,11 @@ pub struct AppConfig { /// Hypervisor URI. If not specified, "" will be used instead #[arg(long, env)] pub hypervisor_uri: Option, + + /// Trusted network. If set, a client from a different will not be able to perform request other + /// than those with GET verb (aside for login) + #[arg(long, env)] + pub trusted_network: Vec, } lazy_static::lazy_static! { @@ -131,6 +138,23 @@ impl AppConfig { self.auth_username == user && self.auth_password == pass } + /// Check if an IP belongs to a trusted network or not + pub fn is_trusted_ip(&self, ip: IpAddr) -> bool { + if self.trusted_network.is_empty() { + return true; + } + + for i in &self.trusted_network { + let net = ipnetwork::IpNetwork::from_str(i).expect("Trusted network is invalid!"); + + if net.contains(ip) { + return true; + } + } + + false + } + /// Get OpenID providers configuration pub fn openid_provider(&self) -> Option> { if self.disable_oidc { diff --git a/virtweb_backend/src/middlewares/auth_middleware.rs b/virtweb_backend/src/middlewares/auth_middleware.rs index 7374334..2c8ea5a 100644 --- a/virtweb_backend/src/middlewares/auth_middleware.rs +++ b/virtweb_backend/src/middlewares/auth_middleware.rs @@ -61,6 +61,11 @@ where let service = Rc::clone(&self.service); Box::pin(async move { + let remote_ip = + actix_remote_ip::RemoteIP::from_request(req.request(), &mut Payload::None) + .await + .unwrap(); + let auth_disabled = AppConfig::get().unsecure_disable_auth; // Check authentication, if required @@ -89,6 +94,15 @@ where ) .map_into_right_body()); } + + if !AppConfig::get().is_trusted_ip(remote_ip.0) && !req.method().as_str().eq("GET") + { + return Ok(req + .into_response( + HttpResponse::MethodNotAllowed().json("I am sorry, but your IP is not trusted. You cannot perform this action!"), + ) + .map_into_right_body()); + } } service diff --git a/virtweb_frontend/src/routes/IsoFilesRoute.tsx b/virtweb_frontend/src/routes/IsoFilesRoute.tsx index a4d5c18..6c3c30d 100644 --- a/virtweb_frontend/src/routes/IsoFilesRoute.tsx +++ b/virtweb_frontend/src/routes/IsoFilesRoute.tsx @@ -223,7 +223,7 @@ function IsoFilesList(p: { p.onReload(); } catch (e) { console.error(e); - alert("Failed to delete file!"); + alert(`Failed to delete file!\n${e}`); } loadingMessage.hide(); diff --git a/virtweb_frontend/src/routes/NetworksListRoute.tsx b/virtweb_frontend/src/routes/NetworksListRoute.tsx index d654484..b780a21 100644 --- a/virtweb_frontend/src/routes/NetworksListRoute.tsx +++ b/virtweb_frontend/src/routes/NetworksListRoute.tsx @@ -57,7 +57,7 @@ export function NetworksListRoute(): React.ReactElement { snackbar("The network was successfully deleted!"); } catch (e) { console.error(e); - alert("Failed to delete the network!"); + alert(`Failed to delete the network!\n${e}`); } }; diff --git a/virtweb_frontend/src/routes/VMListRoute.tsx b/virtweb_frontend/src/routes/VMListRoute.tsx index c288135..792abdd 100644 --- a/virtweb_frontend/src/routes/VMListRoute.tsx +++ b/virtweb_frontend/src/routes/VMListRoute.tsx @@ -22,6 +22,7 @@ import { VMStatusWidget } from "../widgets/vms/VMStatusWidget"; import { useSnackbar } from "../hooks/providers/SnackbarProvider"; import { useConfirm } from "../hooks/providers/ConfirmDialogProvider"; import { useNavigate } from "react-router-dom"; +import { useAlert } from "../hooks/providers/AlertDialogProvider"; export function VMListRoute(): React.ReactElement { const [list, setList] = React.useState(); @@ -66,6 +67,7 @@ function VMListWidget(p: { onReload: () => void; }): React.ReactElement { const confirm = useConfirm(); + const alert = useAlert(); const snackbar = useSnackbar(); const navigate = useNavigate(); @@ -93,7 +95,7 @@ function VMListWidget(p: { p.onReload(); } catch (e) { console.error(e); - snackbar("Failed to delete VM!"); + alert(`Failed to delete VM!\n${e}`); } };