Compare commits

...

16 Commits

Author SHA1 Message Date
Suhas Thalanki
36a850306b updated patch 2025-05-30 16:20:38 -04:00
Suhas Thalanki
5b96736d07 added more whitelisted sources 2025-05-29 11:35:26 -04:00
Suhas Thalanki
fa7f5b6def added source = PGC_S_DEFAULT check for guc hook 2025-05-28 15:22:32 -04:00
Suhas Thalanki
e14854d6fb added log statement for source of check hook 2025-05-28 15:22:32 -04:00
Suhas Thalanki
315c80a08d added info level instead of log 2025-05-21 12:35:11 -04:00
Suhas Thalanki
f0ec173a99 modified log level 2025-05-21 11:29:33 -04:00
Suhas Thalanki
a58d6669a4 added log statement 2025-05-20 17:07:26 -04:00
Suhas Thalanki
54fe9ccc08 Merge branch 'main' into thesuhas/patched-pgrx-14 2025-05-20 15:38:37 -04:00
Suhas Thalanki
e47bdac77b changed non superuser_only flags to userset 2025-05-20 14:44:01 -04:00
Suhas Thalanki
1df624c2d3 added check_hook to transparent_dynamic_masking 2025-05-20 13:28:09 -04:00
Suhas Thalanki
c5e6d1790a updated patch with new if condition, logging level 2025-05-20 11:06:55 -04:00
Suhas Thalanki
228d034d0c updated check hook to use libc:c_char 2025-05-19 18:18:01 -04:00
Suhas Thalanki
e5804eb3e6 removing cargo.toml from patch 2025-05-19 17:06:53 -04:00
Suhas Thalanki
6635e1e2bc adding unsafe postgres 2025-05-19 17:00:48 -04:00
Suhas Thalanki
16bbb0b13e removing sed statements as the changes are part of the patch 2025-05-19 16:58:31 -04:00
Suhas Thalanki
2a2417b5e3 patched pgrx14 and updated anon 2025-05-19 16:42:30 -04:00
2 changed files with 272 additions and 5 deletions

View File

@@ -1097,7 +1097,7 @@ USER root
FROM pg-build-nonroot-with-cargo AS rust-extensions-build-pgrx14
ARG PG_VERSION
RUN cargo install --locked --version 0.14.1 cargo-pgrx && \
RUN cargo install --locked --git https://github.com/thesuhas/pgrx.git --branch expose_guc_assign_hook cargo-pgrx && \
/bin/bash -c 'cargo pgrx init --pg${PG_VERSION:1}=/usr/local/pgsql/bin/pg_config'
USER root
@@ -1355,7 +1355,9 @@ RUN wget https://gitlab.com/dalibo/postgresql_anonymizer/-/archive/2.1.0/postgre
echo "48e7f5ae2f1ca516df3da86c5c739d48dd780a4e885705704ccaad0faa89d6c0 pg_anon.tar.gz" | sha256sum --check && \
mkdir pg_anon-src && cd pg_anon-src && tar xzf ../pg_anon.tar.gz --strip-components=1 -C . && \
find /usr/local/pgsql -type f | sed 's|^/usr/local/pgsql/||' > /before.txt && \
sed -i 's/pgrx = "0.14.1"/pgrx = { version = "=0.14.1", features = [ "unsafe-postgres" ] }/g' Cargo.toml && \
sed -i 's/pgrx = "0.14.1"/pgrx = { git = "https:\/\/github.com\/thesuhas\/pgrx.git", branch = "expose_guc_assign_hook", features = [ "unsafe-postgres" ] }/g' Cargo.toml && \
sed -i 's/pgrx-tests = "0.14.1"/pgrx-tests = { git = "https:\/\/github.com\/thesuhas\/pgrx.git", branch = "expose_guc_assign_hook" }/g' Cargo.toml && \
sed -i '/\[dependencies\]/a libc = "0.2.172"' Cargo.toml && \
patch -p1 < /ext-src/anon_v2.patch
FROM rust-extensions-build-pgrx14 AS pg-anon-pg-build

View File

@@ -1,8 +1,8 @@
diff --git a/sql/anon.sql b/sql/anon.sql
index 0cdc769..f6cc950 100644
index 0cdc769..85a58a6 100644
--- a/sql/anon.sql
+++ b/sql/anon.sql
@@ -1141,3 +1141,8 @@ $$
@@ -1141,3 +1141,9 @@ $$
-- TODO : https://en.wikipedia.org/wiki/L-diversity
-- TODO : https://en.wikipedia.org/wiki/T-closeness
@@ -11,8 +11,9 @@ index 0cdc769..f6cc950 100644
+
+GRANT ALL ON SCHEMA anon to neon_superuser;
+GRANT ALL ON ALL TABLES IN SCHEMA anon TO neon_superuser;
+-- GRANT SET ON PARAMETER anon.transparent_dynamic_masking TO neon_superuser;
diff --git a/sql/init.sql b/sql/init.sql
index 7da6553..9b6164b 100644
index 7da6553..7961984 100644
--- a/sql/init.sql
+++ b/sql/init.sql
@@ -74,50 +74,49 @@ $$
@@ -127,3 +128,267 @@ index 7da6553..9b6164b 100644
VOLATILE
PARALLEL UNSAFE -- because init is unsafe
SECURITY INVOKER
@@ -264,3 +263,22 @@ $$
;
SECURITY LABEL FOR anon ON FUNCTION anon.unload IS 'UNTRUSTED';
+
+
+CREATE OR REPLACE FUNCTION anon.toggle_transparent_dynamic_masking(
+ dbname TEXT,
+ toggle BOOLEAN DEFAULT TRUE
+)
+RETURNS VOID AS
+$$
+BEGIN
+ EXECUTE format('ALTER DATABASE %I SET anon.transparent_dynamic_masking TO %s', dbname, toggle::TEXT);
+END;
+$$
+ LANGUAGE plpgsql
+ VOLATILE
+ SECURITY DEFINER
+ SET search_path=''
+;
+
+SECURITY LABEL FOR anon ON FUNCTION anon.toggle_transparent_dynamic_masking IS 'UNTRUSTED';
diff --git a/src/guc.rs b/src/guc.rs
index 74d3822..d4121ae 100644
--- a/src/guc.rs
+++ b/src/guc.rs
@@ -3,7 +3,7 @@
//----------------------------------------------------------------------------
use pgrx::*;
-use std::ffi::CStr;
+use std::ffi::{CStr, c_void};
pub static ANON_DUMMY_LOCALE: GucSetting<Option<&'static CStr>> =
GucSetting::<Option<&'static CStr>>::new(Some(unsafe {
@@ -51,25 +51,97 @@ static ANON_MASK_SCHEMA: GucSetting<Option<&'static CStr>> =
CStr::from_bytes_with_nul_unchecked(b"mask\0")
}));
+
+unsafe extern "C-unwind" fn check_bool_guc_hook(
+ _newval: *mut bool,
+ _extra: *mut *mut c_void,
+ source: u32
+) -> bool {
+ unsafe {
+ // The sources that we allow are:
+ // 1. PGC_S_DEFAULT (0) -> for default boot up source, likely new session or server.
+ // 2. PGC_S_DATABASE (6) -> a GUC set for a particular database
+ // 3. PGC_S_USER (7) -> a GUC set for a particular role
+ // 4. PGC_S_DATABASE_USER (8) -> a GUC set for a particular role in a particular database
+ // This check only allows sources that load a variable, not ones that try to alter it.
+ // Sources that try to alter it are:
+ // 1. PGC_S_FILE (3) -> ALTER SYSTEM
+ // 2. PGC_S_TEST (12) -> ALTER ROLE/DATABASE
+ // 3. PGC_S_SESSION (13) -> SET ...
+ // TODO (thesuhas): Does PGC_S_GLOBAL need to be added to whitelisted sources?
+ pg_sys::info!("Source: {}", source);
+ if source == 0 || source == 6 || source == 7 || source == 8 {
+ return true;
+ }
+ let oid = pg_sys::GetUserId();
+ let user_name = CStr::from_ptr(pg_sys::GetUserNameFromId(oid, true));
+ let user_str = user_name.to_str().unwrap();
+ pg_sys::info!("user: {} trying to change boolean guc", user_str);
+ if pg_sys::superuser() || user_str == "neon_superuser" || user_str == "neondb_owner" {
+ return true;
+ }
+ pg_sys::ereport!(PgLogLevel::ERROR, PgSqlErrorCode::ERRCODE_INSUFFICIENT_PRIVILEGE, "You are not authorized to change this GUC");
+ false
+ }
+}
+
+unsafe extern "C-unwind" fn check_string_guc_hook(
+_newval: *mut *mut libc::c_char,
+_extra: *mut *mut c_void,
+source: u32
+) -> bool {
+ unsafe {
+ // The sources that we allow are:
+ // 1. PGC_S_DEFAULT (0) -> for default boot up source, likely new session or server.
+ // 2. PGC_S_DATABASE (6) -> a GUC set for a particular database
+ // 3. PGC_S_USER (7) -> a GUC set for a particular role
+ // 4. PGC_S_DATABASE_USER (8) -> a GUC set for a particular role in a particular database
+ // This check only allows sources that load a variable, not ones that try to alter it.
+ // Sources that try to alter it are:
+ // 1. PGC_S_FILE (3) -> ALTER SYSTEM
+ // 2. PGC_S_TEST (12) -> ALTER ROLE/DATABASE
+ // 3. PGC_S_SESSION (13) -> SET ...
+ pg_sys::info!("Source: {}", source);
+ if source == 0 || source == 6 || source == 7 || source == 8 {
+ return true;
+ }
+ let oid = pg_sys::GetUserId();
+ let user_name = CStr::from_ptr(pg_sys::GetUserNameFromId(oid, true));
+ let user_str = user_name.to_str().unwrap();
+ pg_sys::info!("user: {} trying to change string guc", user_str);
+ if pg_sys::superuser() || user_str == "neon_superuser" || user_str == "neondb_owner" {
+ return true;
+ }
+ pg_sys::ereport!(PgLogLevel::ERROR, PgSqlErrorCode::ERRCODE_INSUFFICIENT_PRIVILEGE, "You are not authorized to change this GUC");
+ false
+ }
+}
+
// Register the GUC parameters for the extension
//
pub fn register_gucs() {
- GucRegistry::define_string_guc(
+ GucRegistry::define_string_guc_with_hooks(
"anon.dummy_locale",
"The default locale for the dummy data functions",
"",
&ANON_DUMMY_LOCALE,
GucContext::Suset,
GucFlags::SUPERUSER_ONLY,
+ Some(check_string_guc_hook),
+ None,
+ None,
);
- GucRegistry::define_string_guc(
+ GucRegistry::define_string_guc_with_hooks(
"anon.k_anonymity_provider",
"The security label provider used for k-anonymity",
"",
&ANON_K_ANONYMITY_PROVIDER,
GucContext::Suset,
GucFlags::SUPERUSER_ONLY,
+ Some(check_string_guc_hook),
+ None,
+ None,
);
//
@@ -80,86 +152,113 @@ pub fn register_gucs() {
//
// https://github.com/pgcentralfoundation/pgrx/commit/d096efe6fb2d86e87d117b520b9ccd2f90b2e0d1
//
- GucRegistry::define_string_guc(
+ GucRegistry::define_string_guc_with_hooks(
"anon.masking_policies",
"Define additional masking policies (the 'anon' policy is already defined)",
"",
&ANON_MASKING_POLICIES,
GucContext::Suset,
GucFlags::SUPERUSER_ONLY, /* | GucFlags::LIST_INPUT */
+ Some(check_string_guc_hook),
+ None,
+ None,
);
- GucRegistry::define_bool_guc(
+ GucRegistry::define_bool_guc_with_hooks(
"anon.privacy_by_default",
"Mask all columns with NULL (or the default value for NOT NULL columns)",
"",
&ANON_PRIVACY_BY_DEFAULT,
- GucContext::Suset,
+ GucContext::Userset,
GucFlags::default(),
+ Some(check_bool_guc_hook),
+ None,
+ None,
);
- GucRegistry::define_bool_guc(
+ GucRegistry::define_bool_guc_with_hooks(
"anon.transparent_dynamic_masking",
"New masking engine (EXPERIMENTAL)",
"",
&ANON_TRANSPARENT_DYNAMIC_MASKING,
- GucContext::Suset,
+ GucContext::Userset,
GucFlags::default(),
+ Some(check_bool_guc_hook),
+ None,
+ None,
);
- GucRegistry::define_bool_guc(
+ GucRegistry::define_bool_guc_with_hooks(
"anon.restrict_to_trusted_schemas",
"Masking filters must be in a trusted schema",
"Activate this option to prevent non-superuser from using their own masking filters",
&ANON_RESTRICT_TO_TRUSTED_SCHEMAS,
GucContext::Suset,
GucFlags::SUPERUSER_ONLY,
+ Some(check_bool_guc_hook),
+ None,
+ None,
);
- GucRegistry::define_bool_guc(
+ GucRegistry::define_bool_guc_with_hooks(
"anon.strict_mode",
"A masking rule cannot change a column data type, unless you disable this",
"Disabling the mode is not recommended",
&ANON_STRICT_MODE,
- GucContext::Suset,
+ GucContext::Userset,
GucFlags::default(),
+ Some(check_bool_guc_hook),
+ None,
+ None,
);
// The GUC vars below are not used in the Rust code
// but they are used in the plpgsql code
- GucRegistry::define_string_guc(
+ GucRegistry::define_string_guc_with_hooks(
"anon.algorithm",
"The hash method used for pseudonymizing functions",
"",
&ANON_ALGORITHM,
GucContext::Suset,
GucFlags::SUPERUSER_ONLY,
+ Some(check_string_guc_hook),
+ None,
+ None,
);
- GucRegistry::define_string_guc(
+ GucRegistry::define_string_guc_with_hooks(
"anon.maskschema",
"The schema where the dynamic masking views are stored",
"",
&ANON_MASK_SCHEMA,
- GucContext::Suset,
+ GucContext::Userset,
GucFlags::default(),
+ Some(check_string_guc_hook),
+ None,
+ None,
);
- GucRegistry::define_string_guc(
+ GucRegistry::define_string_guc_with_hooks(
"anon.salt",
"The salt value used for the pseudonymizing functions",
"",
&ANON_SALT,
GucContext::Suset,
GucFlags::SUPERUSER_ONLY,
+ Some(check_string_guc_hook),
+ None,
+ None,
);
- GucRegistry::define_string_guc(
+ GucRegistry::define_string_guc_with_hooks(
"anon.sourceschema",
"The schema where the table are masked by the dynamic masking engine",
"",
&ANON_SOURCE_SCHEMA,
- GucContext::Suset,
+ GucContext::Userset,
GucFlags::default(),
+ Some(check_string_guc_hook),
+ None,
+ None,
);
}