Skip to content

Let mdb_admin manage resource groups#1804

Open
Alena0704 wants to merge 1 commit into
apache:REL_2_STABLEfrom
Alena0704:REL_2_STABLE-mdb_admin-resource-group
Open

Let mdb_admin manage resource groups#1804
Alena0704 wants to merge 1 commit into
apache:REL_2_STABLEfrom
Alena0704:REL_2_STABLE-mdb_admin-resource-group

Conversation

@Alena0704
Copy link
Copy Markdown
Contributor

@Alena0704 Alena0704 commented Jun 4, 2026

Let mdb_admin manage resource groups
In Greenplum/Cloudberry only a superuser may CREATE/ALTER/DROP resource
groups. In managed-service deployments superuser is never handed to
the client, so a cloud admin has no way to tune their own CPU/memory
limits.

The upstream-shaped fix is a predefined role pinned in pg_authid.dat.
That is correct for master, but a new bootstrap catalog entry bumps
CATALOG_VERSION_NO and thus needs a fresh initdb / gpupgrade.
We must be able to grant this capability between minor versions.

So instead of pinning an OID, gate the four entry points on membership
of the mdb_admin role, resolved by name at runtime:

role = get_role_oid("mdb_admin", true);
if (!is_member_of_role(GetUserId(), role))
    ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), ...));

mdb_admin is an ordinary role created by the control plane with a plain
CREATE ROLE, not a built-in catalog role. If the role does not exist,
get_role_oid returns InvalidOid, so on a stock cluster only superusers
pass the check and nothing changes. The capability is enabled only
where the control plane creates the role.

admin_group stays superuser-only for ALTER/DROP: it is infrastructure,
not a user-tunable group.

This commit is adapted from open-gpdb/gpdb commit 3ac99962ad2.
Some tests are added from apache/cloudberry PR #1763.

Fixes #ISSUE_Number

What does this PR do?

Type of Change

  • Bug fix (non-breaking change)
  • New feature (non-breaking change)
  • Breaking change (fix or feature with breaking changes)
  • Documentation update

Breaking Changes

Test Plan

  • Unit tests added/updated
  • Integration tests added/updated
  • Passed make installcheck
  • Passed make -C src/test installcheck-cbdb-parallel

Impact

Performance:

User-facing changes:

Dependencies:

Checklist

Additional Context

CI Skip Instructions


Comment thread src/backend/commands/resgroupcmds.c Outdated
Comment thread src/include/catalog/pg_authid.dat Outdated
Comment thread src/include/catalog/pg_authid.dat Outdated
Comment thread src/backend/commands/resgroupcmds.c
@Alena0704 Alena0704 force-pushed the REL_2_STABLE-mdb_admin-resource-group branch from 3cf18f1 to c429871 Compare June 4, 2026 07:35
@Alena0704
Copy link
Copy Markdown
Contributor Author

Alena0704 commented Jun 4, 2026

I rewrote the patch completely because a new bootstrap catalog entry bumps CATALOG_VERSION_NO and incompatible with minor gpupgrade - I adapted the commit from open-gpdb 3ac99962ad2 and added my tests.

@Alena0704 Alena0704 changed the title Add predefined role pg_manage_resource_groups Let mdb_admin manage resource groups Jun 4, 2026
@Alena0704 Alena0704 force-pushed the REL_2_STABLE-mdb_admin-resource-group branch 3 times, most recently from de4b7f8 to 2e1505d Compare June 4, 2026 14:36
@my-ship-it
Copy link
Copy Markdown
Contributor

This PR's permission check looks up the role by name, not by a fixed OID:

  role = get_role_oid("mdb_admin", true);   /* look up the role by the name "mdb_admin" */
  if (!is_member_of_role(GetUserId(), role))
      ereport(ERROR, ...);

This means: members of any role that happens to be named mdb_admin can manage resource groups. The code doesn't care who created that role or when.

Where the hole is: This assumption overlooks CREATEROLE users. Suppose a DBA has granted some user CREATEROLE (quite common on self-hosted clusters,
e.g. to let someone manage ordinary user accounts):

  -- Executed as an ordinary CREATEROLE user:
  CREATE ROLE mdb_admin;          -- CREATEROLE allows creating any non-superuser role ✓
  GRANT mdb_admin TO myself;      -- Under PG14 semantics, a CREATEROLE user can grant
                                  -- any non-superuser role to anyone, including themselves ✓
  ALTER RESOURCE GROUP default_group SET concurrency 1;  -- now passes the permission check!

Three SQL statements, and a user who was only supposed to "manage accounts" has self-granted resource group administration rights.

@Alena0704
Copy link
Copy Markdown
Contributor Author

Alena0704 commented Jun 5, 2026

This PR's permission check looks up the role by name, not by a fixed OID:

  role = get_role_oid("mdb_admin", true);   /* look up the role by the name "mdb_admin" */
  if (!is_member_of_role(GetUserId(), role))
      ereport(ERROR, ...);

This means: members of any role that happens to be named mdb_admin can manage resource groups. The code doesn't care who created that role or when.

Where the hole is: This assumption overlooks CREATEROLE users. Suppose a DBA has granted some user CREATEROLE (quite common on self-hosted clusters, e.g. to let someone manage ordinary user accounts):

  -- Executed as an ordinary CREATEROLE user:
  CREATE ROLE mdb_admin;          -- CREATEROLE allows creating any non-superuser role ✓
  GRANT mdb_admin TO myself;      -- Under PG14 semantics, a CREATEROLE user can grant
                                  -- any non-superuser role to anyone, including themselves ✓
  ALTER RESOURCE GROUP default_group SET concurrency 1;  -- now passes the permission check!

Three SQL statements, and a user who was only supposed to "manage accounts" has self-granted resource group administration rights.

Agree, I have fixed it by checking that it is included in admin and system group.

In Greenplum/Cloudberry only a superuser may CREATE/ALTER/DROP resource
groups. In managed-service deployments superuser is never handed to
the client, so a cloud admin has no way to tune their own CPU/memory
limits.

The upstream-shaped fix is a predefined role pinned in pg_authid.dat.
That is correct for master, but a new bootstrap catalog entry bumps
CATALOG_VERSION_NO and thus needs a fresh initdb / gpupgrade.
We must be able to grant this capability between minor versions.

So instead of pinning an OID, gate the four entry points on membership
of the mdb_admin role, resolved by name at runtime:

    role = get_role_oid("mdb_admin", true);
    if (!is_member_of_role(GetUserId(), role))
        ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), ...));

mdb_admin is an ordinary role created by the control plane with a plain
CREATE ROLE, not a built-in catalog role. If the role does not exist,
get_role_oid returns InvalidOid, so on a stock cluster only superusers
pass the check and nothing changes. The capability is enabled only
where the control plane creates the role.

admin_group stays superuser-only for ALTER/DROP: it is infrastructure,
not a user-tunable group.

This commit is adapted from open-gpdb/gpdb commit 3ac99962ad2.
Some tests are added from apache/cloudberry PR apache#1763.
@Alena0704 Alena0704 force-pushed the REL_2_STABLE-mdb_admin-resource-group branch from 2e1505d to 2ea6898 Compare June 5, 2026 12:37
@reshke
Copy link
Copy Markdown
Contributor

reshke commented Jun 5, 2026

Where the hole is: This assumption overlooks CREATEROLE users. Suppose a DBA has granted some user CREATEROLE (quite common on self-hosted clusters, e.g. to let someone manage ordinary user accounts):

  -- Executed as an ordinary CREATEROLE user:
  CREATE ROLE mdb_admin;          -- CREATEROLE allows creating any non-superuser role ✓
  GRANT mdb_admin TO myself;      -- Under PG14 semantics, a CREATEROLE user can grant
                                  -- any non-superuser role to anyone, including themselves ✓
  ALTER RESOURCE GROUP default_group SET concurrency 1;  -- now passes the permission check!

Three SQL statements, and a user who was only supposed to "manage accounts" has self-granted resource group administration rights.

for our use in Cloud we just do not consider this as privilege escalation, because we grant this role to cloud users(non-superuser) by request (you just click in UI like that this role to this role)

Anyway issue looks valid in general use

@reshke
Copy link
Copy Markdown
Contributor

reshke commented Jun 5, 2026

This PR's permission check looks up the role by name, not by a fixed OID:

  role = get_role_oid("mdb_admin", true);   /* look up the role by the name "mdb_admin" */
  if (!is_member_of_role(GetUserId(), role))
      ereport(ERROR, ...);

This means: members of any role that happens to be named mdb_admin can manage resource groups. The code doesn't care who created that role or when.

Yep, exactly :) We only care about rolsuper or not, if mdb-admin patch does not allow to gain superuser priviledge - we consider it safe :)

@my-ship-it
Copy link
Copy Markdown
Contributor

This PR's permission check looks up the role by name, not by a fixed OID:

  role = get_role_oid("mdb_admin", true);   /* look up the role by the name "mdb_admin" */
  if (!is_member_of_role(GetUserId(), role))
      ereport(ERROR, ...);

This means: members of any role that happens to be named mdb_admin can manage resource groups. The code doesn't care who created that role or when.

Yep, exactly :) We only care about rolsuper or not, if mdb-admin patch does not allow to gain superuser priviledge - we consider it safe :)

Thanks for the iterations on this. I agree with the constraint driving the current design — a
new bootstrap entry in pg_authid.dat bumps CATALOG_VERSION_NO and breaks minor-version upgrades,
so a runtime lookup is the right shape for the stable branch.

How about Proposal: use a reserved-prefix role name

Rename the role to e.g. pg_manage_resgroup and keep the same runtime get_role_oid() lookup. The
point is that the pg_ namespace is already reserved for roles:

  • CreateRole() rejects any pg_-prefixed name via IsReservedName() (src/backend/commands/user.c),
    so a CREATEROLE user cannot forge it;
  • RenameRole() rejects renaming any role to a pg_ name, so it cannot be smuggled in via ALTER
    ROLE ... RENAME either;

One caveat: DROP is not name-protected

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants