public inbox for pve-devel@lists.proxmox.com
 help / color / mirror / Atom feed
From: Stefan Hanreich <s.hanreich@proxmox.com>
To: pve-devel@lists.proxmox.com
Subject: [pve-devel] [PATCH proxmox-firewall 05/15] sdn: add support for loading vnet-level firewall config
Date: Wed, 11 Sep 2024 11:31:06 +0200	[thread overview]
Message-ID: <20240911093116.112960-6-s.hanreich@proxmox.com> (raw)
In-Reply-To: <20240911093116.112960-1-s.hanreich@proxmox.com>

Signed-off-by: Stefan Hanreich <s.hanreich@proxmox.com>
---
 proxmox-firewall/src/config.rs              | 88 ++++++++++++++++++++-
 proxmox-firewall/tests/integration_tests.rs | 12 +++
 2 files changed, 98 insertions(+), 2 deletions(-)

diff --git a/proxmox-firewall/src/config.rs b/proxmox-firewall/src/config.rs
index c27aac6..ac60e15 100644
--- a/proxmox-firewall/src/config.rs
+++ b/proxmox-firewall/src/config.rs
@@ -1,10 +1,11 @@
 use std::collections::BTreeMap;
 use std::default::Default;
-use std::fs::File;
+use std::fs::{self, DirEntry, File, ReadDir};
 use std::io::{self, BufReader};
 
-use anyhow::{format_err, Context, Error};
+use anyhow::{bail, format_err, Context, Error};
 
+use proxmox_ve_config::firewall::bridge::Config as BridgeConfig;
 use proxmox_ve_config::firewall::cluster::Config as ClusterConfig;
 use proxmox_ve_config::firewall::guest::Config as GuestConfig;
 use proxmox_ve_config::firewall::host::Config as HostConfig;
@@ -12,6 +13,7 @@ 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::types::BridgeName;
 
 use proxmox_nftables::command::{CommandOutput, Commands, List, ListOutput};
 use proxmox_nftables::types::ListChain;
@@ -33,6 +35,11 @@ pub trait FirewallConfigLoader {
     fn guest_firewall_config(&self, vmid: &Vmid) -> Result<Option<Box<dyn io::BufRead>>, Error>;
     fn sdn_running_config(&self) -> Result<Option<Box<dyn io::BufRead>>, Error>;
     fn ipam(&self) -> Result<Option<Box<dyn io::BufRead>>, Error>;
+    fn bridge_list(&self) -> Result<Vec<BridgeName>, Error>;
+    fn bridge_firewall_config(
+        &self,
+        bridge_name: &BridgeName,
+    ) -> Result<Option<Box<dyn io::BufRead>>, Error>;
 }
 
 #[derive(Default)]
@@ -61,8 +68,31 @@ fn open_config_file(path: &str) -> Result<Option<File>, Error> {
     }
 }
 
+fn open_config_folder(path: &str) -> Result<Option<ReadDir>, Error> {
+    match fs::read_dir(path) {
+        Ok(paths) => Ok(Some(paths)),
+        Err(err) if err.kind() == io::ErrorKind::NotFound => {
+            log::info!("SDN config folder {path} does not exist");
+            Ok(None)
+        }
+        Err(err) => {
+            let context = format!("unable to open configuration folder at {BRIDGE_CONFIG_PATH}");
+            Err(anyhow::Error::new(err).context(context))
+        }
+    }
+}
+
+fn fw_name(dir_entry: DirEntry) -> Option<String> {
+    dir_entry
+        .file_name()
+        .to_str()?
+        .strip_suffix(".fw")
+        .map(str::to_string)
+}
+
 const CLUSTER_CONFIG_PATH: &str = "/etc/pve/firewall/cluster.fw";
 const HOST_CONFIG_PATH: &str = "/etc/pve/local/host.fw";
+const BRIDGE_CONFIG_PATH: &str = "/etc/pve/sdn/firewall";
 
 const SDN_RUNNING_CONFIG_PATH: &str = "/etc/pve/sdn/.running-config";
 const SDN_IPAM_PATH: &str = "/etc/pve/priv/ipam.db";
