From: Hannes Laimer <h.laimer@proxmox.com>
To: pve-devel@lists.proxmox.com
Subject: [PATCH pve-network v3 24/45] sdn: microseg: enforce the policy on guest NICs
Date: Tue, 27 Oct 2026 09:55:06 +0100 [thread overview]
Message-ID: <20261027085527.702738-25-h.laimer@proxmox.com> (raw)
In-Reply-To: <20261027085527.702738-1-h.laimer@proxmox.com>
A guest NIC is enforced when it is plugged, and if attaching the
enforcement fails, so does the guest start. A full pass enforces
every guest NIC of the node. The package runs one when it is
configured and when libpve-rs-perl is upgraded, since the BPF
programs are compiled into that library.
A running config without a microseg block, as a version without
microseg support writes it, says nothing about the policy. A pass and
a plug leave enforcement as it is and warn where a policy is
configured.
Signed-off-by: Hannes Laimer <h.laimer@proxmox.com>
---
debian/libpve-network-perl.postinst | 21 ++++++++++
debian/triggers | 1 +
src/PVE/Network/SDN/Microseg.pm | 65 ++++++++++++++++++++++++++++-
src/PVE/Network/SDN/Zones.pm | 5 +++
4 files changed, 91 insertions(+), 1 deletion(-)
diff --git a/debian/libpve-network-perl.postinst b/debian/libpve-network-perl.postinst
index 99faedf48f56..88e9675b6928 100644
--- a/debian/libpve-network-perl.postinst
+++ b/debian/libpve-network-perl.postinst
@@ -22,6 +22,18 @@ migrate_mac_cache() {
fi
}
+# The BPF programs are compiled into libpve-rs-perl, whose upgrade fires the
+# pve-microseg-update trigger so a full pass installs the new build. When both
+# packages are upgraded in one run, this package is only unpacked when the
+# library is set up, so dpkg drops the trigger and the configure runs the pass.
+apply_microseg() {
+ test -f /etc/pve/local/pve-ssl.pem || return 0
+ test -e /proxmox_install_mode && return 0
+
+ perl -MPVE::Network::SDN::Microseg -e 'PVE::Network::SDN::Microseg::apply_all()' \
+ || echo "applying the microsegmentation policy failed, run an SDN apply or a network reload"
+}
+
case "$1" in
configure)
# TODO: remove with PVE 9+
@@ -29,6 +41,15 @@ case "$1" in
migrate_ipam_db
migrate_mac_cache
fi
+ apply_microseg
+ ;;
+
+ triggered)
+ case " $2 " in
+ *" pve-microseg-update "*)
+ apply_microseg
+ ;;
+ esac
;;
esac
diff --git a/debian/triggers b/debian/triggers
index 59dd6885a5f9..30113fe73944 100644
--- a/debian/triggers
+++ b/debian/triggers
@@ -1 +1,2 @@
activate-noawait pve-api-updates
+interest-noawait pve-microseg-update
diff --git a/src/PVE/Network/SDN/Microseg.pm b/src/PVE/Network/SDN/Microseg.pm
index 05c432d4eec7..3cd9c3a8ed35 100644
--- a/src/PVE/Network/SDN/Microseg.pm
+++ b/src/PVE/Network/SDN/Microseg.pm
@@ -5,10 +5,13 @@ use warnings;
use PVE::Cluster qw(cfs_register_file cfs_read_file cfs_write_file);
use PVE::JSONSchema qw(get_standard_option);
+use PVE::RESTEnvironment qw(log_warn);
use PVE::RPCEnvironment;
use PVE::Tools;
use PVE::RS::SDN::Microseg;
+use PVE::RS::SDN::Microseg::ExclusiveLock;
+use PVE::RS::SDN::Microseg::SharedLock;
PVE::JSONSchema::register_format(
'pve-sdn-microseg-id',
@@ -245,9 +248,11 @@ sub optional_properties {
}
sub guest_inventory {
+ my (%opts) = @_;
+
my @props = map { "net$_" } 0 .. $MAX_NIC_INDEX;
- my $by_vmid = PVE::Cluster::get_guest_config_properties(\@props);
+ my $by_vmid = PVE::Cluster::get_guest_config_properties(\@props, $opts{vmid});
my $inventory = [];
for my $vmid (sort { $a <=> $b } keys %$by_vmid) {
@@ -279,4 +284,62 @@ sub type_matches {
return (grep { $_ eq $got } @$want) ? 1 : 0;
}
+my sub parse_guest_nic {
+ my ($iface) = @_;
+
+ return $iface =~ m/^(?:tap|veth)(\d+)i(\d+)$/;
+}
+
+# the guest as the inventory lists it, with nothing but its vmid when it has no NICs
+my sub guest_info {
+ my ($vmid) = @_;
+
+ my ($guest) = guest_inventory(vmid => $vmid)->@*;
+
+ return $guest // { vmid => int($vmid), nics => [] };
+}
+
+# an unmounted pmxcfs reads like a config that was never committed, and enforcement would be dropped
+my sub read_running_config {
+ PVE::Cluster::check_cfs_is_mounted();
+ return PVE::Cluster::get_config('sdn/.running-config');
+}
+
+# a config that cannot be read may still hold a policy
+my sub is_configured {
+ my $sections = eval { config()->to_sections() };
+ return !defined($sections) || scalar(%$sections);
+}
+
+sub apply_interface {
+ my ($iface, $firewall_bridge) = @_;
+
+ my ($vmid) = parse_guest_nic($iface);
+ return if !defined($vmid);
+
+ $firewall_bridge = $firewall_bridge ? 1 : 0;
+
+ my $lock = PVE::RS::SDN::Microseg::SharedLock->new();
+ my $raw = read_running_config();
+ my $guest = guest_info($vmid);
+ if (!$lock->apply_iface($raw, $guest, $iface, $firewall_bridge)) {
+ log_warn("microseg: the running config carries no microseg block,"
+ . " '$iface' stays unenforced. looks like a node is running an older version")
+ if is_configured();
+ }
+}
+
+sub apply_all {
+ eval {
+ my $lock = PVE::RS::SDN::Microseg::ExclusiveLock->new();
+ my $raw = read_running_config();
+ $lock->apply($raw, guest_inventory())
+ or !is_configured()
+ or log_warn("microseg: the running config carries no microseg block, as written"
+ . " before the upgrade or by a node without microsegmentation support, leaving"
+ . " enforcement unchanged");
+ };
+ die "microseg: failed to apply running config: $@" if $@;
+}
+
1;
diff --git a/src/PVE/Network/SDN/Zones.pm b/src/PVE/Network/SDN/Zones.pm
index efcbeb3c1444..da24ef16dddb 100644
--- a/src/PVE/Network/SDN/Zones.pm
+++ b/src/PVE/Network/SDN/Zones.pm
@@ -11,6 +11,7 @@ use PVE::IPRoute2;
use PVE::Network;
use PVE::Network::SDN::Vnets;
+use PVE::Network::SDN::Microseg;
use PVE::Network::SDN::Zones::VlanPlugin;
use PVE::Network::SDN::Zones::QinQPlugin;
use PVE::Network::SDN::Zones::VxlanPlugin;
@@ -364,6 +365,10 @@ sub veth_create {
sub tap_plug {
my ($iface, $bridge, $tag, $firewall, $trunks, $rate) = @_;
+ # attach enforcement before bridging, so an assigned NIC never passes traffic unenforced
+ eval { PVE::Network::SDN::Microseg::apply_interface($iface, $firewall) };
+ die "microseg: refusing to bring up '$iface' unenforced: $@" if $@;
+
my $vnet = PVE::Network::SDN::Vnets::get_vnet($bridge, 1);
if (!$vnet) { # fallback for classic bridge
my $interfaces_config = PVE::INotify::read_file('interfaces');
--
2.47.3
next prev parent reply other threads:[~2026-10-27 8:59 UTC|newest]
Thread overview: 46+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-10-27 8:54 [PATCH access-control/cluster/container/docs/manager/network/proxmox{-ebpf,-ve-rs,-perl-rs}/qemu-server v3 00/45] sdn: add microsegmentation support Hannes Laimer
2026-10-27 8:54 ` [PATCH proxmox-ebpf v3 01/45] build: keep the build directory out of the BPF objects Hannes Laimer
2026-10-27 8:54 ` [PATCH proxmox-ebpf v3 02/45] policy: add the enforcement subsystem for guest interfaces Hannes Laimer
2026-10-27 8:54 ` [PATCH proxmox-ebpf v3 03/45] policy: add a check for non-first fragments Hannes Laimer
2026-10-27 8:54 ` [PATCH proxmox-ve-rs v3 04/45] ve-config: firewall: add a constructor for sctp matches Hannes Laimer
2026-10-27 8:54 ` [PATCH proxmox-ve-rs v3 05/45] ve-config: sdn: microseg: add identity allocation for group sets Hannes Laimer
2026-10-27 8:54 ` [PATCH proxmox-ve-rs v3 06/45] ve-config: sdn: microseg: add config types Hannes Laimer
2026-10-27 8:54 ` [PATCH proxmox-ve-rs v3 07/45] ve-config: sdn: microseg: add tag matcher Hannes Laimer
2026-10-27 8:54 ` [PATCH proxmox-ve-rs v3 08/45] ve-config: sdn: microseg: add name regex matcher Hannes Laimer
2026-10-27 8:54 ` [PATCH proxmox-ve-rs v3 09/45] ve-config: sdn: microseg: narrow rules by protocol and ports Hannes Laimer
2026-10-27 8:54 ` [PATCH proxmox-ve-rs v3 10/45] ve-config: sdn: microseg: add group hierarchy Hannes Laimer
2026-10-27 8:54 ` [PATCH proxmox-ve-rs v3 11/45] ve-config: firewall: expose protocol, icmp type and code numbers Hannes Laimer
2026-10-27 8:54 ` [PATCH proxmox-ve-rs v3 12/45] ve-config: sdn: microseg: add the desired state Hannes Laimer
2026-10-27 8:54 ` [PATCH proxmox-ve-rs v3 13/45] microseg: add a crate that applies the policy to a node's guest NICs Hannes Laimer
2026-10-27 8:54 ` [PATCH proxmox-ve-rs v3 14/45] microseg: add a rule option that matches non-first fragments Hannes Laimer
2026-10-27 8:54 ` [PATCH pve-cluster v3 15/45] cfs: add 'sdn/microseg.cfg' to observed files Hannes Laimer
2026-10-27 8:54 ` [PATCH pve-access-control v3 16/45] permissions: add ACL path for SDN microsegmentation Hannes Laimer
2026-10-27 8:54 ` [PATCH proxmox-perl-rs v3 17/45] pve-rs: sdn: add microseg config binding Hannes Laimer
2026-10-27 8:55 ` [PATCH proxmox-perl-rs v3 18/45] pve-rs: sdn: microseg: bind the data plane Hannes Laimer
2026-10-27 8:55 ` [PATCH proxmox-perl-rs v3 19/45] pve-rs: sdn: microseg: check the data plane limits on render Hannes Laimer
2026-10-27 8:55 ` [PATCH proxmox-perl-rs v3 20/45] pve-rs: debian: fire the pve-microseg-update trigger on upgrade Hannes Laimer
2026-10-27 8:55 ` [PATCH pve-network v3 21/45] sdn: zones: add vxlan-gbp option to vxlan and evpn zones Hannes Laimer
2026-10-27 8:55 ` [PATCH pve-network v3 22/45] sdn: microseg: add config, API and guest inventory Hannes Laimer
2026-10-27 8:55 ` [PATCH pve-network v3 23/45] sdn: dry-run: show pending microseg changes Hannes Laimer
2026-10-27 8:55 ` Hannes Laimer [this message]
2026-10-27 8:55 ` [PATCH pve-network v3 25/45] sdn: microseg: add tag matcher Hannes Laimer
2026-10-27 8:55 ` [PATCH pve-network v3 26/45] sdn: microseg: add name regex matcher Hannes Laimer
2026-10-27 8:55 ` [PATCH pve-network v3 27/45] sdn: microseg: add an update for a running NIC Hannes Laimer
2026-10-27 8:55 ` [PATCH pve-network v3 28/45] sdn: microseg: expose protocol, port and macro rule options Hannes Laimer
2026-10-27 8:55 ` [PATCH pve-network v3 29/45] sdn: microseg: allow nesting groups under a parent Hannes Laimer
2026-10-27 8:55 ` [PATCH pve-network v3 30/45] sdn: microseg: expose the fragments rule option Hannes Laimer
2026-10-27 8:55 ` [PATCH pve-manager v3 31/45] network: apply the microseg policy on reload Hannes Laimer
2026-10-27 8:55 ` [PATCH pve-manager v3 32/45] pve-sdn-commit: apply the microseg policy at boot Hannes Laimer
2026-10-27 8:55 ` [PATCH pve-manager v3 33/45] ui: sdn: zones: add vxlan-gbp checkbox to vxlan and evpn Hannes Laimer
2026-10-27 8:55 ` [PATCH pve-manager v3 34/45] ui: sdn: add microsegmentation panel Hannes Laimer
2026-10-27 8:55 ` [PATCH pve-manager v3 35/45] ui: sdn: dry-run: show pending microseg diff Hannes Laimer
2026-10-27 8:55 ` [PATCH pve-manager v3 36/45] ui: sdn: microseg: add tag matcher Hannes Laimer
2026-10-27 8:55 ` [PATCH pve-manager v3 37/45] ui: sdn: microseg: add name regex matcher Hannes Laimer
2026-10-27 8:55 ` [PATCH pve-manager v3 38/45] ui: sdn: microseg: add narrowing to the rule form Hannes Laimer
2026-10-27 8:55 ` [PATCH pve-manager v3 39/45] ui: sdn: microseg: show and edit the group hierarchy Hannes Laimer
2026-10-27 8:55 ` [PATCH pve-manager v3 40/45] ui: sdn: microseg: add fragments option to the rule form Hannes Laimer
2026-10-27 8:55 ` [PATCH pve-manager v3 41/45] ui: sdn: microseg: add a guest tab with its groups and rules Hannes Laimer
2026-10-27 8:55 ` [PATCH qemu-server v3 42/45] network: update the microseg enforcement on a tag or name change Hannes Laimer
2026-10-27 8:55 ` [PATCH pve-container v3 43/45] lxc: update the microseg enforcement on a tag or hostname change Hannes Laimer
2026-10-27 8:55 ` [PATCH pve-docs v3 44/45] sdn: zones: document the VXLAN-GBP option Hannes Laimer
2026-10-27 8:55 ` [PATCH pve-docs v3 45/45] sdn: add microsegmentation section Hannes Laimer
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=20261027085527.702738-25-h.laimer@proxmox.com \
--to=h.laimer@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