ipsets: autogenerate ipsets for vnets and ipam

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>
This commit is contained in:
Stefan Hanreich 2024-11-15 13:09:31 +01:00 committed by Thomas Lamprecht
parent 669f99801b
commit 6f01ca780b
4 changed files with 1354 additions and 14 deletions

View File

@ -197,6 +197,27 @@ impl Firewall {
self.reset_firewall(&mut commands); self.reset_firewall(&mut commands);
let cluster_host_table = Self::cluster_table(); 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() { if self.config.host().is_enabled() {
log::info!("creating cluster / host configuration"); log::info!("creating cluster / host configuration");
@ -242,7 +263,6 @@ impl Firewall {
commands.push(Delete::table(TableName::from(Self::cluster_table()))); commands.push(Delete::table(TableName::from(Self::cluster_table())));
} }
let guest_table = Self::guest_table();
let enabled_guests: BTreeMap<&Vmid, &GuestConfig> = self let enabled_guests: BTreeMap<&Vmid, &GuestConfig> = self
.config .config
.guests() .guests()

View File

@ -72,20 +72,37 @@ impl ToNftObjects for Ipset {
let mut nomatch_elements = Vec::new(); let mut nomatch_elements = Vec::new();
for element in self.iter() { for element in self.iter() {
let cidr = match &element.address { let expression = match &element.address {
IpsetAddress::Cidr(cidr) => cidr, IpsetAddress::Range(range) => {
IpsetAddress::Alias(alias) => env if family != range.family() {
.alias(alias) continue;
.ok_or(format_err!("could not find alias {alias} in environment"))? }
.address(),
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 { if element.nomatch {
nomatch_elements.push(expression); nomatch_elements.push(expression);
} else { } else {

View File

@ -1,4 +1,5 @@
use crate::types::{ElemConfig, Verdict}; use crate::types::{ElemConfig, Verdict};
use proxmox_ve_config::firewall::types::address::IpRange;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr}; use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
@ -50,6 +51,10 @@ pub enum Expression {
} }
impl 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 { pub fn set(expressions: impl IntoIterator<Item = Expression>) -> Self {
Expression::Set(Vec::from_iter(expressions)) 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")] #[cfg(feature = "config-ext")]
impl From<&IpEntry> for Expression { impl From<&IpEntry> for Expression {
fn from(value: &IpEntry) -> Self { fn from(value: &IpEntry) -> Self {
match value { match value {
IpEntry::Cidr(cidr) => Expression::from(Prefix::from(cidr)), 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),
} }
} }
} }