Merge pull request #2473 from citusdata/node_udf_permissions

Use function permissions to guard node metadata functions
pull/2468/head
Marco Slot 2018-11-15 12:23:53 +01:00 committed by GitHub
commit 586f398b47
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 165 additions and 46 deletions

View File

@ -17,7 +17,7 @@ EXTVERSIONS = 5.0 5.0-1 5.0-2 \
7.3-1 7.3-2 7.3-3 \
7.4-1 7.4-2 7.4-3 \
7.5-1 7.5-2 7.5-3 7.5-4 7.5-5 7.5-6 7.5-7 \
8.0-1 8.0-2 8.0-3 8.0-4 8.0-5 8.0-6 8.0-7 8.0-8
8.0-1 8.0-2 8.0-3 8.0-4 8.0-5 8.0-6 8.0-7 8.0-8 8.0-9
# All citus--*.sql files in the source directory
DATA = $(patsubst $(citus_abs_srcdir)/%.sql,%.sql,$(wildcard $(citus_abs_srcdir)/$(EXTENSION)--*--*.sql))
@ -231,6 +231,8 @@ $(EXTENSION)--8.0-7.sql: $(EXTENSION)--8.0-6.sql $(EXTENSION)--8.0-6--8.0-7.sql
cat $^ > $@
$(EXTENSION)--8.0-8.sql: $(EXTENSION)--8.0-7.sql $(EXTENSION)--8.0-7--8.0-8.sql
cat $^ > $@
$(EXTENSION)--8.0-9.sql: $(EXTENSION)--8.0-8.sql $(EXTENSION)--8.0-8--8.0-9.sql
cat $^ > $@
NO_PGXS = 1

View File

@ -0,0 +1,13 @@
/* citus--8.0-8--8.0-9 */
SET search_path = 'pg_catalog';
REVOKE ALL ON FUNCTION master_activate_node(text,int) FROM PUBLIC;
REVOKE ALL ON FUNCTION master_add_inactive_node(text,int,int,noderole,name) FROM PUBLIC;
REVOKE ALL ON FUNCTION master_add_node(text,int,int,noderole,name) FROM PUBLIC;
REVOKE ALL ON FUNCTION master_add_secondary_node(text,int,text,int,name) FROM PUBLIC;
REVOKE ALL ON FUNCTION master_disable_node(text,int) FROM PUBLIC;
REVOKE ALL ON FUNCTION master_initialize_node_metadata() FROM PUBLIC;
REVOKE ALL ON FUNCTION master_remove_node(text,int) FROM PUBLIC;
REVOKE ALL ON FUNCTION master_update_node(int,text,int) FROM PUBLIC;
RESET search_path;

View File

@ -1,6 +1,6 @@
# Citus extension
comment = 'Citus distributed database'
default_version = '8.0-8'
default_version = '8.0-9'
module_pathname = '$libdir/citus'
relocatable = false
schema = pg_catalog

View File

