Security Features Implemented in pgrust: PostgreSQL's Security Model in Rust
The pgrust crate implements PostgreSQL's complete security stack—including security-restricted operations, SECURITY DEFINER functions, row-level security policies, and GUC enforcement—mirroring the original C implementation's permission model while running inside the Rust-based backend.
pgrust is a Rust-based reimplementation of the PostgreSQL server that executes within the backend and must enforce the same stringent security constraints as the original C code. The security features implemented in pgrust replicate critical subsystems including privileged operation blocking, definer rights management, and row-level access controls. This analysis examines the specific source code files and functions that constitute the security architecture.
Security-Restricted Operations
The security-restricted operation flag is a per-session boolean that blocks privileged actions while the backend is in a restricted state.
In crates/backend/utils/init/miscinit_seams/src/lib.rs, the in_security_restricted_operation seam provides read/write access to this flag. This mechanism prevents commands like SET ROLE or superuser-only GUC changes during sensitive operations such as REINDEX, CLUSTER, or execution within SECURITY DEFINER functions.
use crate::miscinit_seams::in_security_restricted_operation;
/// Returns true if the backend is currently inside a security-restricted
/// operation (e.g., REINDEX, CLUSTER, or a SECURITY DEFINER function).
fn is_restricted() -> bool {
in_security_restricted_operation::call()
}
The flag is checked before allowing any privileged state transition, ensuring that operations occurring inside restricted blocks cannot escalate privileges.
Security Definer Functions
SECURITY DEFINER functions execute with the privileges of their owner rather than the caller, requiring careful privilege switching logic.
The implementation resides in crates/backend/utils/fmgr/fmgr_core/src/lib.rs. The fmgr_security_definer seam determines whether to install a security definer call handler based on the security_definer flag stored in the procedure cache.
if proc.security_definer || fmgr_hook_is_needed(function_id) {
// install fmgr_security_definer as the call handler
}
When declared with #[pg_extern(security_definer = true)], functions run under the owner's security context:
#[pg_extern(security_definer = true)]
fn privileged_action() -> Result<(), PgBox<pg_error::Error>> {
// This code runs with the privileges of the function's owner.
// e.g., it may CREATE EXTENSION or SET superuser-only GUCs.
Ok(())
}
The security_definer predicate is cached in the system cache at crates/backend/utils/cache/syscache_seams/src/lib.rs, allowing the executor to quickly determine if definer rights are required.
Row-Level Security (RLS)
Row-level security policies filter rows based on the current user, enforced per-table through policy expressions.
Core RLS logic lives in crates/backend/utils/misc/more/src/rls.rs. The check_enable_rls function determines whether RLS applies to a given table, while row_security_active and row_security_active_name provide SQL-callable interfaces to check policy status.
pub fn row_security_active(mcx: Mcx<'_>, tableoid: Oid) -> PgResult<bool> {
// Core RLS decision logic lives in backend/utils/misc/more/src/rls.rs
}
The row_security GUC boolean, defined in crates/backend/utils/misc/guc_tables/src/vars.rs, globally enables or disables RLS evaluation at backend startup. When active, the executor consults these functions to decide whether to apply row filters before returning data.
GUC Enforcement and Configuration Security
The Grand Unified Configuration (GUC) system prevents changing superuser-only parameters during restricted operations.
In crates/backend/utils/misc/misc_guc/src/lib.rs, the seam checks in_security_restricted_operation before allowing SET commands on privileged parameters:
use crate::misc_guc::set_config_parameter;
/// Attempt to set a super-user-only GUC while a restricted operation is active.
fn try_set_guc() -> Result<(), PgError> {
if in_security_restricted_operation::call() {
// This will raise: "cannot set parameter \"log_statement_stats\" within security-restricted operation"
set_config_parameter("log_statement_stats", "on")
} else {
Ok(())
}
}
This ensures that configuration changes that could compromise security or audit trails cannot occur inside security-definer contexts or other restricted blocks.
Role Switching and Validation
When switching session users or security contexts via SET ROLE, pgrust validates the transition against the current security-restricted state.
The validation logic in crates/backend/utils/init/miscinit/src/lib.rs ensures that role changes cannot occur inside a restricted block. If in_security_restricted_operation returns true, the function raises an error before completing the transition, preventing privilege escalation during sensitive execution paths.
Backend State Security Hooks
Security state must propagate to parallel workers and statistics collection subsystems. The pgstat_bestart_security seam in crates/backend/utils/init/postinit/src/lib.rs initializes the security context for background workers, ensuring that restricted flags carry over to spawned processes.
Procedural languages like PL/pgSQL reuse these security mechanisms through the handler modules in crates/pl/plpgsql/src/handler/src/lib.rs, ensuring that functions written in higher-level languages respect the same definer-rights and restricted-operation checks as compiled code.
Summary
- Security-restricted operations are guarded by the
in_security_restricted_operationflag inmiscinit_seams, blocking privileged commands during sensitive execution. - SECURITY DEFINER functions are handled by
fmgr_security_definerin the Fmgr core, which switches execution context based on cached predicates from the syscache. - Row-level security policies are enforced through
check_enable_rlsand therow_security_activefunctions, controlled by therow_securityGUC. - GUC enforcement prevents superuser-only configuration changes during restricted operations via checks in
misc_guc. - Role switching is validated against the restricted flag in
miscinit, preventing privilege escalation during restricted blocks. - Backend hooks propagate security state to parallel workers through
pgstat_bestart_securityin the post-init module.
Frequently Asked Questions
How does pgrust implement the security-restricted operation flag?
The flag is implemented as a seam in crates/backend/utils/init/miscinit_seams/src/lib.rs that provides in_security_restricted_operation::call(). This function returns a boolean indicating whether the current session is inside a restricted block, which is checked before allowing privileged operations like GUC changes or role switches.
What is the difference between SECURITY DEFINER handling in pgrust versus standard PostgreSQL?
pgrust implements the same semantic model as standard PostgreSQL but uses Rust-based seams. The fmgr_security_definer logic in crates/backend/utils/fmgr/fmgr_core/src/lib.rs checks the security_definer boolean stored in the system cache (via syscache_seams) and installs the appropriate call handler to switch to the function owner's privileges during execution.
Does pgrust support row-level security policies?
Yes. pgrust implements full RLS support through crates/backend/utils/misc/more/src/rls.rs, which contains check_enable_rls for policy decisions and row_security_active for SQL-level checks. The feature is controlled by the row_security GUC defined in guc_tables/src/vars.rs, matching PostgreSQL's behavior exactly.
How does pgrust prevent unauthorized GUC changes during restricted operations?
Before processing a SET command for superuser-only parameters, the misc_guc seam in crates/backend/utils/misc/misc_guc/src/lib.rs calls in_security_restricted_operation::call(). If the flag is true, it raises an error preventing the change, ensuring that security-definer functions and other restricted contexts cannot modify critical configuration parameters.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →