Skip to content

Commit

Permalink
Propagates SECURITY LABEL ON ROLE stmt (#7304) (#7735)
Browse files Browse the repository at this point in the history
Propagates SECURITY LABEL ON ROLE stmt (#7304)
We propagate `SECURITY LABEL [for provider] ON ROLE rolename IS
labelname` to the worker nodes.
We also make sure to run the relevant `SecLabelStmt` commands on a
newly added node by looking at roles found in `pg_shseclabel`.

See official docs for explanation on how this command works:
https://www.postgresql.org/docs/current/sql-security-label.html
This command stores the role label in the `pg_shseclabel` catalog table.

This commit also fixes the regex string in
`check_gucs_are_alphabetically_sorted.sh` script such that it escapes
the dot. Previously it was looking for all strings starting with "citus"
instead of "citus." as it should.

To test this feature, I currently make use of a special GUC to control
label provider registration in PG_init when creating the Citus extension.

(cherry picked from commit 0d1f188)

Co-authored-by: Naisila Puka <[email protected]>
(cherry picked from commit 686d2b4)
  • Loading branch information
emelsimsek authored and naisila committed Jan 13, 2025
1 parent 7e316c9 commit 1468ae3
Show file tree
Hide file tree
Showing 18 changed files with 774 additions and 12 deletions.
2 changes: 1 addition & 1 deletion ci/check_gucs_are_alphabetically_sorted.sh
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,6 @@ set -euo pipefail
source ci/ci_helpers.sh

# extract citus gucs in the form of "citus.X"
grep -o -E "(\.*\"citus.\w+\")," src/backend/distributed/shared_library_init.c > gucs.out
grep -o -E "(\.*\"citus\.\w+\")," src/backend/distributed/shared_library_init.c > gucs.out
sort -c gucs.out
rm gucs.out
133 changes: 133 additions & 0 deletions gucs.out
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
"citus.all_modifications_commutative",
"citus.allow_modifications_from_workers_to_replicated_tables",
"citus.allow_nested_distributed_execution",
"citus.allow_unsafe_constraints",
"citus.allow_unsafe_locks_from_workers",
"citus.background_task_queue_interval",
"citus.check_available_space_before_move",
"citus.cluster_name",
"citus.coordinator_aggregation_strategy",
"citus.copy_switchover_threshold",
"citus.count_distinct_error_rate",
"citus.cpu_priority",
"citus.cpu_priority_for_logical_replication_senders",
"citus.create_object_propagation",
"citus.defer_drop_after_shard_move",
"citus.defer_drop_after_shard_split",
"citus.defer_shard_delete_interval",
"citus.desired_percent_disk_available_after_move",
"citus.distributed_deadlock_detection_factor",
"citus.enable_alter_database_owner",
"citus.enable_alter_role_propagation",
"citus.enable_alter_role_set_propagation",
"citus.enable_binary_protocol",
"citus.enable_change_data_capture",
"citus.enable_cluster_clock",
"citus.enable_cost_based_connection_establishment",
"citus.enable_create_role_propagation",
"citus.enable_create_type_propagation",
"citus.enable_ddl_propagation",
"citus.enable_deadlock_prevention",
"citus.enable_fast_path_router_planner",
"citus.enable_local_execution",
"citus.enable_local_reference_table_foreign_keys",
"citus.enable_manual_changes_to_shards",
"citus.enable_manual_metadata_changes_for_user",
"citus.enable_metadata_sync",
"citus.enable_non_colocated_router_query_pushdown",
"citus.enable_repartition_joins",
"citus.enable_repartitioned_insert_select",
"citus.enable_router_execution",
"citus.enable_schema_based_sharding",
"citus.enable_single_hash_repartition_joins",
"citus.enable_statistics_collection",
"citus.enable_unique_job_ids",
"citus.enable_unsafe_triggers",
"citus.enable_unsupported_feature_messages",
"citus.enable_version_checks",
"citus.enforce_foreign_key_restrictions",
"citus.enforce_object_restrictions_for_local_objects",
"citus.executor_slow_start_interval",
"citus.explain_all_tasks",
"citus.explain_analyze_sort_method",
"citus.explain_distributed_queries",
"citus.force_max_query_parallelization",
"citus.function_opens_transaction_block",
"citus.grep_remote_commands",
"citus.hide_citus_dependent_objects",
"citus.hide_shards_from_app_name_prefixes",
"citus.isolation_test_session_process_id",
"citus.isolation_test_session_remote_process_id",
"citus.limit_clause_row_fetch_count",
"citus.local_copy_flush_threshold",
"citus.local_hostname",
"citus.local_shared_pool_size",
"citus.local_table_join_policy",
"citus.log_distributed_deadlock_detection",
"citus.log_intermediate_results",
"citus.log_local_commands",
"citus.log_multi_join_order",
"citus.log_remote_commands",
"citus.logical_replication_timeout",
"citus.main_db",
"citus.max_adaptive_executor_pool_size",
"citus.max_background_task_executors",
"citus.max_background_task_executors_per_node",
"citus.max_cached_connection_lifetime",
"citus.max_cached_conns_per_worker",
"citus.max_client_connections",
"citus.max_high_priority_background_processes",
"citus.max_intermediate_result_size",
"citus.max_matview_size_to_auto_recreate",
"citus.max_rebalancer_logged_ignored_moves",
"citus.max_shared_pool_size",
"citus.max_worker_nodes_tracked",
"citus.metadata_sync_interval",
"citus.metadata_sync_mode",
"citus.metadata_sync_retry_interval",
"citus.mitmfifo",
"citus.multi_shard_modify_mode",
"citus.multi_task_query_log_level",
"citus.next_cleanup_record_id",
"citus.next_operation_id",
"citus.next_placement_id",
"citus.next_shard_id",
"citus.node_connection_timeout",
"citus.node_conninfo",
"citus.override_table_visibility",
"citus.prevent_incomplete_connection_establishment",
"citus.propagate_session_settings_for_loopback_connection",
"citus.propagate_set_commands",
"citus.rebalancer_by_disk_size_base_cost",
"citus.recover_2pc_interval",
"citus.remote_copy_flush_threshold",
"citus.remote_task_check_interval",
"citus.repartition_join_bucket_count_per_node",
"citus.replicate_reference_tables_on_activate",
"citus.replication_model",
"citus.running_under_citus_test_suite",
"citus.select_opens_transaction_block",
"citus.shard_count",
"citus.shard_replication_factor",
"citus.show_shards_for_app_name_prefixes",
"citus.skip_advisory_lock_permission_checks",
"citus.skip_constraint_validation",
"citus.skip_jsonb_validation_in_copy",
"citus.sort_returning",
"citus.stat_statements_max",
"citus.stat_statements_purge_interval",
"citus.stat_statements_track",
"citus.stat_tenants_limit",
"citus.stat_tenants_log_level",
"citus.stat_tenants_period",
"citus.stat_tenants_track",
"citus.stat_tenants_untracked_sample_rate",
"citus.subquery_pushdown",
"citus.task_assignment_policy",
"citus.task_executor_type",
"citus.use_citus_managed_tables",
"citus.use_secondary_nodes",
"citus.values_materialization_threshold",
"citus.version",
"citus.worker_min_messages",
"citus.writable_standby_coordinator",
14 changes: 14 additions & 0 deletions src/backend/distributed/commands/distribute_object_ops.c
Original file line number Diff line number Diff line change
Expand Up @@ -364,6 +364,15 @@ static DistributeObjectOps Any_Rename = {
.address = NULL,
.markDistributed = false,
};
static DistributeObjectOps Any_SecLabel = {
.deparse = DeparseSecLabelStmt,
.qualify = NULL,
.preprocess = NULL,
.postprocess = PostprocessSecLabelStmt,
.operationType = DIST_OPS_ALTER,
.address = SecLabelStmtObjectAddress,
.markDistributed = false,
};
static DistributeObjectOps Attribute_Rename = {
.deparse = DeparseRenameAttributeStmt,
.qualify = QualifyRenameAttributeStmt,
Expand Down Expand Up @@ -1991,6 +2000,11 @@ GetDistributeObjectOps(Node *node)
return &Vacuum_Analyze;
}

case T_SecLabelStmt:
{
return &Any_SecLabel;
}

case T_RenameStmt:
{
RenameStmt *stmt = castNode(RenameStmt, node);
Expand Down
71 changes: 68 additions & 3 deletions src/backend/distributed/commands/role.c
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
#include "catalog/pg_auth_members.h"
#include "catalog/pg_authid.h"
#include "catalog/pg_db_role_setting.h"
#include "catalog/pg_shseclabel.h"
#include "catalog/pg_type.h"
#include "commands/dbcommands.h"
#include "nodes/makefuncs.h"
Expand Down Expand Up @@ -65,6 +66,7 @@ static DefElem * makeDefElemBool(char *name, bool value);
static List * GenerateRoleOptionsList(HeapTuple tuple);
static List * GenerateGrantRoleStmtsFromOptions(RoleSpec *roleSpec, List *options);
static List * GenerateGrantRoleStmtsOfRole(Oid roleid);
static List * GenerateSecLabelOnRoleStmts(Oid roleid, char *rolename);
static void EnsureSequentialModeForRoleDDL(void);

static char * GetRoleNameFromDbRoleSetting(HeapTuple tuple,
Expand Down Expand Up @@ -516,13 +518,14 @@ GenerateCreateOrAlterRoleCommand(Oid roleOid)
{
HeapTuple roleTuple = SearchSysCache1(AUTHOID, ObjectIdGetDatum(roleOid));
Form_pg_authid role = ((Form_pg_authid) GETSTRUCT(roleTuple));
char *rolename = pstrdup(NameStr(role->rolname));

CreateRoleStmt *createRoleStmt = NULL;
if (EnableCreateRolePropagation)
{
createRoleStmt = makeNode(CreateRoleStmt);
createRoleStmt->stmt_type = ROLESTMT_ROLE;
createRoleStmt->role = pstrdup(NameStr(role->rolname));
createRoleStmt->role = rolename;
createRoleStmt->options = GenerateRoleOptionsList(roleTuple);
}

Expand All @@ -533,7 +536,7 @@ GenerateCreateOrAlterRoleCommand(Oid roleOid)
alterRoleStmt->role = makeNode(RoleSpec);
alterRoleStmt->role->roletype = ROLESPEC_CSTRING;
alterRoleStmt->role->location = -1;
alterRoleStmt->role->rolename = pstrdup(NameStr(role->rolname));
alterRoleStmt->role->rolename = rolename;
alterRoleStmt->action = 1;
alterRoleStmt->options = GenerateRoleOptionsList(roleTuple);
}
Expand All @@ -545,7 +548,7 @@ GenerateCreateOrAlterRoleCommand(Oid roleOid)
{
/* add a worker_create_or_alter_role command if any of them are set */
char *createOrAlterRoleQuery = CreateCreateOrAlterRoleCommand(
pstrdup(NameStr(role->rolname)),
rolename,
createRoleStmt,
alterRoleStmt);

Expand All @@ -567,6 +570,20 @@ GenerateCreateOrAlterRoleCommand(Oid roleOid)
{
completeRoleList = lappend(completeRoleList, DeparseTreeNode(stmt));
}

/*
* append SECURITY LABEL ON ROLE commands for this specific user
* When we propagate user creation, we also want to make sure that we propagate
* all the security labels it has been given. For this, we check pg_shseclabel
* for the ROLE entry corresponding to roleOid, and generate the relevant
* SecLabel stmts to be run in the new node.
*/
List *secLabelOnRoleStmts = GenerateSecLabelOnRoleStmts(roleOid, rolename);
stmt = NULL;
foreach_declared_ptr(stmt, secLabelOnRoleStmts)
{
completeRoleList = lappend(completeRoleList, DeparseTreeNode(stmt));
}
}

return completeRoleList;
Expand Down Expand Up @@ -896,6 +913,54 @@ GenerateGrantRoleStmtsOfRole(Oid roleid)
}


/*
* GenerateSecLabelOnRoleStmts generates the SecLabelStmts for the role
* whose oid is roleid.
*/
static List *
GenerateSecLabelOnRoleStmts(Oid roleid, char *rolename)
{
List *secLabelStmts = NIL;

/*
* Note that roles are shared database objects, therefore their
* security labels are stored in pg_shseclabel instead of pg_seclabel.
*/
Relation pg_shseclabel = table_open(SharedSecLabelRelationId, AccessShareLock);
ScanKeyData skey[1];
ScanKeyInit(&skey[0], Anum_pg_shseclabel_objoid, BTEqualStrategyNumber, F_OIDEQ,
ObjectIdGetDatum(roleid));
SysScanDesc scan = systable_beginscan(pg_shseclabel, SharedSecLabelObjectIndexId,
true, NULL, 1, &skey[0]);

HeapTuple tuple = NULL;
while (HeapTupleIsValid(tuple = systable_getnext(scan)))
{
SecLabelStmt *secLabelStmt = makeNode(SecLabelStmt);
secLabelStmt->objtype = OBJECT_ROLE;
secLabelStmt->object = (Node *) makeString(pstrdup(rolename));

Datum datumArray[Natts_pg_shseclabel];
bool isNullArray[Natts_pg_shseclabel];

heap_deform_tuple(tuple, RelationGetDescr(pg_shseclabel), datumArray,
isNullArray);

secLabelStmt->provider = TextDatumGetCString(
datumArray[Anum_pg_shseclabel_provider - 1]);
secLabelStmt->label = TextDatumGetCString(
datumArray[Anum_pg_shseclabel_label - 1]);

secLabelStmts = lappend(secLabelStmts, secLabelStmt);
}

systable_endscan(scan);
table_close(pg_shseclabel, AccessShareLock);

return secLabelStmts;
}


/*
* PreprocessCreateRoleStmt creates a worker_create_or_alter_role query for the
* role that is being created. With that query we can create the role in the
Expand Down
Loading

0 comments on commit 1468ae3

Please sign in to comment.