From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from firstgate.proxmox.com (firstgate.proxmox.com [212.224.123.68]) by lore.proxmox.com (Postfix) with ESMTPS id 3662B1FF163 for ; Thu, 10 Oct 2024 17:57:50 +0200 (CEST) Received: from firstgate.proxmox.com (localhost [127.0.0.1]) by firstgate.proxmox.com (Proxmox) with ESMTP id 1BC6A1E116; Thu, 10 Oct 2024 17:57:01 +0200 (CEST) From: Stefan Hanreich To: pve-devel@lists.proxmox.com Date: Thu, 10 Oct 2024 17:56:35 +0200 Message-Id: <20241010155637.255451-24-s.hanreich@proxmox.com> X-Mailer: git-send-email 2.39.5 In-Reply-To: <20241010155637.255451-1-s.hanreich@proxmox.com> References: <20241010155637.255451-1-s.hanreich@proxmox.com> MIME-Version: 1.0 X-SPAM-LEVEL: Spam detection results: 0 AWL -0.260 Adjusted score from AWL reputation of From: address BAYES_00 -1.9 Bayes spam probability is 0 to 1% DMARC_MISSING 0.1 Missing DMARC policy KAM_DMARC_STATUS 0.01 Test Rule for DKIM or SPF Failure with Strict Alignment KAM_LAZY_DOMAIN_SECURITY 1 Sending domain does not have any anti-forgery methods RDNS_NONE 0.793 Delivered to internal network by a host with no rDNS SPF_HELO_NONE 0.001 SPF: HELO does not publish an SPF Record SPF_NONE 0.001 SPF: sender does not publish an SPF Record Subject: [pve-devel] [PATCH proxmox-perl-rs v2 23/25] add PVE::RS::Firewall::SDN module X-BeenThere: pve-devel@lists.proxmox.com X-Mailman-Version: 2.1.29 Precedence: list List-Id: Proxmox VE development discussion List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Reply-To: Proxmox VE development discussion Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit Errors-To: pve-devel-bounces@lists.proxmox.com Sender: "pve-devel" Used for obtaining the IPSets that get autogenerated by the nftables firewall. The returned configuration has the same format as the pve-firewall uses internally, making it compatible with the existing pve-firewall code. Signed-off-by: Stefan Hanreich --- pve-rs/Cargo.toml | 1 + pve-rs/Makefile | 1 + pve-rs/src/firewall/mod.rs | 1 + pve-rs/src/firewall/sdn.rs | 130 +++++++++++++++++++++++++++++++++++++ pve-rs/src/lib.rs | 1 + 5 files changed, 134 insertions(+) create mode 100644 pve-rs/src/firewall/mod.rs create mode 100644 pve-rs/src/firewall/sdn.rs diff --git a/pve-rs/Cargo.toml b/pve-rs/Cargo.toml index 72d548d..cab9a83 100644 --- a/pve-rs/Cargo.toml +++ b/pve-rs/Cargo.toml @@ -45,3 +45,4 @@ proxmox-subscription = "0.4" proxmox-sys = "0.6" proxmox-tfa = { version = "5", features = ["api"] } proxmox-time = "2" +proxmox-ve-config = { version = "0.1.0" } diff --git a/pve-rs/Makefile b/pve-rs/Makefile index c6b4e08..d01da69 100644 --- a/pve-rs/Makefile +++ b/pve-rs/Makefile @@ -28,6 +28,7 @@ PERLMOD_GENPACKAGE := /usr/lib/perlmod/genpackage.pl \ PERLMOD_PACKAGES := \ PVE::RS::APT::Repositories \ + PVE::RS::Firewall::SDN \ PVE::RS::OpenId \ PVE::RS::ResourceScheduling::Static \ PVE::RS::TFA diff --git a/pve-rs/src/firewall/mod.rs b/pve-rs/src/firewall/mod.rs new file mode 100644 index 0000000..8bd18a8 --- /dev/null +++ b/pve-rs/src/firewall/mod.rs @@ -0,0 +1 @@ +pub mod sdn; diff --git a/pve-rs/src/firewall/sdn.rs b/pve-rs/src/firewall/sdn.rs new file mode 100644 index 0000000..5049f74 --- /dev/null +++ b/pve-rs/src/firewall/sdn.rs @@ -0,0 +1,130 @@ +#[perlmod::package(name = "PVE::RS::Firewall::SDN", lib = "pve_rs")] +mod export { + use std::collections::HashMap; + use std::{fs, io}; + + use anyhow::{bail, Context, Error}; + use serde::Serialize; + + use proxmox_ve_config::{ + common::Allowlist, + firewall::types::ipset::{IpsetAddress, IpsetEntry}, + firewall::types::Ipset, + guest::types::Vmid, + sdn::{ + config::{RunningConfig, SdnConfig}, + ipam::{Ipam, IpamJson}, + VnetName, + }, + }; + + #[derive(Clone, Debug, Default, Serialize)] + pub struct LegacyIpsetEntry { + nomatch: bool, + cidr: String, + comment: Option, + } + + impl LegacyIpsetEntry { + pub fn from_ipset_entry(entry: &IpsetEntry) -> Vec { + let mut entries = Vec::new(); + + match &entry.address { + IpsetAddress::Alias(name) => { + entries.push(Self { + nomatch: entry.nomatch, + cidr: name.to_string(), + comment: entry.comment.clone(), + }); + } + IpsetAddress::Cidr(cidr) => { + entries.push(Self { + nomatch: entry.nomatch, + cidr: cidr.to_string(), + comment: entry.comment.clone(), + }); + } + IpsetAddress::Range(range) => { + entries.extend(range.to_cidrs().into_iter().map(|cidr| Self { + nomatch: entry.nomatch, + cidr: cidr.to_string(), + comment: entry.comment.clone(), + })) + } + }; + + entries + } + } + + #[derive(Clone, Debug, Default, Serialize)] + pub struct SdnFirewallConfig { + ipset: HashMap>, + ipset_comments: HashMap, + } + + impl SdnFirewallConfig { + pub fn new() -> Self { + Default::default() + } + + pub fn extend_ipsets(&mut self, ipsets: impl IntoIterator) { + for ipset in ipsets { + let entries = ipset + .iter() + .flat_map(LegacyIpsetEntry::from_ipset_entry) + .collect(); + + self.ipset.insert(ipset.name().name().to_string(), entries); + + if let Some(comment) = &ipset.comment { + self.ipset_comments + .insert(ipset.name().name().to_string(), comment.to_string()); + } + } + } + } + + const SDN_RUNNING_CONFIG: &str = "/etc/pve/sdn/.running-config"; + const SDN_IPAM: &str = "/etc/pve/priv/ipam.db"; + + #[export] + pub fn config( + vnet_filter: Option>, + vm_filter: Option>, + ) -> Result { + let mut refs = SdnFirewallConfig::new(); + + match fs::read_to_string(SDN_RUNNING_CONFIG) { + Ok(data) => { + let running_config: RunningConfig = serde_json::from_str(&data)?; + let sdn_config = SdnConfig::try_from(running_config) + .with_context(|| "Failed to parse SDN config".to_string())?; + + let allowlist = vnet_filter.map(Allowlist::from_iter); + refs.extend_ipsets(sdn_config.ipsets(allowlist.as_ref())); + } + Err(e) if e.kind() == io::ErrorKind::NotFound => (), + Err(e) => { + bail!("Cannot open SDN running config: {e:#}"); + } + }; + + match fs::read_to_string(SDN_IPAM) { + Ok(data) => { + let ipam_json: IpamJson = serde_json::from_str(&data)?; + let ipam: Ipam = Ipam::try_from(ipam_json) + .with_context(|| "Failed to parse IPAM".to_string())?; + + let allowlist = vm_filter.map(Allowlist::from_iter); + refs.extend_ipsets(ipam.ipsets(allowlist.as_ref())); + } + Err(e) if e.kind() == io::ErrorKind::NotFound => (), + Err(e) => { + bail!("Cannot open IPAM database: {e:#}"); + } + }; + + Ok(refs) + } +} diff --git a/pve-rs/src/lib.rs b/pve-rs/src/lib.rs index 5e47ac6..3de37d1 100644 --- a/pve-rs/src/lib.rs +++ b/pve-rs/src/lib.rs @@ -12,6 +12,7 @@ use proxmox_notify::{Config, Notification, Severity}; pub mod common; pub mod apt; +pub mod firewall; pub mod openid; pub mod resource_scheduling; pub mod tfa; -- 2.39.5 _______________________________________________ pve-devel mailing list pve-devel@lists.proxmox.com https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel