all lists on lists.proxmox.com
 help / color / mirror / Atom feed
From: Arthur Bied-Charreton <a.bied-charreton@proxmox.com>
To: pve-devel@lists.proxmox.com
Subject: [PATCH pve-firewall v6 13/28] firewall: references: handle security groups
Date: Wed, 11 Nov 2026 14:18:46 +0100	[thread overview]
Message-ID: <20261111131901.1791124-14-a.bied-charreton@proxmox.com> (raw)
In-Reply-To: <20261111131901.1791124-1-a.bied-charreton@proxmox.com>

Security groups may be referenced by the action of a rule, rather than
the source, dest and cidr fields that ipsets and aliases are referenced
in.

Move field lists into the object spec and let a spec limit its
references to a single rule type so a group named 'drop' is not matched
by the action of a normal DROP rule.

Groups may only be defined in the cluster config so they cannot be
shadowed and references to them are always unscoped.

Signed-off-by: Arthur Bied-Charreton <a.bied-charreton@proxmox.com>
---
 src/PVE/Firewall/References.pm | 52 +++++++++++++++++++++-------------
 src/test/test_update_refs.pl   | 38 +++++++++++++++++++++++++
 2 files changed, 70 insertions(+), 20 deletions(-)

diff --git a/src/PVE/Firewall/References.pm b/src/PVE/Firewall/References.pm
index 2fb8767..baefccc 100644
--- a/src/PVE/Firewall/References.pm
+++ b/src/PVE/Firewall/References.pm
@@ -157,27 +157,37 @@ sub rewrite_refs_in_env {
     return $changes;
 }
 
-# field name + value prefix for firewall object references
+# how a reference to a firewall object looks and where it is found:
+#
+#   prefix: the prefix a reference carries
+#   self:   the section a config defines the object in, used to resolve shadowing
+#   fields: the fields that can hold a reference
+#   type:   the rule type references are limited to
 my $object_ref_specs = {
-    ipset => { prefix => '+', self => 'ipset' },
-    aliases => { prefix => '', self => 'aliases' },
+    ipset => { prefix => '+', self => 'ipset', fields => ['source', 'dest', 'cidr'] },
+    aliases => { prefix => '', self => 'aliases', fields => ['source', 'dest', 'cidr'] },
+    # a security group may only be referenced by the action of the rule invoking it. matching
+    # any other rule could rewrite a DROP action when renaming a group named 'drop', for example.
+    groups => { prefix => '', self => 'groups', fields => ['action'], type => 'group' },
 };
 
-# fields to check for object references
-my $ref_fields = ['source', 'dest', 'cidr'];
+=head3 matched_ref($item, $ctx)
 