@@ -154,6 +184,38 @@ impl FirewallConfigLoader for PveFirewallConfigLoader {
 
         Ok(None)
     }
+
+    fn bridge_list(&self) -> Result<Vec<BridgeName>, Error> {
+        let mut bridges = Vec::new();
+
+        if let Some(files) = open_config_folder(BRIDGE_CONFIG_PATH)? {
+            for file in files {
+                let bridge_name = fw_name(file?).map(BridgeName::new).transpose()?;
+
+                if let Some(bridge_name) = bridge_name {
+                    bridges.push(bridge_name);
+                }
+            }
+        }
+
+        Ok(bridges)
+    }
+
+    fn bridge_firewall_config(
+        &self,
+        bridge_name: &BridgeName,
+    ) -> Result<Option<Box<dyn io::BufRead>>, Error> {
+        log::info!("loading firewall config for bridge {bridge_name}");
+
+        let fd = open_config_file(&format!("/etc/pve/sdn/firewall/{bridge_name}.fw"))?;
+
+        if let Some(file) = fd {
+            let buf_reader = Box::new(BufReader::new(file)) as Box<dyn io::BufRead>;
+            return Ok(Some(buf_reader));
+        }
+
+        Ok(None)
+    }
 }
 
 pub trait NftConfigLoader {
@@ -184,6 +246,7 @@ pub struct FirewallConfig {
     cluster_config: ClusterConfig,
     host_config: HostConfig,
     guest_config: BTreeMap<Vmid, GuestConfig>,
+    bridge_config: BTreeMap<BridgeName, BridgeConfig>,
     nft_config: BTreeMap<String, ListChain>,
     sdn_config: Option<SdnConfig>,
     ipam_config: Option<Ipam>,
@@ -284,6 +347,22 @@ impl FirewallConfig {
         Ok(chains)
     }
 
+    pub fn parse_bridges(
+        firewall_loader: &dyn FirewallConfigLoader,
+    ) -> Result<BTreeMap<BridgeName, BridgeConfig>, Error> {
+        let mut bridge_config = BTreeMap::new();
+
+        for bridge_name in firewall_loader.bridge_list()? {
+            if let Some(config) = firewall_loader.bridge_firewall_config(&bridge_name)? {
+                bridge_config.insert(bridge_name, BridgeConfig::parse(config)?);
+            } else {
+                bail!("Could not read config for {bridge_name}")
+            }
+        }
+
+        Ok(bridge_config)
+    }
+
     pub fn new(
         firewall_loader: &dyn FirewallConfigLoader,
         nft_loader: &dyn NftConfigLoader,
@@ -292,6 +371,7 @@ impl FirewallConfig {
             cluster_config: Self::parse_cluster(firewall_loader)?,
             host_config: Self::parse_host(firewall_loader)?,
             guest_config: Self::parse_guests(firewall_loader)?,
+            bridge_config: Self::parse_bridges(firewall_loader)?,
             sdn_config: Self::parse_sdn(firewall_loader)?,
             ipam_config: Self::parse_ipam(firewall_loader)?,
             nft_config: Self::parse_nft(nft_loader)?,
@@ -310,6 +390,10 @@ impl FirewallConfig {
         &self.guest_config
     }
 
+    pub fn bridges(&self) -> &BTreeMap<BridgeName, BridgeConfig> {
+        &self.bridge_config
+    }
+
     pub fn nft_chains(&self) -> &BTreeMap<String, ListChain> {
         &self.nft_config
     }
diff --git a/proxmox-firewall/tests/integration_tests.rs b/proxmox-firewall/tests/integration_tests.rs
index 5de1a4e..61a8062 100644
--- a/proxmox-firewall/tests/integration_tests.rs
+++ b/proxmox-firewall/tests/integration_tests.rs
@@ -7,6 +7,7 @@ use proxmox_nftables::command::CommandOutput;
 use proxmox_sys::nodename;
 use proxmox_ve_config::guest::types::Vmid;
 use proxmox_ve_config::guest::{GuestEntry, GuestMap, GuestType};
+use proxmox_ve_config::host::types::BridgeName;
 
 struct MockFirewallConfigLoader {}
 
@@ -79,6 +80,17 @@ impl FirewallConfigLoader for MockFirewallConfigLoader {
     fn ipam(&self) -> Result<Option<Box<dyn std::io::BufRead>>, Error> {
         Ok(Some(Box::new(include_str!("input/ipam.db").as_bytes())))
     }
+
+    fn bridge_list(&self) -> Result<Vec<BridgeName>, Error> {
+        Ok(Vec::new())
+    }
+
+    fn bridge_firewall_config(
+        &self,
+        bridge_name: &BridgeName,
+    ) -> Result<Option<Box<dyn std::io::BufRead>>, Error> {
+        Ok(None)
+    }
 }
 
 struct MockNftConfigLoader {}
-- 
2.39.2


_______________________________________________
pve-devel mailing list
pve-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel


  parent reply	other threads:[~2024-09-11  9:31 UTC|newest]

Thread overview: 18+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2024-09-11  9:31 [pve-devel] [RFC firewall/manager/network/proxmox{-ve-rs, -firewall} 00/15] add forward chain firewalling for hosts and bridges Stefan Hanreich
2024-09-11  9:31 ` [pve-devel] [PATCH proxmox-ve-rs 01/15] cargo: bump dependencies Stefan Hanreich
2024-09-11  9:31 ` [pve-devel] [PATCH proxmox-ve-rs 02/15] firewall: add forward direction Stefan Hanreich
2024-09-11  9:31 ` [pve-devel] [PATCH proxmox-ve-rs 03/15] firewall: add bridge firewall config parser Stefan Hanreich
2024-09-11  9:31 ` [pve-devel] [PATCH proxmox-ve-rs 04/15] host: add struct representing bridge names Stefan Hanreich
2024-09-11  9:31 ` Stefan Hanreich [this message]
2024-09-11  9:31 ` [pve-devel] [PATCH proxmox-firewall 06/15] sdn: create forward firewall rules Stefan Hanreich
2024-09-11  9:31 ` [pve-devel] [PATCH proxmox-firewall 07/15] use std::mem::take over drain() Stefan Hanreich
2024-09-11  9:31 ` [pve-devel] [PATCH proxmox-firewall 08/15] cargo: bump dependencies Stefan Hanreich
2024-09-11  9:31 ` [pve-devel] [PATCH pve-firewall 09/15] sdn: add vnet firewall configuration Stefan Hanreich
2024-09-11  9:31 ` [pve-devel] [PATCH pve-firewall 10/15] api: add vnet endpoints Stefan Hanreich
2024-09-11  9:31 ` [pve-devel] [PATCH pve-manager 11/15] firewall: add forward direction to rule panel Stefan Hanreich
2024-09-11  9:31 ` [pve-devel] [PATCH pve-manager 12/15] firewall: add vnet to firewall options component Stefan Hanreich
2024-09-11  9:31 ` [pve-devel] [PATCH pve-manager 13/15] firewall: make base_url dynamically configurable in " Stefan Hanreich
2024-09-11  9:31 ` [pve-devel] [PATCH pve-manager 14/15] sdn: add firewall panel Stefan Hanreich
2024-09-11  9:31 ` [pve-devel] [PATCH pve-network 15/15] firewall: add endpoints for vnet-level firewall Stefan Hanreich
2024-09-11 12:31 ` [pve-devel] [RFC firewall/manager/network/proxmox{-ve-rs, -firewall} 00/15] add forward chain firewalling for hosts and bridges Stefan Hanreich
2024-09-11 15:22 ` Gabriel Goller

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20240911093116.112960-6-s.hanreich@proxmox.com \
    --to=s.hanreich@proxmox.com \
    --cc=pve-devel@lists.proxmox.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
Service provided by Proxmox Server Solutions GmbH | Privacy | Legal