@ -257,7 +257,6 @@ master_disable_node(PG_FUNCTION_ARGS)
CheckCitusVersion(ERROR);
EnsureCoordinator();
EnsureSuperUser();
/* take an exclusive lock on pg_dist_node to serialize pg_dist_node changes */
LockRelationOid(DistNodeRelationId(), ExclusiveLock);
@ -305,7 +304,6 @@ master_activate_node(PG_FUNCTION_ARGS)
CheckCitusVersion(ERROR);
EnsureCoordinator();
EnsureSuperUser();
nodeRecord = ActivateNode(nodeNameString, nodePort);
@ -838,7 +836,6 @@ RemoveNodeFromCluster(char *nodeName, int32 nodePort)
uint32 deletedNodeId = INVALID_PLACEMENT_ID;
EnsureCoordinator();
EnsureSuperUser();
/* take an exclusive lock on pg_dist_node to serialize pg_dist_node changes */
LockRelationOid(DistNodeRelationId(), ExclusiveLock);
@ -943,7 +940,6 @@ AddNodeMetadata(char *nodeName, int32 nodePort, int32 groupId, char *nodeRack,
uint32 primariesWithMetadata = 0;
EnsureCoordinator();
EnsureSuperUser();
*nodeAlreadyExists = false;

View File

@ -135,14 +135,92 @@ SELECT master_get_active_worker_nodes();
-- try to disable a node which does not exist and see that an error is thrown
SELECT master_disable_node('localhost.noexist', 2345);
ERROR: node at "localhost.noexist:2345" does not exist
-- try to disable a node via non-super user
CREATE USER non_super_user;
NOTICE: not propagating CREATE ROLE/USER commands to worker nodes
HINT: Connect to worker nodes directly to manually create all necessary users and roles.
\c - non_super_user - :master_port
SELECT master_disable_node('localhost', :worker_1_port);
ERROR: operation is not allowed
HINT: Run the command with a superuser.
CREATE USER node_metadata_user;
NOTICE: not propagating CREATE ROLE/USER commands to worker nodes
HINT: Connect to worker nodes directly to manually create all necessary users and roles.
GRANT EXECUTE ON FUNCTION master_activate_node(text,int) TO node_metadata_user;
GRANT EXECUTE ON FUNCTION master_add_inactive_node(text,int,int,noderole,name) TO node_metadata_user;
GRANT EXECUTE ON FUNCTION master_add_node(text,int,int,noderole,name) TO node_metadata_user;
GRANT EXECUTE ON FUNCTION master_add_secondary_node(text,int,text,int,name) TO node_metadata_user;
GRANT EXECUTE ON FUNCTION master_disable_node(text,int) TO node_metadata_user;
GRANT EXECUTE ON FUNCTION master_remove_node(text,int) TO node_metadata_user;
GRANT EXECUTE ON FUNCTION master_update_node(int,text,int) TO node_metadata_user;
-- try to manipulate node metadata via non-super user
SET ROLE non_super_user;
SELECT 1 FROM master_initialize_node_metadata();
ERROR: permission denied for function master_initialize_node_metadata
SELECT 1 FROM master_add_inactive_node('localhost', :worker_2_port + 1);
ERROR: permission denied for function master_add_inactive_node
SELECT 1 FROM master_activate_node('localhost', :worker_2_port + 1);
ERROR: permission denied for function master_activate_node
SELECT 1 FROM master_disable_node('localhost', :worker_2_port + 1);
ERROR: permission denied for function master_disable_node
SELECT 1 FROM master_remove_node('localhost', :worker_2_port + 1);
ERROR: permission denied for function master_remove_node
SELECT 1 FROM master_add_node('localhost', :worker_2_port + 1);
ERROR: permission denied for function master_add_node
SELECT 1 FROM master_add_secondary_node('localhost', :worker_2_port + 2, 'localhost', :worker_2_port);
ERROR: permission denied for function master_add_secondary_node
SELECT master_update_node(nodeid, 'localhost', :worker_2_port + 3) FROM pg_dist_node WHERE nodeport = :worker_2_port;
ERROR: permission denied for function master_update_node
-- try to manipulate node metadata via privileged user
SET ROLE node_metadata_user;
BEGIN;
SELECT 1 FROM master_add_inactive_node('localhost', :worker_2_port + 1);
?column?
----------
1
(1 row)
SELECT 1 FROM master_activate_node('localhost', :worker_2_port + 1);
?column?
----------
1
(1 row)
SELECT 1 FROM master_disable_node('localhost', :worker_2_port + 1);
?column?
----------
1
(1 row)
SELECT 1 FROM master_remove_node('localhost', :worker_2_port + 1);
?column?
----------
1
(1 row)
SELECT 1 FROM master_add_node('localhost', :worker_2_port + 1);
?column?
----------
1
(1 row)
SELECT 1 FROM master_add_secondary_node('localhost', :worker_2_port + 2, 'localhost', :worker_2_port);
?column?
----------
1
(1 row)
SELECT master_update_node(nodeid, 'localhost', :worker_2_port + 3) FROM pg_dist_node WHERE nodeport = :worker_2_port;
master_update_node
--------------------
(1 row)
SELECT nodename, nodeport, noderole FROM pg_dist_node ORDER BY nodeport;
nodename | nodeport | noderole
-----------+----------+-----------
localhost | 57637 | primary
localhost | 57639 | primary
localhost | 57640 | secondary
localhost | 57641 | primary
(4 rows)
ABORT;
\c - postgres - :master_port
SELECT master_get_active_worker_nodes();
master_get_active_worker_nodes
@ -291,16 +369,16 @@ SELECT count(1) FROM pg_dist_node;
SELECT
master_add_node('localhost', :worker_1_port),
master_add_node('localhost', :worker_2_port);
master_add_node | master_add_node
---------------------------------------------------+---------------------------------------------------
(8,7,localhost,57637,default,f,t,primary,default) | (9,8,localhost,57638,default,f,t,primary,default)
master_add_node | master_add_node
----------------------------------------------------+-----------------------------------------------------
(11,9,localhost,57637,default,f,t,primary,default) | (12,10,localhost,57638,default,f,t,primary,default)
(1 row)
SELECT * FROM pg_dist_node ORDER BY nodeid;
nodeid | groupid | nodename | nodeport | noderack | hasmetadata | isactive | noderole | nodecluster
--------+---------+-----------+----------+----------+-------------+----------+----------+-------------
8 | 7 | localhost | 57637 | default | f | t | primary | default
9 | 8 | localhost | 57638 | default | f | t | primary | default
11 | 9 | localhost | 57637 | default | f | t | primary | default
12 | 10 | localhost | 57638 | default | f | t | primary | default
(2 rows)
-- check that mixed add/remove node commands work fine inside transaction
@ -454,7 +532,7 @@ ERROR: primaries must be added to the default cluster
-- check that you can't add more than one primary to a group
SELECT groupid AS worker_1_group FROM pg_dist_node WHERE nodeport = :worker_1_port \gset
SELECT master_add_node('localhost', 9999, groupid => :worker_1_group, noderole => 'primary');
ERROR: group 12 already has a primary node
ERROR: group 14 already has a primary node
-- check that you can add secondaries and unavailable nodes to a group
SELECT groupid AS worker_2_group FROM pg_dist_node WHERE nodeport = :worker_2_port \gset
SELECT 1 FROM master_add_node('localhost', 9998, groupid => :worker_1_group, noderole => 'secondary');
@ -480,13 +558,13 @@ SELECT 1 FROM master_add_inactive_node('localhost', 9996, groupid => :worker_2_g
SELECT master_add_inactive_node('localhost', 9999, groupid => :worker_2_group, nodecluster => 'olap', noderole => 'secondary');
master_add_inactive_node
---------------------------------------------------
(19,14,localhost,9999,default,f,f,secondary,olap)
(22,16,localhost,9999,default,f,f,secondary,olap)
(1 row)
SELECT master_activate_node('localhost', 9999);
master_activate_node
---------------------------------------------------
(19,14,localhost,9999,default,f,t,secondary,olap)
(22,16,localhost,9999,default,f,t,secondary,olap)
(1 row)
SELECT master_disable_node('localhost', 9999);
@ -514,17 +592,17 @@ CONTEXT: PL/pgSQL function citus.pg_dist_node_trigger_func() line 18 at RAISE
INSERT INTO pg_dist_node (nodename, nodeport, groupid, noderole, nodecluster)
VALUES ('localhost', 5000, 1000, 'primary', 'olap');
ERROR: new row for relation "pg_dist_node" violates check constraint "primaries_are_only_allowed_in_the_default_cluster"
DETAIL: Failing row contains (17, 1000, localhost, 5000, default, f, t, primary, olap).
DETAIL: Failing row contains (19, 1000, localhost, 5000, default, f, t, primary, olap).
UPDATE pg_dist_node SET nodecluster = 'olap'
WHERE nodeport = :worker_1_port;
ERROR: new row for relation "pg_dist_node" violates check constraint "primaries_are_only_allowed_in_the_default_cluster"
DETAIL: Failing row contains (13, 12, localhost, 57637, default, f, t, primary, olap).
DETAIL: Failing row contains (16, 14, localhost, 57637, default, f, t, primary, olap).
-- check that you /can/ add a secondary node to a non-default cluster
SELECT groupid AS worker_2_group FROM pg_dist_node WHERE nodeport = :worker_2_port \gset
SELECT master_add_node('localhost', 8888, groupid => :worker_1_group, noderole => 'secondary', nodecluster=> 'olap');
master_add_node
---------------------------------------------------
(20,12,localhost,8888,default,f,t,secondary,olap)
(23,14,localhost,8888,default,f,t,secondary,olap)
(1 row)
-- check that super-long cluster names are truncated
@ -537,13 +615,13 @@ SELECT master_add_node('localhost', 8887, groupid => :worker_1_group, noderole =
);
master_add_node
--------------------------------------------------------------------------------------------------------------
(21,12,localhost,8887,default,f,t,secondary,thisisasixtyfourcharacterstringrepeatedfourtimestomake256chars.)
(24,14,localhost,8887,default,f,t,secondary,thisisasixtyfourcharacterstringrepeatedfourtimestomake256chars.)
(1 row)
SELECT * FROM pg_dist_node WHERE nodeport=8887;
nodeid | groupid | nodename | nodeport | noderack | hasmetadata | isactive | noderole | nodecluster
--------+---------+-----------+----------+----------+-------------+----------+-----------+-----------------------------------------------------------------
21 | 12 | localhost | 8887 | default | f | t | secondary | thisisasixtyfourcharacterstringrepeatedfourtimestomake256chars.
24 | 14 | localhost | 8887 | default | f | t | secondary | thisisasixtyfourcharacterstringrepeatedfourtimestomake256chars.
(1 row)
-- don't remove the secondary and unavailable nodes, check that no commands are sent to
@ -552,13 +630,13 @@ SELECT * FROM pg_dist_node WHERE nodeport=8887;
SELECT master_add_secondary_node('localhost', 9995, 'localhost', :worker_1_port);
master_add_secondary_node
------------------------------------------------------
(22,12,localhost,9995,default,f,t,secondary,default)
(25,14,localhost,9995,default,f,t,secondary,default)
(1 row)
SELECT master_add_secondary_node('localhost', 9994, primaryname => 'localhost', primaryport => :worker_2_port);
master_add_secondary_node
------------------------------------------------------
(23,14,localhost,9994,default,f,t,secondary,default)
(26,16,localhost,9994,default,f,t,secondary,default)
(1 row)
SELECT master_add_secondary_node('localhost', 9993, 'localhost', 2000);
@ -566,7 +644,7 @@ ERROR: node at "localhost:2000" does not exist
SELECT master_add_secondary_node('localhost', 9992, 'localhost', :worker_1_port, nodecluster => 'second-cluster');
master_add_secondary_node
-------------------------------------------------------------
(24,12,localhost,9992,default,f,t,secondary,second-cluster)
(27,14,localhost,9992,default,f,t,secondary,second-cluster)
(1 row)
SELECT nodeid AS worker_1_node FROM pg_dist_node WHERE nodeport=:worker_1_port \gset
@ -586,7 +664,7 @@ SELECT master_update_node(:worker_1_node, 'somehost', 9000);
SELECT * FROM pg_dist_node WHERE nodeid = :worker_1_node;
nodeid | groupid | nodename | nodeport | noderack | hasmetadata | isactive | noderole | nodecluster
--------+---------+----------+----------+----------+-------------+----------+----------+-------------
13 | 12 | somehost | 9000 | default | f | t | primary | default
16 | 14 | somehost | 9000 | default | f | t | primary | default
(1 row)
-- cleanup
@ -599,6 +677,6 @@ SELECT master_update_node(:worker_1_node, 'localhost', :worker_1_port);
SELECT * FROM pg_dist_node WHERE nodeid = :worker_1_node;
nodeid | groupid | nodename | nodeport | noderack | hasmetadata | isactive | noderole | nodecluster
--------+---------+-----------+----------+----------+-------------+----------+----------+-------------
13 | 12 | localhost | 57637 | default | f | t | primary | default
16 | 14 | localhost | 57637 | default | f | t | primary | default
(1 row)

View File

@ -467,7 +467,7 @@ INSERT INTO app_analytics_events_mx (app_id, name) VALUES (103, 'Mynt') RETURNIN
SELECT setval('app_analytics_events_mx_id_seq'::regclass, :last_value);
setval
------------------
3940649673949185
4503599627370497
(1 row)
ALTER SEQUENCE app_analytics_events_mx_id_seq

View File

@ -38,26 +38,26 @@ SELECT count(*) FROM pg_dist_transaction;
SELECT * FROM pg_dist_local_group;
groupid
---------
12
14
(1 row)
BEGIN;
CREATE TABLE table_should_abort (value int);
PREPARE TRANSACTION 'citus_12_should_abort';
PREPARE TRANSACTION 'citus_14_should_abort';
BEGIN;
CREATE TABLE table_should_commit (value int);
PREPARE TRANSACTION 'citus_12_should_commit';
PREPARE TRANSACTION 'citus_14_should_commit';
BEGIN;
CREATE TABLE should_be_sorted_into_middle (value int);
PREPARE TRANSACTION 'citus_12_should_be_sorted_into_middle';
-- this node (e.g., node id 12) should not touch
PREPARE TRANSACTION 'citus_14_should_be_sorted_into_middle';
-- this node (e.g., node id 14) should not touch
-- transactions with different nodeIds in the gid
BEGIN;
CREATE TABLE table_should_do_nothing (value int);
PREPARE TRANSACTION 'citus_122_should_do_nothing';
-- Add "fake" pg_dist_transaction records and run recovery
INSERT INTO pg_dist_transaction VALUES (12, 'citus_12_should_commit');
INSERT INTO pg_dist_transaction VALUES (12, 'citus_12_should_be_forgotten');
INSERT INTO pg_dist_transaction VALUES (14, 'citus_14_should_commit');
INSERT INTO pg_dist_transaction VALUES (14, 'citus_14_should_be_forgotten');
INSERT INTO pg_dist_transaction VALUES (122, 'citus_122_should_do_nothing');
SELECT recover_prepared_transactions();
recover_prepared_transactions

View File

@ -57,10 +57,40 @@ SELECT master_get_active_worker_nodes();
-- try to disable a node which does not exist and see that an error is thrown
SELECT master_disable_node('localhost.noexist', 2345);
-- try to disable a node via non-super user
CREATE USER non_super_user;
\c - non_super_user - :master_port
SELECT master_disable_node('localhost', :worker_1_port);
CREATE USER node_metadata_user;
GRANT EXECUTE ON FUNCTION master_activate_node(text,int) TO node_metadata_user;
GRANT EXECUTE ON FUNCTION master_add_inactive_node(text,int,int,noderole,name) TO node_metadata_user;
GRANT EXECUTE ON FUNCTION master_add_node(text,int,int,noderole,name) TO node_metadata_user;
GRANT EXECUTE ON FUNCTION master_add_secondary_node(text,int,text,int,name) TO node_metadata_user;
GRANT EXECUTE ON FUNCTION master_disable_node(text,int) TO node_metadata_user;
GRANT EXECUTE ON FUNCTION master_remove_node(text,int) TO node_metadata_user;
GRANT EXECUTE ON FUNCTION master_update_node(int,text,int) TO node_metadata_user;
-- try to manipulate node metadata via non-super user
SET ROLE non_super_user;
SELECT 1 FROM master_initialize_node_metadata();
SELECT 1 FROM master_add_inactive_node('localhost', :worker_2_port + 1);
SELECT 1 FROM master_activate_node('localhost', :worker_2_port + 1);
SELECT 1 FROM master_disable_node('localhost', :worker_2_port + 1);
SELECT 1 FROM master_remove_node('localhost', :worker_2_port + 1);
SELECT 1 FROM master_add_node('localhost', :worker_2_port + 1);
SELECT 1 FROM master_add_secondary_node('localhost', :worker_2_port + 2, 'localhost', :worker_2_port);
SELECT master_update_node(nodeid, 'localhost', :worker_2_port + 3) FROM pg_dist_node WHERE nodeport = :worker_2_port;
-- try to manipulate node metadata via privileged user
SET ROLE node_metadata_user;
BEGIN;
SELECT 1 FROM master_add_inactive_node('localhost', :worker_2_port + 1);
SELECT 1 FROM master_activate_node('localhost', :worker_2_port + 1);
SELECT 1 FROM master_disable_node('localhost', :worker_2_port + 1);
SELECT 1 FROM master_remove_node('localhost', :worker_2_port + 1);
SELECT 1 FROM master_add_node('localhost', :worker_2_port + 1);
SELECT 1 FROM master_add_secondary_node('localhost', :worker_2_port + 2, 'localhost', :worker_2_port);
SELECT master_update_node(nodeid, 'localhost', :worker_2_port + 3) FROM pg_dist_node WHERE nodeport = :worker_2_port;
SELECT nodename, nodeport, noderole FROM pg_dist_node ORDER BY nodeport;
ABORT;
\c - postgres - :master_port
SELECT master_get_active_worker_nodes();

View File

@ -26,25 +26,25 @@ SELECT * FROM pg_dist_local_group;
BEGIN;
CREATE TABLE table_should_abort (value int);
PREPARE TRANSACTION 'citus_12_should_abort';
PREPARE TRANSACTION 'citus_14_should_abort';
BEGIN;
CREATE TABLE table_should_commit (value int);
PREPARE TRANSACTION 'citus_12_should_commit';
PREPARE TRANSACTION 'citus_14_should_commit';
BEGIN;
CREATE TABLE should_be_sorted_into_middle (value int);
PREPARE TRANSACTION 'citus_12_should_be_sorted_into_middle';
PREPARE TRANSACTION 'citus_14_should_be_sorted_into_middle';
-- this node (e.g., node id 12) should not touch
-- this node (e.g., node id 14) should not touch
-- transactions with different nodeIds in the gid
BEGIN;
CREATE TABLE table_should_do_nothing (value int);
PREPARE TRANSACTION 'citus_122_should_do_nothing';
-- Add "fake" pg_dist_transaction records and run recovery
INSERT INTO pg_dist_transaction VALUES (12, 'citus_12_should_commit');
INSERT INTO pg_dist_transaction VALUES (12, 'citus_12_should_be_forgotten');
INSERT INTO pg_dist_transaction VALUES (14, 'citus_14_should_commit');
INSERT INTO pg_dist_transaction VALUES (14, 'citus_14_should_be_forgotten');
INSERT INTO pg_dist_transaction VALUES (122, 'citus_122_should_do_nothing');
SELECT recover_prepared_transactions();