Co-authored-by: Wolfgang Bumiller <w.bumil...@proxmox.com> Signed-off-by: Stefan Hanreich <s.hanre...@proxmox.com> --- proxmox-ve-config/src/firewall/guest.rs | 194 ++++++++++++++++++++++++ proxmox-ve-config/src/firewall/mod.rs | 1 + 2 files changed, 195 insertions(+) create mode 100644 proxmox-ve-config/src/firewall/guest.rs
diff --git a/proxmox-ve-config/src/firewall/guest.rs b/proxmox-ve-config/src/firewall/guest.rs new file mode 100644 index 0000000..6ca446c --- /dev/null +++ b/proxmox-ve-config/src/firewall/guest.rs @@ -0,0 +1,194 @@ +use std::collections::HashMap; +use std::io; + +use crate::guest::types::Vmid; +use crate::guest::vm::NetworkConfig; + +use crate::firewall::types::alias::{Alias, AliasName}; +use crate::firewall::types::ipset::IpsetScope; +use crate::firewall::types::log::LogLevel; +use crate::firewall::types::rule::{Direction, Rule, Verdict}; +use crate::firewall::types::Ipset; + +use anyhow::{bail, Error}; +use serde::Deserialize; + +use crate::firewall::parse::serde_option_bool; + +#[derive(Debug, Default, Deserialize)] +#[cfg_attr(test, derive(Eq, PartialEq))] +pub struct Options { + #[serde(default, with = "serde_option_bool")] + dhcp: Option<bool>, + + #[serde(default, with = "serde_option_bool")] + enable: Option<bool>, + + #[serde(default, with = "serde_option_bool")] + ipfilter: Option<bool>, + + #[serde(default, with = "serde_option_bool")] + ndp: Option<bool>, + + #[serde(default, with = "serde_option_bool")] + radv: Option<bool>, + + log_level_in: Option<LogLevel>, + log_level_out: Option<LogLevel>, + + #[serde(default, with = "serde_option_bool")] + macfilter: Option<bool>, + + #[serde(rename = "policy_in")] + policy_in: Option<Verdict>, + + #[serde(rename = "policy_out")] + policy_out: Option<Verdict>, +} + +#[derive(Debug)] +pub struct Config { + vmid: Vmid, + + /// The interface prefix: "veth" for containers, "tap" for VMs. + iface_prefix: &'static str, + + network_config: NetworkConfig, + config: super::common::Config<Options>, +} + +impl Config { + pub fn parse<T: io::BufRead, U: io::BufRead>( + vmid: &Vmid, + iface_prefix: &'static str, + firewall_input: T, + network_input: U, + ) -> Result<Self, Error> { + let parser_cfg = super::common::ParserConfig { + guest_iface_names: true, + ipset_scope: Some(IpsetScope::Guest), + }; + + let config = super::common::Config::parse(firewall_input, &parser_cfg)?; + if !config.groups.is_empty() { + bail!("guest firewall config cannot declare groups"); + } + + let network_config = NetworkConfig::parse(network_input)?; + + Ok(Self { + vmid: *vmid, + iface_prefix, + config, + network_config, + }) + } + + pub fn alias(&self, name: &AliasName) -> Option<&Alias> { + self.config.alias(name.name()) + } + + pub fn iface_name_by_key(&self, key: &str) -> Result<String, Error> { + let index = NetworkConfig::index_from_net_key(key)?; + Ok(format!("{}{}i{index}", self.iface_prefix, self.vmid)) + } + + pub fn iface_name_by_index(&self, index: i64) -> String { + format!("{}{}i{index}", self.iface_prefix, self.vmid) + } + + pub fn is_enabled(&self) -> bool { + self.config.options.enable.unwrap_or(false) + } + + pub fn rules(&self) -> &[Rule] { + &self.config.rules + } + + pub fn log_level(&self, dir: Direction) -> LogLevel { + match dir { + Direction::In => self.config.options.log_level_in.unwrap_or_default(), + Direction::Out => self.config.options.log_level_out.unwrap_or_default(), + } + } + + pub fn allow_ndp(&self) -> bool { + self.config.options.ndp.unwrap_or(true) + } + + pub fn allow_dhcp(&self) -> bool { + self.config.options.dhcp.unwrap_or(true) + } + + pub fn allow_ra(&self) -> bool { + self.config.options.radv.unwrap_or(false) + } + + pub fn macfilter(&self) -> bool { + self.config.options.macfilter.unwrap_or(true) + } + + pub fn ipfilter(&self) -> bool { + self.config.options.ipfilter.unwrap_or(false) + } + + pub fn default_policy(&self, dir: Direction) -> Verdict { + match dir { + Direction::In => self.config.options.policy_in.unwrap_or(Verdict::Drop), + Direction::Out => self.config.options.policy_out.unwrap_or(Verdict::Accept), + } + } + + pub fn network_config(&self) -> &NetworkConfig { + &self.network_config + } + + pub fn ipsets(&self) -> &HashMap<String, Ipset> { + self.config.ipsets() + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_parse_config() { + // most of the stuff is already tested in cluster parsing, only testing + // guest specific options here + const CONFIG: &str = r#" +[OPTIONS] +enable: 1 +dhcp: 1 +ipfilter: 0 +log_level_in: emerg +log_level_out: crit +macfilter: 0 +ndp:1 +radv:1 +policy_in: REJECT +policy_out: REJECT +"#; + + let config = CONFIG.as_bytes(); + let network_config: Vec<u8> = Vec::new(); + let config = + Config::parse(&Vmid::new(100), "tap", config, network_config.as_slice()).unwrap(); + + assert_eq!( + config.config.options, + Options { + dhcp: Some(true), + enable: Some(true), + ipfilter: Some(false), + ndp: Some(true), + radv: Some(true), + log_level_in: Some(LogLevel::Emergency), + log_level_out: Some(LogLevel::Critical), + macfilter: Some(false), + policy_in: Some(Verdict::Reject), + policy_out: Some(Verdict::Reject), + } + ); + } +} diff --git a/proxmox-ve-config/src/firewall/mod.rs b/proxmox-ve-config/src/firewall/mod.rs index 85fe6c4..afc3dcc 100644 --- a/proxmox-ve-config/src/firewall/mod.rs +++ b/proxmox-ve-config/src/firewall/mod.rs @@ -1,5 +1,6 @@ pub mod cluster; pub mod common; +pub mod guest; pub mod host; pub mod ports; pub mod types; -- 2.39.2 _______________________________________________ pve-devel mailing list pve-devel@lists.proxmox.com https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel