Package: release.debian.org
Severity: normal
Tags: trixie
X-Debbugs-Cc: [email protected]
Control: affects -1 + src:neutron
User: [email protected]
Usertags: pu

Hi,

[ Reason ]
I'd like to fix a security issue where the policy for tags on
floating-ips shouldn't be granted. For more details, see the
announce from upstream:

https://security.openstack.org/ossa/OSSA-2026-016.html

[ Impact ]
tagging policy bypass allows project readers to mutate tags

[ Tests ]
Upstream unit tests are run during package build.

[ Risks ]
Change isn't big, only to the policy (upstream added some
policy.enforce() calls).

[ Checklist ]
  [x] *all* changes are documented in the d/changelog
  [x] I reviewed all changes and I approve them
  [x] attach debdiff against the package in (old)stable
  [x] the issue is verified as fixed in unstable

[ Changes ]
Just the upstream patch.

[ Other info ]
Note the attached debdiff targeted trixie-security, I'll
adjust for p-u before uploading.

Please allow me to uplaod Neutron 2:26.0.0-9+deb13u1.

Cheers,

Thomas Goirand (zigo)
diff -Nru neutron-26.0.0/debian/changelog neutron-26.0.0/debian/changelog
--- neutron-26.0.0/debian/changelog     2025-07-13 17:16:33.000000000 +0200
+++ neutron-26.0.0/debian/changelog     2026-05-29 08:24:56.000000000 +0200
@@ -1,3 +1,11 @@
+neutron (2:26.0.0-9+deb13u1) trixie-security; urgency=medium
+
+  * OSSA-2026-016: Neutron tagging policy bypass allows project readers to
+    mutate tags. Added upstream patch: "Fix plural policy names in tagging
+    controller and floatingip policy" (Closes: #1138172).
+
+ -- Thomas Goirand <[email protected]>  Fri, 29 May 2026 08:24:56 +0200
+
 neutron (2:26.0.0-9) unstable; urgency=medium
 
   * Replace Add_state_reporting_back_to_metadata_agents.patch by a version
diff -Nru 
neutron-26.0.0/debian/patches/OSSA-2026-016_Fix_plural_policy_names_in_tagging_controller_and_floatingip_policy.patch
 
neutron-26.0.0/debian/patches/OSSA-2026-016_Fix_plural_policy_names_in_tagging_controller_and_floatingip_policy.patch
--- 
neutron-26.0.0/debian/patches/OSSA-2026-016_Fix_plural_policy_names_in_tagging_controller_and_floatingip_policy.patch
       1970-01-01 01:00:00.000000000 +0100
+++ 
neutron-26.0.0/debian/patches/OSSA-2026-016_Fix_plural_policy_names_in_tagging_controller_and_floatingip_policy.patch
       2026-05-29 08:24:56.000000000 +0200
@@ -0,0 +1,150 @@
+Author: Rodolfo Alonso Hernandez <[email protected]>
+Date: Tue, 19 May 2026 12:47:59 +0200
+Description: Fix plural policy names in tagging controller and floatingip 
policy
+ The TaggingController.create() and update() methods enforce policy
+ action names using the plural collection key (e.g. create_networks:tags)
+ instead of the singular member name (e.g. create_network:tags). Since
+ the registered policy rules use the singular form, the unmatched plural
+ names fall through to oslo.policy's default rule, allowing project
+ readers to mutate tags on same-project resources.
+ .
+ Fix the delete_floatingips:tags policy rule name (should be singular
+ delete_floatingip:tags) and add a unit test that validates
+ _get_policy_action produces the correct singular form for all supported
+ resources and actions, and that each generated name matches an actually
+ registered policy rule.
+Bug: https://launchpad.net/bugs/2150132
+Bug-Debian: https://bugs.debian.org/1138172
+Signed-off-by: Rodolfo Alonso Hernandez <[email protected]>
+Change-Id: I783510565e4fc4191b5494eb9a6dc0bdd3ace3fc
+Origin: upstream, https://review.opendev.org/c/openstack/neutron/+/989376
+Last-Update: 2026-05-29
+
+diff --git a/neutron/conf/policies/floatingip.py 
b/neutron/conf/policies/floatingip.py
+index 9a3eaaf..ae99279 100644
+--- a/neutron/conf/policies/floatingip.py
++++ b/neutron/conf/policies/floatingip.py
+@@ -170,7 +170,7 @@
+             deprecated_since=versionutils.deprecated.WALLABY)
+     ),
+     policy.DocumentedRuleDefault(
+-        name='delete_floatingips:tags',
++        name='delete_floatingip:tags',
+         check_str=base.ADMIN_OR_PROJECT_MEMBER,
+         description='Delete the floating IP tags',
+         operations=ACTION_DELETE_TAGS,
+diff --git a/neutron/extensions/tagging.py b/neutron/extensions/tagging.py
+index 543f2dc..b88cda0 100644
+--- a/neutron/extensions/tagging.py
++++ b/neutron/extensions/tagging.py
+@@ -197,8 +197,10 @@
+         # GET /v2.0/{obj_resource}/{obj_resource_id}/tags
+         ctx = request.context
+         rinfo = self._get_resource_info(ctx, kwargs)
+-        policy.enforce(ctx, 'get_{}_{}'.format(rinfo.obj_type, TAGS),
+-                       rinfo.obj)
++        policy.enforce(
++            ctx,
++            self._get_policy_action("get", rinfo.obj_type),
++            rinfo.obj)
+         return self.plugin.get_tags(ctx, rinfo.obj_type, rinfo.obj['id'])
+ 
+     def show(self, request, id, **kwargs):
+@@ -207,8 +209,10 @@
+         validate_tag(id)
+         ctx = request.context
+         rinfo = self._get_resource_info(ctx, kwargs)
+-        policy.enforce(ctx, 'get_{}:{}'.format(rinfo.obj_type, TAGS),
+-                       rinfo.obj)
++        policy.enforce(
++            ctx,
++            self._get_policy_action("get", rinfo.obj_type),
++            rinfo.obj)
+         return self.plugin.get_tag(ctx, rinfo.obj_type, rinfo.obj['id'], id)
+ 
+     def create(self, request, body, **kwargs):
+@@ -217,8 +221,10 @@
+         validate_tags(body)
+         ctx = request.context
+         rinfo = self._get_resource_info(ctx, kwargs, tags=body[TAGS])
+-        policy.enforce(ctx, 'create_{}:{}'.format(rinfo.obj_type, TAGS),
+-                       rinfo.obj)
++        policy.enforce(
++            ctx,
++            self._get_policy_action("create", rinfo.obj_type),
++            rinfo.obj)
+         validate_tags_limit(rinfo.obj_type, body['tags'])
+         notify_tag_action(ctx, 'create.start', rinfo.obj_type,
+                           rinfo.obj['id'], body['tags'])
+@@ -234,8 +240,10 @@
+         validate_tag(id)
+         ctx = request.context
+         rinfo = self._get_resource_info(ctx, kwargs, tags=[id])
+-        policy.enforce(ctx, 'update_{}:{}'.format(rinfo.obj_type, TAGS),
+-                       rinfo.obj)
++        policy.enforce(
++            ctx,
++            self._get_policy_action("update", rinfo.obj_type),
++            rinfo.obj)
+         current_tags = self.plugin.get_tags(
+             ctx, rinfo.obj_type, rinfo.obj['id'])['tags']
+         new_tags = current_tags + [id]
+diff --git a/neutron/tests/unit/extensions/test_tagging.py 
b/neutron/tests/unit/extensions/test_tagging.py
+index 8366fb7..56d2b7f 100644
+--- a/neutron/tests/unit/extensions/test_tagging.py
++++ b/neutron/tests/unit/extensions/test_tagging.py
+@@ -24,6 +24,7 @@
+ from neutron_lib.utils import net as net_utils
+ from oslo_utils import uuidutils
+ 
++from neutron.conf import policies as conf_policies
+ from neutron.extensions import tagging
+ from neutron.objects import network as network_obj
+ from neutron.objects import network_segment_range as network_segment_range_obj
+@@ -87,6 +88,22 @@
+         ovo_resources = set(tagging.OVO_CLS.keys())
+         self.assertEqual(tc_supported_resources, ovo_resources)
+ 
++    def test__get_policy_action_all_resources_and_actions(self):
++        registered_rules = {rule.name for rule in conf_policies.list_rules()}
++        actions = ('get', 'create', 'update', 'delete')
++        for collection, member in self.tc.supported_resources.items():
++            for action in actions:
++                expected = f'{action}_{member}:tags'
++                result = self.tc._get_policy_action(action, collection)
++                self.assertEqual(
++                    expected, result,
++                    f'_get_policy_action("{action}", "{collection}") '
++                    f'returned "{result}" instead of "{expected}"')
++                self.assertIn(
++                    result, registered_rules,
++                    f'Policy rule "{result}" is not registered in '
++                    f'neutron.conf.policies')
++
+     def _check_resource_info(self, obj, obj_type):
+         id_key = self.tc.supported_resources[obj_type] + '_id'
+         res = self.tc._get_resource_info(self.ctx, {id_key: obj['id']})
+diff --git 
a/releasenotes/notes/fix-tagging-policy-action-names-a59e17308f214600.yaml 
b/releasenotes/notes/fix-tagging-policy-action-names-a59e17308f214600.yaml
+new file mode 100644
+index 0000000..91ee65c
+--- /dev/null
++++ b/releasenotes/notes/fix-tagging-policy-action-names-a59e17308f214600.yaml
+@@ -0,0 +1,18 @@
++---
++security:
++  - |
++    Fixed a security issue where the tagging controller's single-tag write
++    endpoints (``POST /v2.0/{resource}/{id}/tags`` and
++    ``PUT /v2.0/{resource}/{id}/tags/{tag}``) enforced policy action names
++    using the plural collection key (e.g. ``create_networks:tags``) instead
++    of the singular member name (e.g. ``create_network:tags``). Because the
++    plural names did not match any registered policy rule, they fell through
++    to oslo.policy's default rule, allowing project readers to create and
++    update tags on same-project resources.
++    All tag-write endpoints now consistently use the singular policy action
++    names that match the registered rules.
++fixes:
++  - |
++    Fixed the ``delete_floatingips:tags`` policy rule name to use the correct
++    singular form ``delete_floatingip:tags``, consistent with all other
++    tag-related policy rules.
diff -Nru neutron-26.0.0/debian/patches/series 
neutron-26.0.0/debian/patches/series
--- neutron-26.0.0/debian/patches/series        2025-07-13 17:16:33.000000000 
+0200
+++ neutron-26.0.0/debian/patches/series        2026-05-29 08:24:56.000000000 
+0200
@@ -1,3 +1,4 @@
 fix-path-of-healthcheck_disable.patch
 Add_state_reporting_back_to_metadata_agents.patch
 Fix_LoopingCallBase_argument_issue.patch
+OSSA-2026-016_Fix_plural_policy_names_in_tagging_controller_and_floatingip_policy.patch

Reply via email to