mirror of
				https://git.proxmox.com/git/proxmox-perl-rs
				synced 2025-10-30 17:41:24 +00:00 
			
		
		
		
	add PVE::RS::Firewall::SDN module
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 <s.hanreich@proxmox.com> Reviewed-by: Wolfgang Bumiller <w.bumiller@proxmox.com> Tested-by: Gabriel Goller <g.goller@proxmox.com> Tested-by: Hannes Dürr <h.duerr@proxmox.com>
This commit is contained in:
		
							parent
							
								
									ebaf08b8e6
								
							
						
					
					
						commit
						02b7eae00a
					
				| @ -45,3 +45,4 @@ proxmox-subscription = "0.5" | ||||
| proxmox-sys = "0.6" | ||||
| proxmox-tfa = { version = "5", features = ["api"] } | ||||
| proxmox-time = "2" | ||||
| proxmox-ve-config = { version = "0.1.0" } | ||||
|  | ||||
| @ -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 | ||||
|  | ||||
							
								
								
									
										1
									
								
								pve-rs/src/firewall/mod.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								pve-rs/src/firewall/mod.rs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1 @@ | ||||
| pub mod sdn; | ||||
							
								
								
									
										130
									
								
								pve-rs/src/firewall/sdn.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										130
									
								
								pve-rs/src/firewall/sdn.rs
									
									
									
									
									
										Normal file
									
								
							| @ -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<String>, | ||||
|     } | ||||
| 
 | ||||
|     impl LegacyIpsetEntry { | ||||
|         pub fn from_ipset_entry(entry: &IpsetEntry) -> Vec<LegacyIpsetEntry> { | ||||
|             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<String, Vec<LegacyIpsetEntry>>, | ||||
|         ipset_comments: HashMap<String, String>, | ||||
|     } | ||||
| 
 | ||||
|     impl SdnFirewallConfig { | ||||
|         pub fn new() -> Self { | ||||
|             Default::default() | ||||
|         } | ||||
| 
 | ||||
|         pub fn extend_ipsets(&mut self, ipsets: impl IntoIterator<Item = Ipset>) { | ||||
|             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<Vec<VnetName>>, | ||||
|         vm_filter: Option<Vec<Vmid>>, | ||||
|     ) -> Result<SdnFirewallConfig, Error> { | ||||
|         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) | ||||
|     } | ||||
| } | ||||
| @ -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; | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user
	 Stefan Hanreich
						Stefan Hanreich