-=head3 matched_ref($item, $repl)
-
-Return the reference C<$item> holds that C<$repl> has an entry for, or C<undef> if there is none.
+Return the reference C<$item> holds that the replacement map of C<$ctx> has an entry for, or
+C<undef> if there is none.
 
 =cut
 
 sub matched_ref {
-    my ($item, $repl) = @_;
+    my ($item, $ctx) = @_;
+
+    my $spec = $ctx->{spec};
 
-    for my $field ($ref_fields->@*) {
+    return undef if $spec->{type} && ($item->{type} // '') ne $spec->{type};
+
+    for my $field ($spec->{fields}->@*) {
         my $ref = lc($item->{$field} // '');
-        return $ref if exists($repl->{$ref});
+        return $ref if exists($ctx->{repl}->{$ref});
     }
 
     return undef;
@@ -209,7 +219,7 @@ sub rewrite_items {
     for my $pos (0 .. $items->$#*) {
         my $item = $items->[$pos];
 
-        my $ref = matched_ref($item, $repl);
+        my $ref = matched_ref($item, $ctx);
         if (!defined($ref)) {
             # no match, keep item
             push @result, $item;
@@ -229,7 +239,7 @@ sub rewrite_items {
         next if $remove;
 
         if ($action eq 'rename') {
-            for my $field ($ref_fields->@*) {
+            for my $field ($ctx->{spec}->{fields}->@*) {
                 my $value = lc($item->{$field} // '');
                 $item->{$field} = $repl->{$value} if exists($repl->{$value});
             }
@@ -243,7 +253,7 @@ sub rewrite_items {
     return \@result;
 }
 
-=head3 build_ref_map($op, $conf, $env, $is_guest)
+=head3 build_ref_map($op, $spec, $conf, $env, $is_guest)
 
 Map every allowed reference to the objects in C<$op> that resolves in C<$conf> to what it should
 become, i.e. either the renamed reference or C<undef> if C<$op> has no new name.
@@ -266,9 +276,7 @@ a guest-local or a cluster object
 =cut
 
 sub build_ref_map {
-    my ($op, $conf, $env, $is_guest) = @_;
-
-    my $spec = $object_ref_specs->{ $op->{kind} } // die "unknown object kind '$op->{kind}'\n";
+    my ($op, $spec, $conf, $env, $is_guest) = @_;
 
     my $prefix = $spec->{prefix};
     my $self = $spec->{self};
@@ -323,8 +331,11 @@ Only exposed for testing, see C<update_refs>'s POD for details.
 sub rewrite_refs_in_conf {
     my ($conf, $op, $env, $is_guest) = @_;
 
+    my $spec = $object_ref_specs->{ $op->{kind} } // die "unknown object kind '$op->{kind}'\n";
+
     my $ctx = {
-        repl => build_ref_map($op, $conf, $env, $is_guest),
+        spec => $spec,
+        repl => build_ref_map($op, $spec, $conf, $env, $is_guest),
         action => $op->{action},
         changes => [],
     };
@@ -360,7 +371,7 @@ and renames are written back lowercased.
 C<$opts> holds:
 
     conf     # the firewall config the object is defined in
-    kind     # 'ipset' or 'aliases'
+    kind     # 'ipset', 'aliases' or 'groups'
     old      # the old name, or an arrayref of names to disable or drop
     new      # the name to rewrite them references to 'old' to, only used when renaming
     env      # 'cluster', 'sdn', or 'vm'/'ct' for a guest-defined object
@@ -371,7 +382,8 @@ C<$opts> holds:
 
 The caller is responsible for locking and saving C<conf> itself.
 
-IPSet members have no disabled state, so C<disable> removes them as well.
+A security group may only be referenced by the action of a rule so those references are confined to
+rules. IPSet members have no disabled state, so C<disable> removes them as well.
 
 If C<conf> is the cluster config, i.e. if C<env> is C<cluster> or C<sdn>, guest, host and vnet
 configs are sequentially locked, updated and saved. C<save> is called once the references in
diff --git a/src/test/test_update_refs.pl b/src/test/test_update_refs.pl
index 36afff7..b4262b3 100755
--- a/src/test/test_update_refs.pl
+++ b/src/test/test_update_refs.pl
@@ -325,6 +325,44 @@ subtest 'rename changes record the new reference' => sub {
     );
 };
 
+subtest 'groups: rename rewrites the action of group rules' => sub {
+    my $conf = {
+        rules => [
+            { type => 'group', action => 'web' },
+            { type => 'in', action => 'ACCEPT', source => 'web' },
+        ],
+        groups => {
+            web => [{ type => 'in', action => 'ACCEPT' }],
+        },
+        ipset => {
+            set => [{ cidr => 'web' }],
+        },
+    };
+
+    my $changes = rewrite($conf, 'groups', 'web', 'www', 'cluster', 0);
+
+    is($conf->{rules}->[0]->{action}, 'www', 'group rule points at the new name');
+    is($conf->{rules}->[1]->{action}, 'ACCEPT', 'a plain rule keeps its action');
+    is($conf->{rules}->[1]->{source}, 'web', 'a source matching the group name is left alone');
+    is($conf->{ipset}->{set}->[0]->{cidr}, 'web', 'an IPSet member is left alone');
+    is(scalar($changes->@*), 1, 'only the group rule is reported');
+};
+
+subtest 'groups: a group named after an action does not match plain rules' => sub {
+    my $conf = {
+        rules => [
+            { type => 'out', action => 'DROP' }, { type => 'group', action => 'drop' },
+        ],
+    };
+
+    my $changes = rewrite($conf, 'groups', 'drop', undef, 'cluster', 0);
+
+    is($conf->{rules}->[0]->{action}, 'DROP', 'the DROP rule is untouched');
+    ok(!$conf->{rules}->[0]->{enable}, 'the DROP rule is not disabled');
+    is($conf->{rules}->[1]->{enable}, 0, 'the group rule is disabled');
+    is(scalar($changes->@*), 1, 'only the group rule is reported');
+};
+
 subtest 'update_refs: rejects invalid argument combinations' => sub {
     # monkey patches to prevent tests from actually walking the cluster
     no warnings qw(redefine once);
-- 
2.47.3





  parent reply	other threads:[~2026-11-11 13:21 UTC|newest]

Thread overview: 29+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-11-11 13:18 [PATCH container/docs/firewall/manager/network/qemu-server v6 00/28] handle dangling references when firewall objects go away Arthur Bied-Charreton
2026-11-11 13:18 ` [PATCH pve-firewall v6 01/28] firewall: run make tidy Arthur Bied-Charreton
2026-11-11 13:18 ` [PATCH pve-firewall v6 02/28] firewall: move object reference helpers to their own module Arthur Bied-Charreton
2026-11-11 13:18 ` [PATCH pve-firewall v6 03/28] firewall: references: add helpers to update object references Arthur Bied-Charreton
2026-11-11 13:18 ` [PATCH pve-firewall v6 04/28] firewall: references: log the references an object update touched Arthur Bied-Charreton
2026-11-11 13:18 ` [PATCH pve-firewall v6 05/28] firewall: references: add wrappers for the SDN-generated IPSets Arthur Bied-Charreton
2026-11-11 13:18 ` [PATCH pve-firewall v6 06/28] firewall: parser: do not log errors for disabled rules Arthur Bied-Charreton
2026-11-11 13:18 ` [PATCH pve-firewall v6 07/28] firewall: ipset: add option to handle dangling references on rename Arthur Bied-Charreton
2026-11-11 13:18 ` [PATCH pve-firewall v6 08/28] firewall: ipset: add option to handle dangling references on delete Arthur Bied-Charreton
2026-11-11 13:18 ` [PATCH pve-firewall v6 09/28] firewall: aliases: add option to handle dangling references on rename Arthur Bied-Charreton
2026-11-11 13:18 ` [PATCH pve-firewall v6 10/28] firewall: aliases: add option to handle dangling references on delete Arthur Bied-Charreton
2026-11-11 13:18 ` [PATCH pve-firewall v6 11/28] firewall: api: run reference updates as tasks Arthur Bied-Charreton
2026-11-11 13:18 ` [PATCH pve-firewall v6 12/28] firewall: tests: add tests for object reference updates Arthur Bied-Charreton
2026-11-11 13:18 ` Arthur Bied-Charreton [this message]
2026-11-11 13:18 ` [PATCH pve-firewall v6 14/28] firewall: groups: add option to handle dangling references on rename Arthur Bied-Charreton
2026-11-11 13:18 ` [PATCH pve-firewall v6 15/28] fix #8176: firewall: groups: handle dangling references on delete Arthur Bied-Charreton
2026-11-11 13:18 ` [PATCH pve-network v6 16/28] apply: add option to handle dangling references on VNet deletion Arthur Bied-Charreton
2026-11-11 13:18 ` [PATCH qemu-server v6 17/28] api: destroy_vm: add option to handle dangling IPSet references Arthur Bied-Charreton
2026-11-11 13:18 ` [PATCH pve-container v6 18/28] " Arthur Bied-Charreton
2026-11-11 13:18 ` [PATCH pve-manager v6 19/28] ui: firewall: add a common widget for handling dangling references Arthur Bied-Charreton
2026-11-11 13:18 ` [PATCH pve-manager v6 20/28] ui: firewall: ipset: add controls for " Arthur Bied-Charreton
2026-11-11 13:18 ` [PATCH pve-manager v6 21/28] ui: firewall: aliases: " Arthur Bied-Charreton
2026-11-11 13:18 ` [PATCH pve-manager v6 22/28] ui: sdn: apply: add control for dangling IPSet references Arthur Bied-Charreton
2026-11-11 13:18 ` [PATCH pve-manager v6 23/28] ui: guest destroy: use let for non-constant variable bindings Arthur Bied-Charreton
2026-11-11 13:18 ` [PATCH pve-manager v6 24/28] ui: guest destroy: add control for dangling IPSet references Arthur Bied-Charreton
2026-11-11 13:18 ` [PATCH pve-manager v6 25/28] ui: firewall: show the task for reference updates Arthur Bied-Charreton
2026-11-11 13:18 ` [PATCH pve-manager v6 26/28] ui: firewall: groups: add controls for dangling references Arthur Bied-Charreton
2026-11-11 13:19 ` [PATCH pve-docs v6 27/28] firewall: remove trailing spaces Arthur Bied-Charreton
2026-11-11 13:19 ` [PATCH pve-docs v6 28/28] firewall: document renaming and removing objects Arthur Bied-Charreton

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=20261111131901.1791124-14-a.bied-charreton@proxmox.com \
    --to=a.bied-charreton@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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.
Service provided by Proxmox Server Solutions GmbH | Privacy | Legal