[pve-devel] [PATCH proxmox-firewall 1/1] firewall: add altname support for firewall rules

Stefan Hanreich s.hanreich at proxmox.com
Wed Jul 9 21:45:23 CEST 2025


This works by reading all the currently configured altnames and then
replacing any occurences of altnames when creating the firewall rules.
We handle it this way because nftables has no support for matching on
the altnames of interfaces.

Signed-off-by: Stefan Hanreich <s.hanreich at proxmox.com>
---
 proxmox-firewall/src/config.rs              | 29 +++++++++++++++++++++
 proxmox-firewall/src/rule.rs                |  6 ++++-
 proxmox-firewall/tests/integration_tests.rs |  7 +++++
 3 files changed, 41 insertions(+), 1 deletion(-)

diff --git a/proxmox-firewall/src/config.rs b/proxmox-firewall/src/config.rs
index ec9849e..f07fb1e 100644
--- a/proxmox-firewall/src/config.rs
+++ b/proxmox-firewall/src/config.rs
@@ -13,6 +13,8 @@ use proxmox_ve_config::firewall::types::alias::{Alias, AliasName, AliasScope};
 
 use proxmox_ve_config::guest::types::Vmid;
 use proxmox_ve_config::guest::{GuestEntry, GuestMap};
+use proxmox_ve_config::host::network::InterfaceMapping;
+use proxmox_ve_config::host::network::IpLink;
 use proxmox_ve_config::host::types::BridgeName;
 
 use proxmox_nftables::command::{CommandOutput, Commands, List, ListOutput};
@@ -40,6 +42,7 @@ pub trait FirewallConfigLoader {
         &self,
         bridge_name: &BridgeName,
     ) -> Result<Option<Box<dyn io::BufRead>>, Error>;
+    fn interface_mapping(&self) -> Result<InterfaceMapping, Error>;
 }
 
 #[derive(Default)]
@@ -221,6 +224,26 @@ impl FirewallConfigLoader for PveFirewallConfigLoader {
 
         Ok(None)
     }
+
+    fn interface_mapping(&self) -> Result<InterfaceMapping, Error> {
+        let output = std::process::Command::new("ip")
+            .arg("-details")
+            .arg("-json")
+            .arg("link")
+            .arg("show")
+            .stdout(std::process::Stdio::piped())
+            .output()
+            .with_context(|| "could not obtain ip link output")?;
+
+        if !output.status.success() {
+            bail!("ip link returned non-zero exit code")
+        }
+
+        Ok(serde_json::from_slice::<Vec<IpLink>>(&output.stdout)
+            .with_context(|| "could not deserialize ip link output")?
+            .into_iter()
+            .collect())
+    }
 }
 
 pub trait NftConfigLoader {
@@ -255,6 +278,7 @@ pub struct FirewallConfig {
     nft_config: BTreeMap<String, ListChain>,
     sdn_config: Option<SdnConfig>,
     ipam_config: Option<Ipam>,
+    interface_mapping: InterfaceMapping,
 }
 
 impl FirewallConfig {
@@ -380,6 +404,7 @@ impl FirewallConfig {
             sdn_config: Self::parse_sdn(firewall_loader)?,
             ipam_config: Self::parse_ipam(firewall_loader)?,
             nft_config: Self::parse_nft(nft_loader)?,
+            interface_mapping: firewall_loader.interface_mapping()?,
         })
     }
 
@@ -415,6 +440,10 @@ impl FirewallConfig {
         self.cluster().is_enabled() && self.host().nftables()
     }
 
+    pub fn interface_mapping(&self, iface_name: &str) -> Option<&str> {
+        self.interface_mapping.get(iface_name).map(|x| x.as_str())
+    }
+
     pub fn alias(&self, name: &AliasName, vmid: Option<Vmid>) -> Option<&Alias> {
         log::trace!("getting alias {name:?}");
 
diff --git a/proxmox-firewall/src/rule.rs b/proxmox-firewall/src/rule.rs
index 14ee544..c4975a9 100644
--- a/proxmox-firewall/src/rule.rs
+++ b/proxmox-firewall/src/rule.rs
@@ -135,7 +135,11 @@ impl NftRuleEnv<'_> {
 
                 rule_iface.to_string()
             }
-            None => rule_iface.to_string(),
+            None => self
+                .firewall_config
+                .interface_mapping(rule_iface)
+                .map(|iface_name| iface_name.to_string())
+                .unwrap_or_else(|| rule_iface.to_string()),
         }
     }
 
diff --git a/proxmox-firewall/tests/integration_tests.rs b/proxmox-firewall/tests/integration_tests.rs
index 1c014ad..69f9cc2 100644
--- a/proxmox-firewall/tests/integration_tests.rs
+++ b/proxmox-firewall/tests/integration_tests.rs
@@ -1,4 +1,5 @@
 use anyhow::{Context, Error};
+use proxmox_ve_config::host::network::InterfaceMapping;
 use std::collections::HashMap;
 
 use proxmox_firewall::config::{FirewallConfig, FirewallConfigLoader, NftConfigLoader};
@@ -91,6 +92,12 @@ impl FirewallConfigLoader for MockFirewallConfigLoader {
     ) -> Result<Option<Box<dyn std::io::BufRead>>, Error> {
         Ok(None)
     }
+
+    fn interface_mapping(
+        &self,
+    ) -> Result<proxmox_ve_config::host::network::InterfaceMapping, Error> {
+        Ok(InterfaceMapping::from_iter(vec![]))
+    }
 }
 
 struct MockNftConfigLoader {}
-- 
2.39.5




More information about the pve-devel mailing list