* [pve-devel] [PATCH docs/firewall/manager/proxmox{-firewall, -perl-rs} v4 0/9] autogenerate ipsets for sdn objects @ 2024-11-15 12:09 Stefan Hanreich 2024-11-15 12:09 ` [pve-devel] [PATCH proxmox-firewall v4 2/9] config: tests: add support for loading sdn and ipam config Stefan Hanreich ` (7 more replies) 0 siblings, 8 replies; 9+ messages in thread From: Stefan Hanreich @ 2024-11-15 12:09 UTC (permalink / raw) To: pve-devel This patch series adds support for autogenerating ipsets for SDN objects. It autogenerates ipsets for every VNet as follows: * ipset containing all IP ranges of the VNet * ipset containing all gateways of the VNet * ipset containing all IP ranges of the subnet - except gateways * ipset containing all dhcp ranges of the vnet Additionally it generates an IPSet for every guest that has one or more IPAM entries in the pve IPAM. Those can then be used in the cluster / host / guest firewalls. Firewall rules automatically update on changes of the SDN / IPAM configuration. This patch series works for the old firewall as well as the new firewall. The ipsets in nftables currently get generated as named ipsets in every table, this means that the `nft list ruleset` output can get quite crowded for large SDN configurations or large IPAM databases. Another option would be to only include them as anonymous IPsets in the rules, which would make the nft output far less crowded but this way would use more memory when making extensive use of the sdn ipsets, since everytime it is used in a rule we create an entirely new ipset. The base for proxmox-ve-rs (which is a filtered version of the proxmox-firewall repository can be found here:) staff/s.hanreich/proxmox-ve-rs.git master Dependencies: * proxmox-perl-rs and proxmox-firewall depend on proxmox-ve-rs * pve-firewall depends on proxmox-perl-rs * pve-manager depends on pve-firewall Changes from v3 to v4: * omitted proxmox-ve-rs since it is merged * always load SDN configuration now when loading cluster config * adapt is_nftables to check the flag file instead of reading the config * gracefully fail when RPCEnvironment is not available Changes from v2: * rename end in IpRange to last to avoid confusion - thanks @Wolfgang * bump Rust to 1.82 - thanks @Wolfgang * improvements to the code generating IPSets - thanks @Wolfgang * implement AsRef<str> for SDN name types - thanks @Wolfgang * improve docstrings (proper capitalization and punctuation) - thanks @Wolfgang * included a patch that removes proxmox-ve-config from proxmox-firewall Changes from RFC: * added documentation * added separate SDN scope for IPSets * rustfmt fixes proxmox-firewall: Stefan Hanreich (3): add proxmox-ve-rs crate - move proxmox-ve-config there config: tests: add support for loading sdn and ipam config ipsets: autogenerate ipsets for vnets and ipam Cargo.toml | 4 +- Makefile | 2 +- proxmox-firewall/Cargo.toml | 2 +- proxmox-firewall/src/config.rs | 69 + proxmox-firewall/src/firewall.rs | 22 +- proxmox-firewall/src/object.rs | 41 +- .../tests/input/.running-config.json | 45 + proxmox-firewall/tests/input/ipam.db | 32 + proxmox-firewall/tests/integration_tests.rs | 10 + .../integration_tests__firewall.snap | 1288 +++++++++++++++++ proxmox-nftables/Cargo.toml | 2 +- proxmox-nftables/src/expression.rs | 17 +- proxmox-nftables/src/types.rs | 2 +- proxmox-ve-config/Cargo.toml | 25 - proxmox-ve-config/resources/ct_helper.json | 52 - proxmox-ve-config/resources/macros.json | 923 ------------ proxmox-ve-config/src/firewall/cluster.rs | 374 ----- proxmox-ve-config/src/firewall/common.rs | 184 --- proxmox-ve-config/src/firewall/ct_helper.rs | 115 -- proxmox-ve-config/src/firewall/fw_macros.rs | 69 - proxmox-ve-config/src/firewall/guest.rs | 237 --- proxmox-ve-config/src/firewall/host.rs | 372 ----- proxmox-ve-config/src/firewall/mod.rs | 10 - proxmox-ve-config/src/firewall/parse.rs | 494 ------- proxmox-ve-config/src/firewall/ports.rs | 80 - .../src/firewall/types/address.rs | 615 -------- proxmox-ve-config/src/firewall/types/alias.rs | 174 --- proxmox-ve-config/src/firewall/types/group.rs | 36 - proxmox-ve-config/src/firewall/types/ipset.rs | 349 ----- proxmox-ve-config/src/firewall/types/log.rs | 222 --- proxmox-ve-config/src/firewall/types/mod.rs | 14 - proxmox-ve-config/src/firewall/types/port.rs | 181 --- proxmox-ve-config/src/firewall/types/rule.rs | 412 ------ .../src/firewall/types/rule_match.rs | 977 ------------- proxmox-ve-config/src/guest/mod.rs | 115 -- proxmox-ve-config/src/guest/types.rs | 38 - proxmox-ve-config/src/guest/vm.rs | 510 ------- proxmox-ve-config/src/host/mod.rs | 1 - proxmox-ve-config/src/host/utils.rs | 70 - proxmox-ve-config/src/lib.rs | 3 - 40 files changed, 1517 insertions(+), 6671 deletions(-) create mode 100644 proxmox-firewall/tests/input/.running-config.json create mode 100644 proxmox-firewall/tests/input/ipam.db delete mode 100644 proxmox-ve-config/Cargo.toml delete mode 100644 proxmox-ve-config/resources/ct_helper.json delete mode 100644 proxmox-ve-config/resources/macros.json delete mode 100644 proxmox-ve-config/src/firewall/cluster.rs delete mode 100644 proxmox-ve-config/src/firewall/common.rs delete mode 100644 proxmox-ve-config/src/firewall/ct_helper.rs delete mode 100644 proxmox-ve-config/src/firewall/fw_macros.rs delete mode 100644 proxmox-ve-config/src/firewall/guest.rs delete mode 100644 proxmox-ve-config/src/firewall/host.rs delete mode 100644 proxmox-ve-config/src/firewall/mod.rs delete mode 100644 proxmox-ve-config/src/firewall/parse.rs delete mode 100644 proxmox-ve-config/src/firewall/ports.rs delete mode 100644 proxmox-ve-config/src/firewall/types/address.rs delete mode 100644 proxmox-ve-config/src/firewall/types/alias.rs delete mode 100644 proxmox-ve-config/src/firewall/types/group.rs delete mode 100644 proxmox-ve-config/src/firewall/types/ipset.rs delete mode 100644 proxmox-ve-config/src/firewall/types/log.rs delete mode 100644 proxmox-ve-config/src/firewall/types/mod.rs delete mode 100644 proxmox-ve-config/src/firewall/types/port.rs delete mode 100644 proxmox-ve-config/src/firewall/types/rule.rs delete mode 100644 proxmox-ve-config/src/firewall/types/rule_match.rs delete mode 100644 proxmox-ve-config/src/guest/mod.rs delete mode 100644 proxmox-ve-config/src/guest/types.rs delete mode 100644 proxmox-ve-config/src/guest/vm.rs delete mode 100644 proxmox-ve-config/src/host/mod.rs delete mode 100644 proxmox-ve-config/src/host/utils.rs delete mode 100644 proxmox-ve-config/src/lib.rs pve-firewall: Stefan Hanreich (3): add support for loading sdn firewall configuration nftables: make is_nftables check flag file instead of config api: load sdn ipsets src/PVE/API2/Firewall/Aliases.pm | 2 + src/PVE/API2/Firewall/Cluster.pm | 7 ++- src/PVE/API2/Firewall/Groups.pm | 1 + src/PVE/API2/Firewall/Host.pm | 1 + src/PVE/API2/Firewall/IPSet.pm | 2 + src/PVE/API2/Firewall/Rules.pm | 2 + src/PVE/API2/Firewall/VM.pm | 5 ++- src/PVE/Firewall.pm | 76 +++++++++++++++++++++++++++----- src/PVE/Service/pve_firewall.pm | 4 +- 9 files changed, 84 insertions(+), 16 deletions(-) proxmox-perl-rs: Stefan Hanreich (1): add PVE::RS::Firewall::SDN module 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 pve-manager: Stefan Hanreich (1): firewall: add sdn scope to IPRefSelector www/manager6/form/IPRefSelector.js | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) pve-docs: Stefan Hanreich (1): sdn: add documentation for firewall integration pvesdn.adoc | 92 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 92 insertions(+) Summary over all repositories: 56 files changed, 1834 insertions(+), 6688 deletions(-) -- Generated by git-murpp 0.6.0 _______________________________________________ pve-devel mailing list pve-devel@lists.proxmox.com https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel ^ permalink raw reply [flat|nested] 9+ messages in thread
* [pve-devel] [PATCH proxmox-firewall v4 2/9] config: tests: add support for loading sdn and ipam config 2024-11-15 12:09 [pve-devel] [PATCH docs/firewall/manager/proxmox{-firewall, -perl-rs} v4 0/9] autogenerate ipsets for sdn objects Stefan Hanreich @ 2024-11-15 12:09 ` Stefan Hanreich 2024-11-15 12:09 ` [pve-devel] [PATCH proxmox-firewall v4 3/9] ipsets: autogenerate ipsets for vnets and ipam Stefan Hanreich ` (6 subsequent siblings) 7 siblings, 0 replies; 9+ messages in thread From: Stefan Hanreich @ 2024-11-15 12:09 UTC (permalink / raw) To: pve-devel; +Cc: Wolfgang Bumiller Also add example SDN configuration files that get automatically loaded, which can be used for future tests. 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> --- proxmox-firewall/src/config.rs | 69 +++++++++++++++++++ .../tests/input/.running-config.json | 45 ++++++++++++ proxmox-firewall/tests/input/ipam.db | 32 +++++++++ proxmox-firewall/tests/integration_tests.rs | 10 +++ proxmox-nftables/src/types.rs | 2 +- 5 files changed, 157 insertions(+), 1 deletion(-) create mode 100644 proxmox-firewall/tests/input/.running-config.json create mode 100644 proxmox-firewall/tests/input/ipam.db diff --git a/proxmox-firewall/src/config.rs b/proxmox-firewall/src/config.rs index 5bd2512..c27aac6 100644 --- a/proxmox-firewall/src/config.rs +++ b/proxmox-firewall/src/config.rs @@ -16,6 +16,10 @@ use proxmox_ve_config::guest::{GuestEntry, GuestMap}; use proxmox_nftables::command::{CommandOutput, Commands, List, ListOutput}; use proxmox_nftables::types::ListChain; use proxmox_nftables::NftClient; +use proxmox_ve_config::sdn::{ + config::{RunningConfig, SdnConfig}, + ipam::{Ipam, IpamJson}, +}; pub trait FirewallConfigLoader { fn cluster(&self) -> Result<Option<Box<dyn io::BufRead>>, Error>; @@ -27,6 +31,8 @@ pub trait FirewallConfigLoader { guest: &GuestEntry, ) -> Result<Option<Box<dyn io::BufRead>>, Error>; 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>; } #[derive(Default)] @@ -58,6 +64,9 @@ fn open_config_file(path: &str) -> Result<Option<File>, Error> { const CLUSTER_CONFIG_PATH: &str = "/etc/pve/firewall/cluster.fw"; const HOST_CONFIG_PATH: &str = "/etc/pve/local/host.fw"; +const SDN_RUNNING_CONFIG_PATH: &str = "/etc/pve/sdn/.running-config"; +const SDN_IPAM_PATH: &str = "/etc/pve/priv/ipam.db"; + impl FirewallConfigLoader for PveFirewallConfigLoader { fn cluster(&self) -> Result<Option<Box<dyn io::BufRead>>, Error> { log::info!("loading cluster config"); @@ -119,6 +128,32 @@ impl FirewallConfigLoader for PveFirewallConfigLoader { Ok(None) } + + fn sdn_running_config(&self) -> Result<Option<Box<dyn io::BufRead>>, Error> { + log::info!("loading SDN running-config"); + + let fd = open_config_file(SDN_RUNNING_CONFIG_PATH)?; + + 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) + } + + fn ipam(&self) -> Result<Option<Box<dyn io::BufRead>>, Error> { + log::info!("loading IPAM config"); + + let fd = open_config_file(SDN_IPAM_PATH)?; + + 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 { @@ -150,6 +185,8 @@ pub struct FirewallConfig { host_config: HostConfig, guest_config: BTreeMap<Vmid, GuestConfig>, nft_config: BTreeMap<String, ListChain>, + sdn_config: Option<SdnConfig>, + ipam_config: Option<Ipam>, } impl FirewallConfig { @@ -207,6 +244,28 @@ impl FirewallConfig { Ok(guests) } + pub fn parse_sdn( + firewall_loader: &dyn FirewallConfigLoader, + ) -> Result<Option<SdnConfig>, Error> { + Ok(match firewall_loader.sdn_running_config()? { + Some(data) => { + let running_config: RunningConfig = serde_json::from_reader(data)?; + Some(SdnConfig::try_from(running_config)?) + } + _ => None, + }) + } + + pub fn parse_ipam(firewall_loader: &dyn FirewallConfigLoader) -> Result<Option<Ipam>, Error> { + Ok(match firewall_loader.ipam()? { + Some(data) => { + let raw_ipam: IpamJson = serde_json::from_reader(data)?; + Some(Ipam::try_from(raw_ipam)?) + } + _ => None, + }) + } + pub fn parse_nft( nft_loader: &dyn NftConfigLoader, ) -> Result<BTreeMap<String, ListChain>, Error> { @@ -233,6 +292,8 @@ impl FirewallConfig { cluster_config: Self::parse_cluster(firewall_loader)?, host_config: Self::parse_host(firewall_loader)?, guest_config: Self::parse_guests(firewall_loader)?, + sdn_config: Self::parse_sdn(firewall_loader)?, + ipam_config: Self::parse_ipam(firewall_loader)?, nft_config: Self::parse_nft(nft_loader)?, }) } @@ -253,6 +314,14 @@ impl FirewallConfig { &self.nft_config } + pub fn sdn(&self) -> Option<&SdnConfig> { + self.sdn_config.as_ref() + } + + pub fn ipam(&self) -> Option<&Ipam> { + self.ipam_config.as_ref() + } + pub fn is_enabled(&self) -> bool { self.cluster().is_enabled() && self.host().nftables() } diff --git a/proxmox-firewall/tests/input/.running-config.json b/proxmox-firewall/tests/input/.running-config.json new file mode 100644 index 0000000..a4511f0 --- /dev/null +++ b/proxmox-firewall/tests/input/.running-config.json @@ -0,0 +1,45 @@ +{ + "subnets": { + "ids": { + "test-10.101.0.0-16": { + "gateway": "10.101.1.1", + "snat": 1, + "vnet": "public", + "dhcp-range": [ + "start-address=10.101.99.100,end-address=10.101.99.200" + ], + "type": "subnet" + }, + "test-fd80::-64": { + "snat": 1, + "gateway": "fd80::1", + "dhcp-range": [ + "start-address=fd80::1000,end-address=fd80::ffff" + ], + "vnet": "public", + "type": "subnet" + } + } + }, + "version": 49, + "vnets": { + "ids": { + "public": { + "zone": "test", + "type": "vnet" + } + } + }, + "zones": { + "ids": { + "test": { + "dhcp": "dnsmasq", + "ipam": "pve", + "type": "simple" + } + } + }, + "controllers": { + "ids": {} + } +} diff --git a/proxmox-firewall/tests/input/ipam.db b/proxmox-firewall/tests/input/ipam.db new file mode 100644 index 0000000..ac2901e --- /dev/null +++ b/proxmox-firewall/tests/input/ipam.db @@ -0,0 +1,32 @@ +{ + "zones": { + "public": { + "subnets": { + "10.101.0.0/16": { + "ips": { + "10.101.1.1": { + "gateway": 1 + }, + "10.101.1.100": { + "vmid": "101", + "mac": "BC:24:11:11:22:33", + "hostname": null + } + } + }, + "fd80::/64": { + "ips": { + "fd80::1": { + "gateway": 1 + }, + "fd80::1000": { + "mac": "BC:24:11:11:22:33", + "vmid": "101", + "hostname": "test-vm" + } + } + } + } + } + } +} diff --git a/proxmox-firewall/tests/integration_tests.rs b/proxmox-firewall/tests/integration_tests.rs index e9baffe..5de1a4e 100644 --- a/proxmox-firewall/tests/integration_tests.rs +++ b/proxmox-firewall/tests/integration_tests.rs @@ -69,6 +69,16 @@ impl FirewallConfigLoader for MockFirewallConfigLoader { Ok(None) } + + fn sdn_running_config(&self) -> Result<Option<Box<dyn std::io::BufRead>>, Error> { + Ok(Some(Box::new( + include_str!("input/.running-config.json").as_bytes(), + ))) + } + + fn ipam(&self) -> Result<Option<Box<dyn std::io::BufRead>>, Error> { + Ok(Some(Box::new(include_str!("input/ipam.db").as_bytes()))) + } } struct MockNftConfigLoader {} diff --git a/proxmox-nftables/src/types.rs b/proxmox-nftables/src/types.rs index a83e958..3101436 100644 --- a/proxmox-nftables/src/types.rs +++ b/proxmox-nftables/src/types.rs @@ -636,7 +636,7 @@ impl SetName { }; let name = match name.scope() { - IpsetScope::Datacenter => name.to_string(), + IpsetScope::Datacenter | IpsetScope::Sdn => name.to_string(), IpsetScope::Guest => { if let Some(vmid) = vmid { format!("guest-{vmid}/{}", name.name()) -- 2.39.5 _______________________________________________ pve-devel mailing list pve-devel@lists.proxmox.com https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel ^ permalink raw reply [flat|nested] 9+ messages in thread
* [pve-devel] [PATCH proxmox-firewall v4 3/9] ipsets: autogenerate ipsets for vnets and ipam 2024-11-15 12:09 [pve-devel] [PATCH docs/firewall/manager/proxmox{-firewall, -perl-rs} v4 0/9] autogenerate ipsets for sdn objects Stefan Hanreich 2024-11-15 12:09 ` [pve-devel] [PATCH proxmox-firewall v4 2/9] config: tests: add support for loading sdn and ipam config Stefan Hanreich @ 2024-11-15 12:09 ` Stefan Hanreich 2024-11-15 12:09 ` [pve-devel] [PATCH pve-firewall v4 4/9] add support for loading sdn firewall configuration Stefan Hanreich ` (5 subsequent siblings) 7 siblings, 0 replies; 9+ messages in thread From: Stefan Hanreich @ 2024-11-15 12:09 UTC (permalink / raw) To: pve-devel; +Cc: Wolfgang Bumiller They act like virtual ipsets, similar to ipfilter-net, that can be used for defining firewall rules for sdn objects dynamically. The changes in proxmox-ve-config also introduced a dedicated struct for representing ip ranges, so we update the existing code, so that it uses that struct as well. 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> --- proxmox-firewall/src/firewall.rs | 22 +- proxmox-firewall/src/object.rs | 41 +- .../integration_tests__firewall.snap | 1288 +++++++++++++++++ proxmox-nftables/src/expression.rs | 17 +- 4 files changed, 1354 insertions(+), 14 deletions(-) diff --git a/proxmox-firewall/src/firewall.rs b/proxmox-firewall/src/firewall.rs index 941aa20..347f3af 100644 --- a/proxmox-firewall/src/firewall.rs +++ b/proxmox-firewall/src/firewall.rs @@ -197,6 +197,27 @@ impl Firewall { self.reset_firewall(&mut commands); let cluster_host_table = Self::cluster_table(); + let guest_table = Self::guest_table(); + + if let Some(sdn_config) = self.config.sdn() { + let ipsets = sdn_config + .ipsets(None) + .map(|ipset| (ipset.name().to_string(), ipset)) + .collect(); + + self.create_ipsets(&mut commands, &ipsets, &cluster_host_table, None)?; + self.create_ipsets(&mut commands, &ipsets, &guest_table, None)?; + } + + if let Some(ipam_config) = self.config.ipam() { + let ipsets = ipam_config + .ipsets(None) + .map(|ipset| (ipset.name().to_string(), ipset)) + .collect(); + + self.create_ipsets(&mut commands, &ipsets, &cluster_host_table, None)?; + self.create_ipsets(&mut commands, &ipsets, &guest_table, None)?; + } if self.config.host().is_enabled() { log::info!("creating cluster / host configuration"); @@ -242,7 +263,6 @@ impl Firewall { commands.push(Delete::table(TableName::from(Self::cluster_table()))); } - let guest_table = Self::guest_table(); let enabled_guests: BTreeMap<&Vmid, &GuestConfig> = self .config .guests() diff --git a/proxmox-firewall/src/object.rs b/proxmox-firewall/src/object.rs index 32c4ddb..cf7e773 100644 --- a/proxmox-firewall/src/object.rs +++ b/proxmox-firewall/src/object.rs @@ -72,20 +72,37 @@ impl ToNftObjects for Ipset { let mut nomatch_elements = Vec::new(); for element in self.iter() { - let cidr = match &element.address { - IpsetAddress::Cidr(cidr) => cidr, - IpsetAddress::Alias(alias) => env - .alias(alias) - .ok_or(format_err!("could not find alias {alias} in environment"))? - .address(), + let expression = match &element.address { + IpsetAddress::Range(range) => { + if family != range.family() { + continue; + } + + Expression::from(range) + } + IpsetAddress::Cidr(cidr) => { + if family != cidr.family() { + continue; + } + + Expression::from(Prefix::from(cidr)) + } + IpsetAddress::Alias(alias) => { + let cidr = env + .alias(alias) + .ok_or_else(|| { + format_err!("could not find alias {alias} in environment") + })? + .address(); + + if family != cidr.family() { + continue; + } + + Expression::from(Prefix::from(cidr)) + } }; - if family != cidr.family() { - continue; - } - - let expression = Expression::from(Prefix::from(cidr)); - if element.nomatch { nomatch_elements.push(expression); } else { diff --git a/proxmox-firewall/tests/snapshots/integration_tests__firewall.snap b/proxmox-firewall/tests/snapshots/integration_tests__firewall.snap index 40d4405..e1b599c 100644 --- a/proxmox-firewall/tests/snapshots/integration_tests__firewall.snap +++ b/proxmox-firewall/tests/snapshots/integration_tests__firewall.snap @@ -202,6 +202,1294 @@ expression: "firewall.full_host_fw().expect(\"firewall can be generated\")" } } }, + { + "add": { + "set": { + "family": "inet", + "table": "proxmox-firewall", + "name": "v4-sdn/public-all", + "type": "ipv4_addr", + "flags": [ + "interval" + ] + } + } + }, + { + "flush": { + "set": { + "family": "inet", + "table": "proxmox-firewall", + "name": "v4-sdn/public-all" + } + } + }, + { + "add": { + "set": { + "family": "inet", + "table": "proxmox-firewall", + "name": "v4-sdn/public-all-nomatch", + "type": "ipv4_addr", + "flags": [ + "interval" + ] + } + } + }, + { + "flush": { + "set": { + "family": "inet", + "table": "proxmox-firewall", + "name": "v4-sdn/public-all-nomatch" + } + } + }, + { + "add": { + "element": { + "family": "inet", + "table": "proxmox-firewall", + "name": "v4-sdn/public-all", + "elem": [ + { + "prefix": { + "addr": "10.101.0.0", + "len": 16 + } + } + ] + } + } + }, + { + "add": { + "set": { + "family": "inet", + "table": "proxmox-firewall", + "name": "v6-sdn/public-all", + "type": "ipv6_addr", + "flags": [ + "interval" + ] + } + } + }, + { + "flush": { + "set": { + "family": "inet", + "table": "proxmox-firewall", + "name": "v6-sdn/public-all" + } + } + }, + { + "add": { + "set": { + "family": "inet", + "table": "proxmox-firewall", + "name": "v6-sdn/public-all-nomatch", + "type": "ipv6_addr", + "flags": [ + "interval" + ] + } + } + }, + { + "flush": { + "set": { + "family": "inet", + "table": "proxmox-firewall", + "name": "v6-sdn/public-all-nomatch" + } + } + }, + { + "add": { + "element": { + "family": "inet", + "table": "proxmox-firewall", + "name": "v6-sdn/public-all", + "elem": [ + { + "prefix": { + "addr": "fd80::", + "len": 64 + } + } + ] + } + } + }, + { + "add": { + "set": { + "family": "inet", + "table": "proxmox-firewall", + "name": "v4-sdn/public-dhcp", + "type": "ipv4_addr", + "flags": [ + "interval" + ] + } + } + }, + { + "flush": { + "set": { + "family": "inet", + "table": "proxmox-firewall", + "name": "v4-sdn/public-dhcp" + } + } + }, + { + "add": { + "set": { + "family": "inet", + "table": "proxmox-firewall", + "name": "v4-sdn/public-dhcp-nomatch", + "type": "ipv4_addr", + "flags": [ + "interval" + ] + } + } + }, + { + "flush": { + "set": { + "family": "inet", + "table": "proxmox-firewall", + "name": "v4-sdn/public-dhcp-nomatch" + } + } + }, + { + "add": { + "element": { + "family": "inet", + "table": "proxmox-firewall", + "name": "v4-sdn/public-dhcp", + "elem": [ + { + "range": [ + "10.101.99.100", + "10.101.99.200" + ] + } + ] + } + } + }, + { + "add": { + "set": { + "family": "inet", + "table": "proxmox-firewall", + "name": "v6-sdn/public-dhcp", + "type": "ipv6_addr", + "flags": [ + "interval" + ] + } + } + }, + { + "flush": { + "set": { + "family": "inet", + "table": "proxmox-firewall", + "name": "v6-sdn/public-dhcp" + } + } + }, + { + "add": { + "set": { + "family": "inet", + "table": "proxmox-firewall", + "name": "v6-sdn/public-dhcp-nomatch", + "type": "ipv6_addr", + "flags": [ + "interval" + ] + } + } + }, + { + "flush": { + "set": { + "family": "inet", + "table": "proxmox-firewall", + "name": "v6-sdn/public-dhcp-nomatch" + } + } + }, + { + "add": { + "element": { + "family": "inet", + "table": "proxmox-firewall", + "name": "v6-sdn/public-dhcp", + "elem": [ + { + "range": [ + "fd80::1000", + "fd80::ffff" + ] + } + ] + } + } + }, + { + "add": { + "set": { + "family": "inet", + "table": "proxmox-firewall", + "name": "v4-sdn/public-gateway", + "type": "ipv4_addr", + "flags": [ + "interval" + ] + } + } + }, + { + "flush": { + "set": { + "family": "inet", + "table": "proxmox-firewall", + "name": "v4-sdn/public-gateway" + } + } + }, + { + "add": { + "set": { + "family": "inet", + "table": "proxmox-firewall", + "name": "v4-sdn/public-gateway-nomatch", + "type": "ipv4_addr", + "flags": [ + "interval" + ] + } + } + }, + { + "flush": { + "set": { + "family": "inet", + "table": "proxmox-firewall", + "name": "v4-sdn/public-gateway-nomatch" + } + } + }, + { + "add": { + "element": { + "family": "inet", + "table": "proxmox-firewall", + "name": "v4-sdn/public-gateway", + "elem": [ + { + "prefix": { + "addr": "10.101.1.1", + "len": 32 + } + } + ] + } + } + }, + { + "add": { + "set": { + "family": "inet", + "table": "proxmox-firewall", + "name": "v6-sdn/public-gateway", + "type": "ipv6_addr", + "flags": [ + "interval" + ] + } + } + }, + { + "flush": { + "set": { + "family": "inet", + "table": "proxmox-firewall", + "name": "v6-sdn/public-gateway" + } + } + }, + { + "add": { + "set": { + "family": "inet", + "table": "proxmox-firewall", + "name": "v6-sdn/public-gateway-nomatch", + "type": "ipv6_addr", + "flags": [ + "interval" + ] + } + } + }, + { + "flush": { + "set": { + "family": "inet", + "table": "proxmox-firewall", + "name": "v6-sdn/public-gateway-nomatch" + } + } + }, + { + "add": { + "element": { + "family": "inet", + "table": "proxmox-firewall", + "name": "v6-sdn/public-gateway", + "elem": [ + { + "prefix": { + "addr": "fd80::1", + "len": 128 + } + } + ] + } + } + }, + { + "add": { + "set": { + "family": "inet", + "table": "proxmox-firewall", + "name": "v4-sdn/public-no-gateway", + "type": "ipv4_addr", + "flags": [ + "interval" + ] + } + } + }, + { + "flush": { + "set": { + "family": "inet", + "table": "proxmox-firewall", + "name": "v4-sdn/public-no-gateway" + } + } + }, + { + "add": { + "set": { + "family": "inet", + "table": "proxmox-firewall", + "name": "v4-sdn/public-no-gateway-nomatch", + "type": "ipv4_addr", + "flags": [ + "interval" + ] + } + } + }, + { + "flush": { + "set": { + "family": "inet", + "table": "proxmox-firewall", + "name": "v4-sdn/public-no-gateway-nomatch" + } + } + }, + { + "add": { + "element": { + "family": "inet", + "table": "proxmox-firewall", + "name": "v4-sdn/public-no-gateway", + "elem": [ + { + "prefix": { + "addr": "10.101.0.0", + "len": 16 + } + } + ] + } + } + }, + { + "add": { + "element": { + "family": "inet", + "table": "proxmox-firewall", + "name": "v4-sdn/public-no-gateway-nomatch", + "elem": [ + { + "prefix": { + "addr": "10.101.1.1", + "len": 32 + } + } + ] + } + } + }, + { + "add": { + "set": { + "family": "inet", + "table": "proxmox-firewall", + "name": "v6-sdn/public-no-gateway", + "type": "ipv6_addr", + "flags": [ + "interval" + ] + } + } + }, + { + "flush": { + "set": { + "family": "inet", + "table": "proxmox-firewall", + "name": "v6-sdn/public-no-gateway" + } + } + }, + { + "add": { + "set": { + "family": "inet", + "table": "proxmox-firewall", + "name": "v6-sdn/public-no-gateway-nomatch", + "type": "ipv6_addr", + "flags": [ + "interval" + ] + } + } + }, + { + "flush": { + "set": { + "family": "inet", + "table": "proxmox-firewall", + "name": "v6-sdn/public-no-gateway-nomatch" + } + } + }, + { + "add": { + "element": { + "family": "inet", + "table": "proxmox-firewall", + "name": "v6-sdn/public-no-gateway", + "elem": [ + { + "prefix": { + "addr": "fd80::", + "len": 64 + } + } + ] + } + } + }, + { + "add": { + "element": { + "family": "inet", + "table": "proxmox-firewall", + "name": "v6-sdn/public-no-gateway-nomatch", + "elem": [ + { + "prefix": { + "addr": "fd80::1", + "len": 128 + } + } + ] + } + } + }, + { + "add": { + "set": { + "family": "bridge", + "table": "proxmox-firewall-guests", + "name": "v4-sdn/public-all", + "type": "ipv4_addr", + "flags": [ + "interval" + ] + } + } + }, + { + "flush": { + "set": { + "family": "bridge", + "table": "proxmox-firewall-guests", + "name": "v4-sdn/public-all" + } + } + }, + { + "add": { + "set": { + "family": "bridge", + "table": "proxmox-firewall-guests", + "name": "v4-sdn/public-all-nomatch", + "type": "ipv4_addr", + "flags": [ + "interval" + ] + } + } + }, + { + "flush": { + "set": { + "family": "bridge", + "table": "proxmox-firewall-guests", + "name": "v4-sdn/public-all-nomatch" + } + } + }, + { + "add": { + "element": { + "family": "bridge", + "table": "proxmox-firewall-guests", + "name": "v4-sdn/public-all", + "elem": [ + { + "prefix": { + "addr": "10.101.0.0", + "len": 16 + } + } + ] + } + } + }, + { + "add": { + "set": { + "family": "bridge", + "table": "proxmox-firewall-guests", + "name": "v6-sdn/public-all", + "type": "ipv6_addr", + "flags": [ + "interval" + ] + } + } + }, + { + "flush": { + "set": { + "family": "bridge", + "table": "proxmox-firewall-guests", + "name": "v6-sdn/public-all" + } + } + }, + { + "add": { + "set": { + "family": "bridge", + "table": "proxmox-firewall-guests", + "name": "v6-sdn/public-all-nomatch", + "type": "ipv6_addr", + "flags": [ + "interval" + ] + } + } + }, + { + "flush": { + "set": { + "family": "bridge", + "table": "proxmox-firewall-guests", + "name": "v6-sdn/public-all-nomatch" + } + } + }, + { + "add": { + "element": { + "family": "bridge", + "table": "proxmox-firewall-guests", + "name": "v6-sdn/public-all", + "elem": [ + { + "prefix": { + "addr": "fd80::", + "len": 64 + } + } + ] + } + } + }, + { + "add": { + "set": { + "family": "bridge", + "table": "proxmox-firewall-guests", + "name": "v4-sdn/public-dhcp", + "type": "ipv4_addr", + "flags": [ + "interval" + ] + } + } + }, + { + "flush": { + "set": { + "family": "bridge", + "table": "proxmox-firewall-guests", + "name": "v4-sdn/public-dhcp" + } + } + }, + { + "add": { + "set": { + "family": "bridge", + "table": "proxmox-firewall-guests", + "name": "v4-sdn/public-dhcp-nomatch", + "type": "ipv4_addr", + "flags": [ + "interval" + ] + } + } + }, + { + "flush": { + "set": { + "family": "bridge", + "table": "proxmox-firewall-guests", + "name": "v4-sdn/public-dhcp-nomatch" + } + } + }, + { + "add": { + "element": { + "family": "bridge", + "table": "proxmox-firewall-guests", + "name": "v4-sdn/public-dhcp", + "elem": [ + { + "range": [ + "10.101.99.100", + "10.101.99.200" + ] + } + ] + } + } + }, + { + "add": { + "set": { + "family": "bridge", + "table": "proxmox-firewall-guests", + "name": "v6-sdn/public-dhcp", + "type": "ipv6_addr", + "flags": [ + "interval" + ] + } + } + }, + { + "flush": { + "set": { + "family": "bridge", + "table": "proxmox-firewall-guests", + "name": "v6-sdn/public-dhcp" + } + } + }, + { + "add": { + "set": { + "family": "bridge", + "table": "proxmox-firewall-guests", + "name": "v6-sdn/public-dhcp-nomatch", + "type": "ipv6_addr", + "flags": [ + "interval" + ] + } + } + }, + { + "flush": { + "set": { + "family": "bridge", + "table": "proxmox-firewall-guests", + "name": "v6-sdn/public-dhcp-nomatch" + } + } + }, + { + "add": { + "element": { + "family": "bridge", + "table": "proxmox-firewall-guests", + "name": "v6-sdn/public-dhcp", + "elem": [ + { + "range": [ + "fd80::1000", + "fd80::ffff" + ] + } + ] + } + } + }, + { + "add": { + "set": { + "family": "bridge", + "table": "proxmox-firewall-guests", + "name": "v4-sdn/public-gateway", + "type": "ipv4_addr", + "flags": [ + "interval" + ] + } + } + }, + { + "flush": { + "set": { + "family": "bridge", + "table": "proxmox-firewall-guests", + "name": "v4-sdn/public-gateway" + } + } + }, + { + "add": { + "set": { + "family": "bridge", + "table": "proxmox-firewall-guests", + "name": "v4-sdn/public-gateway-nomatch", + "type": "ipv4_addr", + "flags": [ + "interval" + ] + } + } + }, + { + "flush": { + "set": { + "family": "bridge", + "table": "proxmox-firewall-guests", + "name": "v4-sdn/public-gateway-nomatch" + } + } + }, + { + "add": { + "element": { + "family": "bridge", + "table": "proxmox-firewall-guests", + "name": "v4-sdn/public-gateway", + "elem": [ + { + "prefix": { + "addr": "10.101.1.1", + "len": 32 + } + } + ] + } + } + }, + { + "add": { + "set": { + "family": "bridge", + "table": "proxmox-firewall-guests", + "name": "v6-sdn/public-gateway", + "type": "ipv6_addr", + "flags": [ + "interval" + ] + } + } + }, + { + "flush": { + "set": { + "family": "bridge", + "table": "proxmox-firewall-guests", + "name": "v6-sdn/public-gateway" + } + } + }, + { + "add": { + "set": { + "family": "bridge", + "table": "proxmox-firewall-guests", + "name": "v6-sdn/public-gateway-nomatch", + "type": "ipv6_addr", + "flags": [ + "interval" + ] + } + } + }, + { + "flush": { + "set": { + "family": "bridge", + "table": "proxmox-firewall-guests", + "name": "v6-sdn/public-gateway-nomatch" + } + } + }, + { + "add": { + "element": { + "family": "bridge", + "table": "proxmox-firewall-guests", + "name": "v6-sdn/public-gateway", + "elem": [ + { + "prefix": { + "addr": "fd80::1", + "len": 128 + } + } + ] + } + } + }, + { + "add": { + "set": { + "family": "bridge", + "table": "proxmox-firewall-guests", + "name": "v4-sdn/public-no-gateway", + "type": "ipv4_addr", + "flags": [ + "interval" + ] + } + } + }, + { + "flush": { + "set": { + "family": "bridge", + "table": "proxmox-firewall-guests", + "name": "v4-sdn/public-no-gateway" + } + } + }, + { + "add": { + "set": { + "family": "bridge", + "table": "proxmox-firewall-guests", + "name": "v4-sdn/public-no-gateway-nomatch", + "type": "ipv4_addr", + "flags": [ + "interval" + ] + } + } + }, + { + "flush": { + "set": { + "family": "bridge", + "table": "proxmox-firewall-guests", + "name": "v4-sdn/public-no-gateway-nomatch" + } + } + }, + { + "add": { + "element": { + "family": "bridge", + "table": "proxmox-firewall-guests", + "name": "v4-sdn/public-no-gateway", + "elem": [ + { + "prefix": { + "addr": "10.101.0.0", + "len": 16 + } + } + ] + } + } + }, + { + "add": { + "element": { + "family": "bridge", + "table": "proxmox-firewall-guests", + "name": "v4-sdn/public-no-gateway-nomatch", + "elem": [ + { + "prefix": { + "addr": "10.101.1.1", + "len": 32 + } + } + ] + } + } + }, + { + "add": { + "set": { + "family": "bridge", + "table": "proxmox-firewall-guests", + "name": "v6-sdn/public-no-gateway", + "type": "ipv6_addr", + "flags": [ + "interval" + ] + } + } + }, + { + "flush": { + "set": { + "family": "bridge", + "table": "proxmox-firewall-guests", + "name": "v6-sdn/public-no-gateway" + } + } + }, + { + "add": { + "set": { + "family": "bridge", + "table": "proxmox-firewall-guests", + "name": "v6-sdn/public-no-gateway-nomatch", + "type": "ipv6_addr", + "flags": [ + "interval" + ] + } + } + }, + { + "flush": { + "set": { + "family": "bridge", + "table": "proxmox-firewall-guests", + "name": "v6-sdn/public-no-gateway-nomatch" + } + } + }, + { + "add": { + "element": { + "family": "bridge", + "table": "proxmox-firewall-guests", + "name": "v6-sdn/public-no-gateway", + "elem": [ + { + "prefix": { + "addr": "fd80::", + "len": 64 + } + } + ] + } + } + }, + { + "add": { + "element": { + "family": "bridge", + "table": "proxmox-firewall-guests", + "name": "v6-sdn/public-no-gateway-nomatch", + "elem": [ + { + "prefix": { + "addr": "fd80::1", + "len": 128 + } + } + ] + } + } + }, + { + "add": { + "set": { + "family": "inet", + "table": "proxmox-firewall", + "name": "v4-sdn/guest-ipam-101", + "type": "ipv4_addr", + "flags": [ + "interval" + ] + } + } + }, + { + "flush": { + "set": { + "family": "inet", + "table": "proxmox-firewall", + "name": "v4-sdn/guest-ipam-101" + } + } + }, + { + "add": { + "set": { + "family": "inet", + "table": "proxmox-firewall", + "name": "v4-sdn/guest-ipam-101-nomatch", + "type": "ipv4_addr", + "flags": [ + "interval" + ] + } + } + }, + { + "flush": { + "set": { + "family": "inet", + "table": "proxmox-firewall", + "name": "v4-sdn/guest-ipam-101-nomatch" + } + } + }, + { + "add": { + "element": { + "family": "inet", + "table": "proxmox-firewall", + "name": "v4-sdn/guest-ipam-101", + "elem": [ + { + "prefix": { + "addr": "10.101.1.100", + "len": 32 + } + } + ] + } + } + }, + { + "add": { + "set": { + "family": "inet", + "table": "proxmox-firewall", + "name": "v6-sdn/guest-ipam-101", + "type": "ipv6_addr", + "flags": [ + "interval" + ] + } + } + }, + { + "flush": { + "set": { + "family": "inet", + "table": "proxmox-firewall", + "name": "v6-sdn/guest-ipam-101" + } + } + }, + { + "add": { + "set": { + "family": "inet", + "table": "proxmox-firewall", + "name": "v6-sdn/guest-ipam-101-nomatch", + "type": "ipv6_addr", + "flags": [ + "interval" + ] + } + } + }, + { + "flush": { + "set": { + "family": "inet", + "table": "proxmox-firewall", + "name": "v6-sdn/guest-ipam-101-nomatch" + } + } + }, + { + "add": { + "element": { + "family": "inet", + "table": "proxmox-firewall", + "name": "v6-sdn/guest-ipam-101", + "elem": [ + { + "prefix": { + "addr": "fd80::1000", + "len": 128 + } + } + ] + } + } + }, + { + "add": { + "set": { + "family": "bridge", + "table": "proxmox-firewall-guests", + "name": "v4-sdn/guest-ipam-101", + "type": "ipv4_addr", + "flags": [ + "interval" + ] + } + } + }, + { + "flush": { + "set": { + "family": "bridge", + "table": "proxmox-firewall-guests", + "name": "v4-sdn/guest-ipam-101" + } + } + }, + { + "add": { + "set": { + "family": "bridge", + "table": "proxmox-firewall-guests", + "name": "v4-sdn/guest-ipam-101-nomatch", + "type": "ipv4_addr", + "flags": [ + "interval" + ] + } + } + }, + { + "flush": { + "set": { + "family": "bridge", + "table": "proxmox-firewall-guests", + "name": "v4-sdn/guest-ipam-101-nomatch" + } + } + }, + { + "add": { + "element": { + "family": "bridge", + "table": "proxmox-firewall-guests", + "name": "v4-sdn/guest-ipam-101", + "elem": [ + { + "prefix": { + "addr": "10.101.1.100", + "len": 32 + } + } + ] + } + } + }, + { + "add": { + "set": { + "family": "bridge", + "table": "proxmox-firewall-guests", + "name": "v6-sdn/guest-ipam-101", + "type": "ipv6_addr", + "flags": [ + "interval" + ] + } + } + }, + { + "flush": { + "set": { + "family": "bridge", + "table": "proxmox-firewall-guests", + "name": "v6-sdn/guest-ipam-101" + } + } + }, + { + "add": { + "set": { + "family": "bridge", + "table": "proxmox-firewall-guests", + "name": "v6-sdn/guest-ipam-101-nomatch", + "type": "ipv6_addr", + "flags": [ + "interval" + ] + } + } + }, + { + "flush": { + "set": { + "family": "bridge", + "table": "proxmox-firewall-guests", + "name": "v6-sdn/guest-ipam-101-nomatch" + } + } + }, + { + "add": { + "element": { + "family": "bridge", + "table": "proxmox-firewall-guests", + "name": "v6-sdn/guest-ipam-101", + "elem": [ + { + "prefix": { + "addr": "fd80::1000", + "len": 128 + } + } + ] + } + } + }, { "add": { "set": { diff --git a/proxmox-nftables/src/expression.rs b/proxmox-nftables/src/expression.rs index 18b92d4..e56a15c 100644 --- a/proxmox-nftables/src/expression.rs +++ b/proxmox-nftables/src/expression.rs @@ -1,4 +1,5 @@ use crate::types::{ElemConfig, Verdict}; +use proxmox_ve_config::firewall::types::address::IpRange; use serde::{Deserialize, Serialize}; use std::net::{IpAddr, Ipv4Addr, Ipv6Addr}; @@ -50,6 +51,10 @@ pub enum Expression { } impl Expression { + pub fn range(start: impl Into<Expression>, last: impl Into<Expression>) -> Self { + Expression::Range(Box::new((start.into(), last.into()))) + } + pub fn set(expressions: impl IntoIterator<Item = Expression>) -> Self { Expression::Set(Vec::from_iter(expressions)) } @@ -169,12 +174,22 @@ impl From<&IpList> for Expression { } } +#[cfg(feature = "config-ext")] +impl From<&IpRange> for Expression { + fn from(value: &IpRange) -> Self { + match value { + IpRange::V4(range) => Expression::range(range.start(), range.last()), + IpRange::V6(range) => Expression::range(range.start(), range.last()), + } + } +} + #[cfg(feature = "config-ext")] impl From<&IpEntry> for Expression { fn from(value: &IpEntry) -> Self { match value { IpEntry::Cidr(cidr) => Expression::from(Prefix::from(cidr)), - IpEntry::Range(beg, end) => Expression::Range(Box::new((beg.into(), end.into()))), + IpEntry::Range(range) => Expression::from(range), } } } -- 2.39.5 _______________________________________________ pve-devel mailing list pve-devel@lists.proxmox.com https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel ^ permalink raw reply [flat|nested] 9+ messages in thread
* [pve-devel] [PATCH pve-firewall v4 4/9] add support for loading sdn firewall configuration 2024-11-15 12:09 [pve-devel] [PATCH docs/firewall/manager/proxmox{-firewall, -perl-rs} v4 0/9] autogenerate ipsets for sdn objects Stefan Hanreich 2024-11-15 12:09 ` [pve-devel] [PATCH proxmox-firewall v4 2/9] config: tests: add support for loading sdn and ipam config Stefan Hanreich 2024-11-15 12:09 ` [pve-devel] [PATCH proxmox-firewall v4 3/9] ipsets: autogenerate ipsets for vnets and ipam Stefan Hanreich @ 2024-11-15 12:09 ` Stefan Hanreich 2024-11-15 12:09 ` [pve-devel] [PATCH pve-firewall v4 5/9] nftables: make is_nftables check flag file instead of config Stefan Hanreich ` (4 subsequent siblings) 7 siblings, 0 replies; 9+ messages in thread From: Stefan Hanreich @ 2024-11-15 12:09 UTC (permalink / raw) To: pve-devel; +Cc: Wolfgang Bumiller This also includes support for parsing rules referencing IPSets in the new SDN scope and generating those IPSets in the firewall. Loading SDN configuration is optional, since loading it requires root privileges which we do not have in all call sites. Adding the flag allows us to selectively load the SDN configuration only where required and at the same time allows us to only elevate privileges in the API where absolutely needed. 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> --- src/PVE/Firewall.pm | 62 +++++++++++++++++++++++++++++---- src/PVE/Service/pve_firewall.pm | 4 +-- 2 files changed, 57 insertions(+), 9 deletions(-) diff --git a/src/PVE/Firewall.pm b/src/PVE/Firewall.pm index 09544ba..7642bf6 100644 --- a/src/PVE/Firewall.pm +++ b/src/PVE/Firewall.pm @@ -25,6 +25,7 @@ use PVE::Tools qw($IPV4RE $IPV6RE); use PVE::Tools qw(run_command lock_file dir_glob_foreach); use PVE::Firewall::Helpers; +use PVE::RS::Firewall::SDN; my $pvefw_conf_dir = "/etc/pve/firewall"; my $clusterfw_conf_filename = "$pvefw_conf_dir/cluster.fw"; @@ -1689,9 +1690,12 @@ sub verify_rule { if (my $value = $rule->{$name}) { if ($value =~ m/^\+/) { - if ($value =~ m@^\+(guest/|dc/)?(${ipset_name_pattern})$@) { + if ($value =~ m@^\+(guest/|dc/|sdn/)?(${ipset_name_pattern})$@) { &$add_error($name, "no such ipset '$2'") - if !($cluster_conf->{ipset}->{$2} || ($fw_conf && $fw_conf->{ipset}->{$2})); + if !($cluster_conf->{ipset}->{$2} + || ($fw_conf && $fw_conf->{ipset}->{$2}) + || ($cluster_conf->{sdn} && $cluster_conf->{sdn}->{ipset}->{$2}) + || ($fw_conf->{sdn} && $fw_conf->{sdn}->{ipset}->{$2})); } else { &$add_error($name, "invalid ipset name '$value'"); @@ -2108,13 +2112,15 @@ sub ipt_gen_src_or_dst_match { my $match; if ($adr =~ m/^\+/) { - if ($adr =~ m@^\+(guest/|dc/)?(${ipset_name_pattern})$@) { + if ($adr =~ m@^\+(guest/|dc/|sdn/)?(${ipset_name_pattern})$@) { my $scope = $1 // ""; my $name = $2; my $ipset_chain; - if ($scope ne 'dc/' && $fw_conf && $fw_conf->{ipset}->{$name}) { + if ((!$scope || $scope eq 'guest/') && $fw_conf && $fw_conf->{ipset}->{$name}) { $ipset_chain = compute_ipset_chain_name($fw_conf->{vmid}, $name, $ipversion); - } elsif ($scope ne 'guest/' && $cluster_conf && $cluster_conf->{ipset}->{$name}) { + } elsif ((!$scope || $scope eq 'dc/') && $cluster_conf && $cluster_conf->{ipset}->{$name}) { + $ipset_chain = compute_ipset_chain_name(0, $name, $ipversion); + } elsif ((!$scope || $scope eq 'sdn/') && $cluster_conf->{sdn} && $cluster_conf->{sdn}->{ipset}->{$name}) { $ipset_chain = compute_ipset_chain_name(0, $name, $ipversion); } else { die "no such ipset '$name'\n"; @@ -3644,7 +3650,8 @@ sub lock_clusterfw_conf { } sub load_clusterfw_conf { - my ($filename) = @_; + my ($filename, $options) = @_; + $filename = $clusterfw_conf_filename if !defined($filename); my $empty_conf = { @@ -3655,6 +3662,7 @@ sub load_clusterfw_conf { group_comments => {}, ipset => {} , ipset_comments => {}, + sdn => load_sdn_conf(), }; my $cluster_conf = generic_fw_config_parser($filename, $empty_conf, $empty_conf, 'cluster'); @@ -3663,6 +3671,45 @@ sub load_clusterfw_conf { return $cluster_conf; } +sub load_sdn_conf { + my $rpcenv = eval { PVE::RPCEnvironment::get(); }; + + if ($@) { + warn "could not load SDN configuration because RPCEnvironment is not initialized."; + return {}; + } + + my $authuser = $rpcenv->get_user(); + + my $guests = PVE::Cluster::get_vmlist(); + my $allowed_vms = []; + foreach my $vmid (sort keys %{$guests->{ids}}) { + next if !$rpcenv->check($authuser, "/vms/$vmid", [ 'VM.Audit' ], 1); + push @$allowed_vms, $vmid; + } + + my $vnets = PVE::Network::SDN::Vnets::config(1); + my $privs = [ 'SDN.Audit', 'SDN.Allocate' ]; + my $allowed_vnets = []; + foreach my $vnet (sort keys %{$vnets->{ids}}) { + my $zone = $vnets->{ids}->{$vnet}->{zone}; + next if !$rpcenv->check_any($authuser, "/sdn/zones/$zone/$vnet", $privs, 1); + push @$allowed_vnets, $vnet; + } + + my $sdn_config = { + ipset => {} , + ipset_comments => {}, + }; + + eval { + $sdn_config = PVE::RS::Firewall::SDN::config($allowed_vnets, $allowed_vms); + }; + warn $@ if $@; + + return $sdn_config; +} + sub save_clusterfw_conf { my ($cluster_conf) = @_; @@ -3768,7 +3815,7 @@ sub compile { $vmfw_configs = read_vm_firewall_configs($cluster_conf, $vmdata, $testdir); } else { # normal operation - $cluster_conf = load_clusterfw_conf(undef) if !$cluster_conf; + $cluster_conf = load_clusterfw_conf() if !$cluster_conf; $hostfw_conf = load_hostfw_conf($cluster_conf, undef) if !$hostfw_conf; @@ -4043,6 +4090,7 @@ sub compile_ipsets { } generate_ipset_chains($ipset_ruleset, undef, $cluster_conf, undef, $cluster_conf->{ipset}); + generate_ipset_chains($ipset_ruleset, undef, $cluster_conf, undef, $cluster_conf->{sdn}->{ipset}); return $ipset_ruleset; } diff --git a/src/PVE/Service/pve_firewall.pm b/src/PVE/Service/pve_firewall.pm index 65cb2b8..02b507a 100755 --- a/src/PVE/Service/pve_firewall.pm +++ b/src/PVE/Service/pve_firewall.pm @@ -158,7 +158,7 @@ __PACKAGE__->register_method ({ PVE::Firewall::set_verbose(1); # show syntax errors - my $cluster_conf = PVE::Firewall::load_clusterfw_conf(undef); + my $cluster_conf = PVE::Firewall::load_clusterfw_conf(); $res->{enable} = $cluster_conf->{options}->{enable} ? 1 : 0; if ($status eq 'running') { @@ -202,7 +202,7 @@ __PACKAGE__->register_method ({ PVE::Firewall::set_verbose(1); - my $cluster_conf = PVE::Firewall::load_clusterfw_conf(undef); + my $cluster_conf = PVE::Firewall::load_clusterfw_conf(); my ($ruleset, $ipset_ruleset, $rulesetv6, $ebtables_ruleset) = PVE::Firewall::compile($cluster_conf, undef, undef); print "ipset cmdlist:\n"; -- 2.39.5 _______________________________________________ pve-devel mailing list pve-devel@lists.proxmox.com https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel ^ permalink raw reply [flat|nested] 9+ messages in thread
* [pve-devel] [PATCH pve-firewall v4 5/9] nftables: make is_nftables check flag file instead of config 2024-11-15 12:09 [pve-devel] [PATCH docs/firewall/manager/proxmox{-firewall, -perl-rs} v4 0/9] autogenerate ipsets for sdn objects Stefan Hanreich ` (2 preceding siblings ...) 2024-11-15 12:09 ` [pve-devel] [PATCH pve-firewall v4 4/9] add support for loading sdn firewall configuration Stefan Hanreich @ 2024-11-15 12:09 ` Stefan Hanreich 2024-11-15 12:09 ` [pve-devel] [PATCH pve-firewall v4 6/9] api: load sdn ipsets Stefan Hanreich ` (3 subsequent siblings) 7 siblings, 0 replies; 9+ messages in thread From: Stefan Hanreich @ 2024-11-15 12:09 UTC (permalink / raw) To: pve-devel; +Cc: Wolfgang Bumiller is_nftables is used in the VM and CT network startup scripts to determine whether the nftables firewall is enabled or not. This causes issues on container and VM startup when loading the SDN config, since it requires the RPCEnvironment which is not initialized yet. Therefore change this check to look for the existence of the flag file instead. It also avoids parsing the entire cluster and host firewall configuration on VM / CT startup, which means increased performance. While we're at it, make all methods related to the configuration parsing private, in order to avoid accidental usage of the expensive methods. Signed-off-by: Stefan Hanreich <s.hanreich@proxmox.com> Reviewed-by: Wolfgang Bumiller <w.bumiller@proxmox.com> --- src/PVE/Firewall.pm | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/src/PVE/Firewall.pm b/src/PVE/Firewall.pm index 7642bf6..bfaa33a 100644 --- a/src/PVE/Firewall.pm +++ b/src/PVE/Firewall.pm @@ -4727,7 +4727,14 @@ sub remove_pvefw_chains_ebtables { ebtables_restore_cmdlist(get_ebtables_cmdlist({})); } +# This is checked in proxmox-firewall to avoid log-spam due to failing to parse the config +my $FORCE_NFT_DISABLE_FLAG_FILE = "/run/proxmox-nftables-firewall-force-disable"; + sub is_nftables { + return !-e $FORCE_NFT_DISABLE_FLAG_FILE; +} + +my sub get_nftables_option { my ($cluster_conf, $host_conf) = @_; if (!-x "/usr/libexec/proxmox/proxmox-firewall") { @@ -4743,9 +4750,6 @@ sub is_nftables { my sub update_force_nftables_disable_flag { my ($cluster_firewall_enabled, $is_nftables) = @_; - # This is checked in proxmox-firewall to avoid log-spam due to failing to parse the config - my $FORCE_NFT_DISABLE_FLAG_FILE = "/run/proxmox-nftables-firewall-force-disable"; - if (!($cluster_firewall_enabled && $is_nftables)) { if (! -e $FORCE_NFT_DISABLE_FLAG_FILE) { open(my $_fh, '>', $FORCE_NFT_DISABLE_FLAG_FILE) @@ -4757,13 +4761,13 @@ my sub update_force_nftables_disable_flag { } } -sub is_enabled_and_not_nftables { +my sub is_enabled_and_not_nftables { my ($cluster_conf, $host_conf) = @_; $cluster_conf = load_clusterfw_conf() if !defined($cluster_conf); $host_conf = load_hostfw_conf($cluster_conf) if !defined($host_conf); - my $is_nftables = is_nftables($cluster_conf, $host_conf); + my $is_nftables = get_nftables_option($cluster_conf, $host_conf); update_force_nftables_disable_flag($cluster_conf->{options}->{enable}, $is_nftables); -- 2.39.5 _______________________________________________ pve-devel mailing list pve-devel@lists.proxmox.com https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel ^ permalink raw reply [flat|nested] 9+ messages in thread
* [pve-devel] [PATCH pve-firewall v4 6/9] api: load sdn ipsets 2024-11-15 12:09 [pve-devel] [PATCH docs/firewall/manager/proxmox{-firewall, -perl-rs} v4 0/9] autogenerate ipsets for sdn objects Stefan Hanreich ` (3 preceding siblings ...) 2024-11-15 12:09 ` [pve-devel] [PATCH pve-firewall v4 5/9] nftables: make is_nftables check flag file instead of config Stefan Hanreich @ 2024-11-15 12:09 ` Stefan Hanreich 2024-11-15 12:09 ` [pve-devel] [PATCH proxmox-perl-rs v4 7/9] add PVE::RS::Firewall::SDN module Stefan Hanreich ` (2 subsequent siblings) 7 siblings, 0 replies; 9+ messages in thread From: Stefan Hanreich @ 2024-11-15 12:09 UTC (permalink / raw) To: pve-devel; +Cc: Wolfgang Bumiller Since the SDN configuration reads the IPAM config file, which resides in /etc/pve/priv we need to add the protected flag to several endpoints. 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> --- src/PVE/API2/Firewall/Aliases.pm | 2 ++ src/PVE/API2/Firewall/Cluster.pm | 7 ++++++- src/PVE/API2/Firewall/Groups.pm | 1 + src/PVE/API2/Firewall/Host.pm | 1 + src/PVE/API2/Firewall/IPSet.pm | 2 ++ src/PVE/API2/Firewall/Rules.pm | 2 ++ src/PVE/API2/Firewall/VM.pm | 5 ++++- 7 files changed, 18 insertions(+), 2 deletions(-) diff --git a/src/PVE/API2/Firewall/Aliases.pm b/src/PVE/API2/Firewall/Aliases.pm index 33ac669..2f947aa 100644 --- a/src/PVE/API2/Firewall/Aliases.pm +++ b/src/PVE/API2/Firewall/Aliases.pm @@ -87,6 +87,7 @@ sub register_get_aliases { path => '', method => 'GET', description => "List aliases", + protected => 1, permissions => PVE::Firewall::rules_audit_permissions($class->rule_env()), parameters => { additionalProperties => 0, @@ -177,6 +178,7 @@ sub register_read_alias { path => '{name}', method => 'GET', description => "Read alias.", + protected => 1, permissions => PVE::Firewall::rules_audit_permissions($class->rule_env()), parameters => { additionalProperties => 0, diff --git a/src/PVE/API2/Firewall/Cluster.pm b/src/PVE/API2/Firewall/Cluster.pm index 48ad90d..e519ab9 100644 --- a/src/PVE/API2/Firewall/Cluster.pm +++ b/src/PVE/API2/Firewall/Cluster.pm @@ -88,6 +88,7 @@ __PACKAGE__->register_method({ path => 'options', method => 'GET', description => "Get Firewall options.", + protected => 1, permissions => { check => ['perm', '/', [ 'Sys.Audit' ]], }, @@ -214,6 +215,7 @@ __PACKAGE__->register_method({ permissions => { check => ['perm', '/', [ 'Sys.Audit' ]], }, + protected => 1, parameters => { additionalProperties => 0, properties => { @@ -255,7 +257,10 @@ __PACKAGE__->register_method({ my $conf = PVE::Firewall::load_clusterfw_conf(); - return PVE::Firewall::Helpers::collect_refs($conf, $param->{type}, "dc"); + my $cluster_refs = PVE::Firewall::Helpers::collect_refs($conf, $param->{type}, "dc"); + my $sdn_refs = PVE::Firewall::Helpers::collect_refs($conf->{sdn}, $param->{type}, "sdn"); + + return [@$sdn_refs, @$cluster_refs]; }}); 1; diff --git a/src/PVE/API2/Firewall/Groups.pm b/src/PVE/API2/Firewall/Groups.pm index ffdc45c..98b0747 100644 --- a/src/PVE/API2/Firewall/Groups.pm +++ b/src/PVE/API2/Firewall/Groups.pm @@ -44,6 +44,7 @@ __PACKAGE__->register_method({ path => '', method => 'GET', description => "List security groups.", + protected => 1, permissions => { user => 'all' }, parameters => { additionalProperties => 0, diff --git a/src/PVE/API2/Firewall/Host.pm b/src/PVE/API2/Firewall/Host.pm index 0432de2..8bd5da1 100644 --- a/src/PVE/API2/Firewall/Host.pm +++ b/src/PVE/API2/Firewall/Host.pm @@ -68,6 +68,7 @@ __PACKAGE__->register_method({ path => 'options', method => 'GET', description => "Get host firewall options.", + protected => 1, proxyto => 'node', permissions => { check => ['perm', '/nodes/{node}', [ 'Sys.Audit' ]], diff --git a/src/PVE/API2/Firewall/IPSet.pm b/src/PVE/API2/Firewall/IPSet.pm index ed92d87..98c5443 100644 --- a/src/PVE/API2/Firewall/IPSet.pm +++ b/src/PVE/API2/Firewall/IPSet.pm @@ -91,6 +91,7 @@ sub register_get_ipset { path => '', method => 'GET', description => "List IPSet content", + protected => 1, permissions => PVE::Firewall::rules_audit_permissions($class->rule_env()), parameters => { additionalProperties => 0, @@ -586,6 +587,7 @@ sub register_index { path => '', method => 'GET', description => "List IPSets", + protected => 1, permissions => PVE::Firewall::rules_audit_permissions($class->rule_env()), parameters => { additionalProperties => 0, diff --git a/src/PVE/API2/Firewall/Rules.pm b/src/PVE/API2/Firewall/Rules.pm index 9fcfb20..9e903d4 100644 --- a/src/PVE/API2/Firewall/Rules.pm +++ b/src/PVE/API2/Firewall/Rules.pm @@ -72,6 +72,7 @@ sub register_get_rules { path => '', method => 'GET', description => "List rules.", + protected => 1, permissions => PVE::Firewall::rules_audit_permissions($rule_env), parameters => { additionalProperties => 0, @@ -120,6 +121,7 @@ sub register_get_rule { path => '{pos}', method => 'GET', description => "Get single rule data.", + protected => 1, permissions => PVE::Firewall::rules_audit_permissions($rule_env), parameters => { additionalProperties => 0, diff --git a/src/PVE/API2/Firewall/VM.pm b/src/PVE/API2/Firewall/VM.pm index 4222103..75b4345 100644 --- a/src/PVE/API2/Firewall/VM.pm +++ b/src/PVE/API2/Firewall/VM.pm @@ -69,6 +69,7 @@ sub register_handlers { path => 'options', method => 'GET', description => "Get VM firewall options.", + protected => 1, proxyto => 'node', permissions => { check => ['perm', '/vms/{vmid}', [ 'VM.Audit' ]], @@ -234,6 +235,7 @@ sub register_handlers { path => 'refs', method => 'GET', description => "Lists possible IPSet/Alias reference which are allowed in source/dest properties.", + protected => 1, permissions => { check => ['perm', '/vms/{vmid}', [ 'VM.Audit' ]], }, @@ -282,9 +284,10 @@ sub register_handlers { my $fw_conf = PVE::Firewall::load_vmfw_conf($cluster_conf, $rule_env, $param->{vmid}); my $dc_refs = PVE::Firewall::Helpers::collect_refs($cluster_conf, $param->{type}, 'dc'); + my $sdn_refs = PVE::Firewall::Helpers::collect_refs($cluster_conf->{sdn}, $param->{type}, 'sdn'); my $vm_refs = PVE::Firewall::Helpers::collect_refs($fw_conf, $param->{type}, 'guest'); - return [@$dc_refs, @$vm_refs]; + return [@$dc_refs, @$sdn_refs, @$vm_refs]; }}); } -- 2.39.5 _______________________________________________ pve-devel mailing list pve-devel@lists.proxmox.com https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel ^ permalink raw reply [flat|nested] 9+ messages in thread
* [pve-devel] [PATCH proxmox-perl-rs v4 7/9] add PVE::RS::Firewall::SDN module 2024-11-15 12:09 [pve-devel] [PATCH docs/firewall/manager/proxmox{-firewall, -perl-rs} v4 0/9] autogenerate ipsets for sdn objects Stefan Hanreich ` (4 preceding siblings ...) 2024-11-15 12:09 ` [pve-devel] [PATCH pve-firewall v4 6/9] api: load sdn ipsets Stefan Hanreich @ 2024-11-15 12:09 ` Stefan Hanreich 2024-11-15 12:09 ` [pve-devel] [PATCH pve-manager v4 8/9] firewall: add sdn scope to IPRefSelector Stefan Hanreich 2024-11-15 12:09 ` [pve-devel] [PATCH pve-docs v4 9/9] sdn: add documentation for firewall integration Stefan Hanreich 7 siblings, 0 replies; 9+ messages in thread From: Stefan Hanreich @ 2024-11-15 12:09 UTC (permalink / raw) To: pve-devel; +Cc: Wolfgang Bumiller 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> --- 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 9470cb7..c0af2b3 100644 --- a/pve-rs/Cargo.toml +++ b/pve-rs/Cargo.toml @@ -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" } 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<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) + } +} 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 ^ permalink raw reply [flat|nested] 9+ messages in thread
* [pve-devel] [PATCH pve-manager v4 8/9] firewall: add sdn scope to IPRefSelector 2024-11-15 12:09 [pve-devel] [PATCH docs/firewall/manager/proxmox{-firewall, -perl-rs} v4 0/9] autogenerate ipsets for sdn objects Stefan Hanreich ` (5 preceding siblings ...) 2024-11-15 12:09 ` [pve-devel] [PATCH proxmox-perl-rs v4 7/9] add PVE::RS::Firewall::SDN module Stefan Hanreich @ 2024-11-15 12:09 ` Stefan Hanreich 2024-11-15 12:09 ` [pve-devel] [PATCH pve-docs v4 9/9] sdn: add documentation for firewall integration Stefan Hanreich 7 siblings, 0 replies; 9+ messages in thread From: Stefan Hanreich @ 2024-11-15 12:09 UTC (permalink / raw) To: pve-devel Signed-off-by: Stefan Hanreich <s.hanreich@proxmox.com> Tested-by: Gabriel Goller <g.goller@proxmox.com> Tested-by: Hannes Dürr <h.duerr@proxmox.com> --- www/manager6/form/IPRefSelector.js | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/www/manager6/form/IPRefSelector.js b/www/manager6/form/IPRefSelector.js index d41cde5f5..16078e428 100644 --- a/www/manager6/form/IPRefSelector.js +++ b/www/manager6/form/IPRefSelector.js @@ -67,6 +67,12 @@ Ext.define('PVE.form.IPRefSelector', { }); } + let scopes = { + 'dc': gettext("Datacenter"), + 'guest': gettext("Guest"), + 'sdn': gettext("SDN"), + }; + columns.push( { header: gettext('Name'), @@ -80,7 +86,7 @@ Ext.define('PVE.form.IPRefSelector', { hideable: false, width: 140, renderer: function(value) { - return value === 'dc' ? gettext("Datacenter") : gettext("Guest"); + return scopes[value] ?? "unknown scope"; }, }, { -- 2.39.5 _______________________________________________ pve-devel mailing list pve-devel@lists.proxmox.com https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel ^ permalink raw reply [flat|nested] 9+ messages in thread
* [pve-devel] [PATCH pve-docs v4 9/9] sdn: add documentation for firewall integration 2024-11-15 12:09 [pve-devel] [PATCH docs/firewall/manager/proxmox{-firewall, -perl-rs} v4 0/9] autogenerate ipsets for sdn objects Stefan Hanreich ` (6 preceding siblings ...) 2024-11-15 12:09 ` [pve-devel] [PATCH pve-manager v4 8/9] firewall: add sdn scope to IPRefSelector Stefan Hanreich @ 2024-11-15 12:09 ` Stefan Hanreich 7 siblings, 0 replies; 9+ messages in thread From: Stefan Hanreich @ 2024-11-15 12:09 UTC (permalink / raw) To: pve-devel Signed-off-by: Stefan Hanreich <s.hanreich@proxmox.com> --- pvesdn.adoc | 92 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 92 insertions(+) diff --git a/pvesdn.adoc b/pvesdn.adoc index 39de80f..c187365 100644 --- a/pvesdn.adoc +++ b/pvesdn.adoc @@ -702,6 +702,98 @@ For more information please consult the documentation of xref:pvesdn_ipam_plugin_pveipam[the PVE IPAM plugin]. Changing DHCP leases is currently not supported for the other IPAM plugins. +Firewall Integration +-------------------- + +SDN integrates with the Proxmox VE firewall by automatically generating IPSets +which can then be referenced in the source / destination fields of firewall +rules. This happens automatically for VNets and IPAM entries. + +VNets and Subnets +~~~~~~~~~~~~~~~~~ + +The firewall automatically generates the following IPSets in the SDN scope for +every VNet: + +`vnet-all`:: + Contains the CIDRs of all subnets in a VNet +`vnet-gateway`:: + Contains the IPs of the gateways of all subnets in a VNet +`vnet-no-gateway`:: + Contains the CIDRs of all subnets in a VNet, but excludes the gateways +`vnet-dhcp`:: + Contains all DHCP ranges configured in the subnets in a VNet + +When making changes to your configuration, the IPSets update automatically, so +you do not have to update your firewall rules when changing the configuration of +your Subnets. + +Simple Zone Example +^^^^^^^^^^^^^^^^^^^ + +Assuming the configuration below for a VNet and its contained subnets: + +---- +# /etc/pve/sdn/vnets.cfg + +vnet: vnet0 + zone simple + +# /etc/pve/sdn/subnets.cfg + +subnet: simple-192.0.2.0-24 + vnet vnet0 + dhcp-range start-address=192.0.2.100,end-address=192.0.2.199 + gateway 192.0.2.1 + +subnet: simple-2001:db8::-64 + vnet vnet0 + dhcp-range start-address=2001:db8::1000,end-address=2001:db8::1999 + gateway 2001:db8::1 +---- + +In this example we configured an IPv4 subnet in the VNet `vnet0`, with +'192.0.2.0/24' as its IP Range, '192.0.2.1' as the gateway and the DHCP range is +'192.0.2.100' - '192.0.2.199'. + +Additionally we configured an IPv6 subnet with '2001:db8::/64' as the IP range, +'2001:db8::1' as the gateway and a DHCP range of '2001:db8::1000' - +'2001:db8::1999'. + +The respective auto-generated IPsets for vnet0 would then contain the following +elements: + +`vnet0-all`:: +* '192.0.2.0/24' +* '2001:db8::/64' +`vnet0-gateway`:: +* '192.0.2.1' +* '2001:db8::1' +`vnet0-no-gateway`:: +* '192.0.2.0/24' +* '2001:db8::/64' +* '!192.0.2.1' +* '!2001:db8::1' +`vnet0-dhcp`:: +* '192.0.2.100 - 192.0.2.199' +* '2001:db8::1000 - 2001:db8::1999' + +IPAM +~~~~ + +If you are using the built-in PVE IPAM, then the firewall automatically +generates an IPset for every guest that has entries in the IPAM. The respective +IPset for a guest with ID 100 would be `guest-ipam-100`. It contains all IP +addresses from all IPAM entries. So if guest 100 is member of multiple VNets, +then the IPset would contain the IPs from *all* VNets. + +When entries get added / updated / deleted, then the respective IPSets will be +updated accordingly. + +WARNING: When removing all entries for a guest and there are firewall rules +still referencing the auto-generated IPSet then the firewall will fail to update +the ruleset, since it references a non-existing IPSet. + [[pvesdn_setup_examples]] Examples -------- -- 2.39.5 _______________________________________________ pve-devel mailing list pve-devel@lists.proxmox.com https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel ^ permalink raw reply [flat|nested] 9+ messages in thread
end of thread, other threads:[~2024-11-15 12:10 UTC | newest] Thread overview: 9+ messages (download: mbox.gz / follow: Atom feed) -- links below jump to the message on this page -- 2024-11-15 12:09 [pve-devel] [PATCH docs/firewall/manager/proxmox{-firewall, -perl-rs} v4 0/9] autogenerate ipsets for sdn objects Stefan Hanreich 2024-11-15 12:09 ` [pve-devel] [PATCH proxmox-firewall v4 2/9] config: tests: add support for loading sdn and ipam config Stefan Hanreich 2024-11-15 12:09 ` [pve-devel] [PATCH proxmox-firewall v4 3/9] ipsets: autogenerate ipsets for vnets and ipam Stefan Hanreich 2024-11-15 12:09 ` [pve-devel] [PATCH pve-firewall v4 4/9] add support for loading sdn firewall configuration Stefan Hanreich 2024-11-15 12:09 ` [pve-devel] [PATCH pve-firewall v4 5/9] nftables: make is_nftables check flag file instead of config Stefan Hanreich 2024-11-15 12:09 ` [pve-devel] [PATCH pve-firewall v4 6/9] api: load sdn ipsets Stefan Hanreich 2024-11-15 12:09 ` [pve-devel] [PATCH proxmox-perl-rs v4 7/9] add PVE::RS::Firewall::SDN module Stefan Hanreich 2024-11-15 12:09 ` [pve-devel] [PATCH pve-manager v4 8/9] firewall: add sdn scope to IPRefSelector Stefan Hanreich 2024-11-15 12:09 ` [pve-devel] [PATCH pve-docs v4 9/9] sdn: add documentation for firewall integration Stefan Hanreich
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox