Skip to content

Commit 0a65125

Browse files
Alena0704x4mreshke
authored
Allow non-superuser role to manage resource groups.
Historically Yandex Greenplum allows non-superuser no managed resource groups. So, a regular non-superuser role allowed to run pg_resgroup_move_query(), and tune CPU/memory limits if granted with mdb_admin. Such feature was introduced as early as 6.22, see also gpdb commit 3ac99962. This commit introduces same feature for managed Cloudberry. To disallow altering predefined roles, fixed-OID hardening is used, reserving 8067 OID to be an mdb_admin role OID. We choose this (efficiently a catalog change) over complex bookkeeping what CREATEROLE can do and what is disallowed. We use Yandex managed predefined roles bootstrap util via auxiliary contrib extension, based on what Yandex Postgres fork does, see also pg-sharding/cpg repo. Co-authored-by: Andrey Borodin <x4mmm@yandex-team.ru> Co-authored-by: reshke <reshke@double.cloud>
1 parent d2f1685 commit 0a65125

22 files changed

Lines changed: 631 additions & 23 deletions

.github/workflows/build-cloudberry-rocky8.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -311,6 +311,7 @@ jobs:
311311
"contrib/pgstattuple:installcheck",
312312
"contrib/tablefunc:installcheck",
313313
"contrib/passwordcheck:installcheck",
314+
"contrib/pg_aux_catalog:installcheck",
314315
"contrib/pg_buffercache:installcheck",
315316
"contrib/sslinfo:installcheck"]
316317
},

.github/workflows/build-cloudberry.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -308,6 +308,7 @@ jobs:
308308
"contrib/pgstattuple:installcheck",
309309
"contrib/tablefunc:installcheck",
310310
"contrib/passwordcheck:installcheck",
311+
"contrib/pg_aux_catalog:installcheck",
311312
"contrib/pg_buffercache:installcheck",
312313
"contrib/sslinfo:installcheck"]
313314
},

.github/workflows/build-deb-cloudberry.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -243,6 +243,7 @@ jobs:
243243
"contrib/pgstattuple:installcheck",
244244
"contrib/tablefunc:installcheck",
245245
"contrib/passwordcheck:installcheck",
246+
"contrib/pg_aux_catalog:installcheck",
246247
"contrib/pg_buffercache:installcheck",
247248
"contrib/sslinfo:installcheck"]
248249
},

contrib/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ SUBDIRS = \
3434
old_snapshot \
3535
pageinspect \
3636
passwordcheck \
37+
pg_aux_catalog \
3738
postgres_fdw \
3839
pg_buffercache \
3940
pg_freespacemap \

contrib/pg_aux_catalog/.gitignore

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
# Generated test output
2+
/log/
3+
/results/
4+
/tmp_check/
5+
/isolation2/results/
6+
/isolation2/output_iso/

contrib/pg_aux_catalog/Makefile

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
# contrib/pg_aux_catalog/Makefile
2+
3+
MODULE_big = pg_aux_catalog
4+
OBJS = \
5+
$(WIN32RES) \
6+
pg_aux_catalog.o
7+
8+
EXTENSION = pg_aux_catalog
9+
DATA = pg_aux_catalog--1.0.sql
10+
11+
PGFILEDESC = "pg_aux_catalog - auxiliary catalog management"
12+
13+
REGRESS = pg_aux_catalog
14+
REGRESS_OPTS = --init-file=$(top_srcdir)/src/test/regress/init_file
15+
16+
ifdef USE_PGXS
17+
PG_CONFIG = pg_config
18+
PGXS := $(shell $(PG_CONFIG) --pgxs)
19+
include $(PGXS)
20+
else
21+
subdir = contrib/pg_aux_catalog
22+
top_builddir = ../..
23+
include $(top_builddir)/src/Makefile.global
24+
include $(top_srcdir)/contrib/contrib-global.mk
25+
endif
26+
27+
# Multi-session resource-group permission tests, run with the GPDB isolation2
28+
# harness. Requires a running cluster with resource groups enabled
29+
# (gp_resource_manager=group); see installcheck-resgroup in
30+
# src/test/isolation2. Not part of the default "installcheck".
31+
installcheck-isolation2: install
32+
$(pg_isolation2_regress_installcheck) \
33+
--init-file=$(top_builddir)/src/test/regress/init_file \
34+
--inputdir=$(srcdir)/isolation2 \
35+
--outputdir=isolation2 \
36+
--schedule=$(srcdir)/isolation2/isolation2_schedule
37+

contrib/pg_aux_catalog/README.md

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
# pg_aux_catalog
2+
3+
Auxiliary catalog management for Apache Cloudberry.
4+
5+
This extension provisions the **`mdb_admin`** privilege role, which lets a
6+
non-superuser manage resource groups in managed-service deployments where the
7+
client is never given superuser.
8+
9+
## Background
10+
11+
In Greenplum/Cloudberry only a superuser may `CREATE`/`ALTER`/`DROP` resource
12+
groups or move a running query between groups with `pg_resgroup_move_query()`.
13+
The server gates those four entry points on membership of `mdb_admin`,
14+
identified by a **fixed OID (8067)** rather than by name, so the privilege is
15+
recognised reliably across the coordinator and all segments.
16+
17+
A fixed OID cannot be obtained from a plain `CREATE ROLE` (that assigns an
18+
ordinary OID). This extension provides the one supported way to create the
19+
role at OID 8067.
20+
21+
## Functions
22+
23+
### `pg_create_mdb_admin_role() returns oid`
24+
25+
Creates the `mdb_admin` role with its fixed OID (8067).
26+
Returns the OID of the created role (8067). Errors if a role with that OID or
27+
the name `mdb_admin` already exists. The OID assignment is dispatched to the
28+
segments, so the role has the same OID cluster-wide.
29+
30+
## Usage
31+
32+
```sql
33+
CREATE EXTENSION pg_aux_catalog;
34+
35+
-- Provision the role (the control plane does this once per cluster).
36+
SELECT pg_create_mdb_admin_role();
37+
38+
-- Grant the capability to a tenant admin.
39+
GRANT mdb_admin TO cloud_admin;
40+
41+
-- cloud_admin can now manage resource groups without superuser:
42+
SET ROLE cloud_admin;
43+
CREATE RESOURCE GROUP rg_tenant WITH (concurrency = 4, cpu_max_percent = 20);
44+
ALTER RESOURCE GROUP rg_tenant SET cpu_max_percent 30;
45+
DROP RESOURCE GROUP rg_tenant;
46+
```
47+
48+
`admin_group` and `system_group` remain superuser-only for `ALTER`/`DROP`:
49+
they are infrastructure, not user-tunable groups.
50+
51+
## Building and testing
52+
53+
```sh
54+
make -C contrib/pg_aux_catalog install
55+
make -C contrib/pg_aux_catalog installcheck
56+
```
57+
58+
`installcheck` runs a single-session regression test (role creation and the
59+
resource-group permission gate). A multi-session isolation2 test covering the
60+
dispatched / cross-session behaviour lives under `isolation2/` and is run
61+
separately, against a cluster with resource groups enabled
62+
(`gp_resource_manager=group`):
63+
64+
```sh
65+
make -C contrib/pg_aux_catalog installcheck-isolation2
66+
```
67+
68+
## Credits
69+
70+
Based on [pg-sharding/cpg](https://github.com/pg-sharding/cpg) commit
71+
`7b8c912`. Some tests are adapted from open-gpdb/gpdb commit `3ac99962ad2`.
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
-- Tests for the pg_aux_catalog extension: creation of the fixed-OID
2+
-- mdb_admin role and the resource-group permission gate it enables.
3+
CREATE EXTENSION pg_aux_catalog;
4+
-- ---------------------------------------------------------------------
5+
-- pg_create_mdb_admin_role() creates the mdb_admin role with its fixed OID.
6+
-- ---------------------------------------------------------------------
7+
SELECT pg_create_mdb_admin_role() AS mdb_admin_oid;
8+
mdb_admin_oid
9+
---------------
10+
8067
11+
(1 row)
12+
13+
-- The role exists with the fixed OID and is a non-login, non-superuser,
14+
-- connection-limited role.
15+
SELECT oid = 8067 AS has_fixed_oid, rolcanlogin, rolsuper,
16+
rolcreaterole, rolcreatedb, rolconnlimit
17+
FROM pg_authid WHERE rolname = 'mdb_admin';
18+
has_fixed_oid | rolcanlogin | rolsuper | rolcreaterole | rolcreatedb | rolconnlimit
19+
---------------+-------------+----------+---------------+-------------+--------------
20+
t | f | f | f | f | 0
21+
(1 row)
22+
23+
-- Creating it a second time is rejected.
24+
SELECT pg_create_mdb_admin_role();
25+
ERROR: role with OID 8067 already exists
26+
-- ---------------------------------------------------------------------
27+
-- Resource-group permission gate: a role that is not a member of mdb_admin
28+
-- is rejected on every entry point. These checks run before the "resource
29+
-- group is enabled" check, so they are deterministic regardless of the
30+
-- resource manager in use.
31+
-- ---------------------------------------------------------------------
32+
CREATE ROLE regress_rg_noadmin;
33+
SET ROLE regress_rg_noadmin;
34+
CREATE RESOURCE GROUP regress_rg_x WITH (concurrency=1, cpu_max_percent=5);
35+
ERROR: must be mdb_admin to create resource groups
36+
ALTER RESOURCE GROUP regress_rg_x SET cpu_max_percent 6;
37+
ERROR: must be mdb_admin to alter resource groups
38+
DROP RESOURCE GROUP regress_rg_x;
39+
ERROR: must be mdb_admin to drop resource groups
40+
RESET ROLE;
41+
DROP ROLE regress_rg_noadmin;
42+
-- ---------------------------------------------------------------------
43+
-- Cleanup.
44+
-- ---------------------------------------------------------------------
45+
DROP ROLE mdb_admin;
46+
DROP EXTENSION pg_aux_catalog;
Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
-- Tests permission checks for the mdb_admin role with
2+
-- resource groups enabled.
3+
4+
-- start_matchsubs
5+
-- m/ERROR: cannot find process: \d+/
6+
-- s/\d+/XXX/g
7+
-- end_matchsubs
8+
9+
DROP ROLE IF EXISTS role_rg_admin;
10+
DROP
11+
DROP ROLE IF EXISTS role_rg_noadmin;
12+
DROP
13+
DROP ROLE IF EXISTS mdb_admin;
14+
DROP
15+
-- start_ignore
16+
DROP RESOURCE GROUP rg_perm_admin1;
17+
DROP RESOURCE GROUP rg_perm_admin2;
18+
DROP RESOURCE GROUP rg_perm_revoke1;
19+
DROP RESOURCE GROUP rg_perm_revoke2;
20+
DROP RESOURCE GROUP rg_perm_test;
21+
-- end_ignore
22+
23+
-- ---------------------------------------------------------------------
24+
-- Setup. The mdb_admin role is not predefined in the catalog; it is
25+
-- created here the same way the control plane provisions it at runtime.
26+
-- ---------------------------------------------------------------------
27+
CREATE RESOURCE GROUP rg_perm_test WITH (concurrency=2, cpu_max_percent=10);
28+
CREATE
29+
CREATE ROLE mdb_admin;
30+
CREATE
31+
CREATE ROLE role_rg_admin RESOURCE GROUP rg_perm_test;
32+
CREATE
33+
CREATE ROLE role_rg_noadmin RESOURCE GROUP rg_perm_test;
34+
CREATE
35+
GRANT mdb_admin TO role_rg_admin;
36+
GRANT
37+
38+
-- ---------------------------------------------------------------------
39+
-- 1. Member of mdb_admin can CREATE/ALTER/DROP resource groups
40+
-- (statements are dispatched to segments).
41+
-- ---------------------------------------------------------------------
42+
1: SET ROLE role_rg_admin;
43+
SET
44+
1: CREATE RESOURCE GROUP rg_perm_admin1 WITH (concurrency=1, cpu_max_percent=5);
45+
CREATE
46+
1: ALTER RESOURCE GROUP rg_perm_admin1 SET cpu_max_percent 6;
47+
ALTER
48+
1: DROP RESOURCE GROUP rg_perm_admin1;
49+
DROP
50+
51+
-- 2. Even a member cannot ALTER or DROP the system admin_group.
52+
1: ALTER RESOURCE GROUP admin_group SET cpu_max_percent 99;
53+
ERROR: must be superuser to alter resource group "admin_group"
54+
1: DROP RESOURCE GROUP admin_group;
55+
ERROR: must be superuser to drop resource group "admin_group"
56+
1q: ... <quitting>
57+
58+
-- ---------------------------------------------------------------------
59+
-- 3. A non-member is rejected on every entry point.
60+
-- ---------------------------------------------------------------------
61+
2: SET ROLE role_rg_noadmin;
62+
SET
63+
2: CREATE RESOURCE GROUP rg_perm_admin2 WITH (concurrency=1, cpu_max_percent=5);
64+
ERROR: must be mdb_admin to create resource groups
65+
2: ALTER RESOURCE GROUP rg_perm_test SET cpu_max_percent 7;
66+
ERROR: must be mdb_admin to alter resource groups
67+
2: DROP RESOURCE GROUP rg_perm_test;
68+
ERROR: must be mdb_admin to drop resource groups
69+
2q: ... <quitting>
70+
71+
-- ---------------------------------------------------------------------
72+
-- 4. pg_resgroup_move_query() honours the same permission check.
73+
-- The first call (non-member) must fail with "must be mdb_admin".
74+
-- The second call (member) gets past the permission gate and
75+
-- fails on the pid lookup (masked by start_matchsubs above).
76+
-- ---------------------------------------------------------------------
77+
3: SET ROLE role_rg_noadmin;
78+
SET
79+
3: SELECT pg_resgroup_move_query(999999999, 'admin_group');
80+
ERROR: must be mdb_admin to move query
81+
3: RESET ROLE;
82+
RESET
83+
3: SET ROLE role_rg_admin;
84+
SET
85+
3: SELECT pg_resgroup_move_query(999999999, 'admin_group');
86+
ERROR: cannot find process: XXX
87+
3q: ... <quitting>
88+
89+
-- ---------------------------------------------------------------------
90+
-- 5. Cross-session REVOKE takes effect on the granted session's
91+
-- next statement (the privilege is re-checked per command, not
92+
-- cached at SET ROLE time).
93+
-- ---------------------------------------------------------------------
94+
4: SET ROLE role_rg_admin;
95+
SET
96+
4: CREATE RESOURCE GROUP rg_perm_revoke1 WITH (concurrency=1, cpu_max_percent=5);
97+
CREATE
98+
5: REVOKE mdb_admin FROM role_rg_admin;
99+
REVOKE
100+
4: CREATE RESOURCE GROUP rg_perm_revoke2 WITH (concurrency=1, cpu_max_percent=5);
101+
ERROR: must be mdb_admin to create resource groups
102+
4: DROP RESOURCE GROUP rg_perm_revoke1;
103+
ERROR: must be mdb_admin to drop resource groups
104+
4q: ... <quitting>
105+
5q: ... <quitting>
106+
107+
-- ---------------------------------------------------------------------
108+
-- Cleanup. Roles must be dropped before the resource group they
109+
-- reference, otherwise DROP RESOURCE GROUP fails with
110+
-- "resource group is used by at least one role".
111+
-- ---------------------------------------------------------------------
112+
RESET ROLE;
113+
RESET
114+
DROP ROLE role_rg_admin;
115+
DROP
116+
DROP ROLE role_rg_noadmin;
117+
DROP
118+
DROP ROLE mdb_admin;
119+
DROP
120+
DROP RESOURCE GROUP rg_perm_revoke1;
121+
DROP
122+
DROP RESOURCE GROUP rg_perm_test;
123+
DROP
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
test: resgroup_mdb_admin

0 commit comments

Comments
 (0)