From c4ac16420ad0858c7e63f96be876206549538793 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anders=20=C3=85strand?= Date: Wed, 23 Jul 2025 10:20:13 +0200 Subject: [PATCH 1/2] Rename helper function This function counts the number of encryption keys in the key file associated with the given OID. Name it accordingly. Also remove comment about only user which is no longer true. --- contrib/pg_tde/src/access/pg_tde_tdemap.c | 12 +++++------- contrib/pg_tde/src/catalog/tde_principal_key.c | 4 ++-- contrib/pg_tde/src/include/access/pg_tde_tdemap.h | 2 +- contrib/pg_tde/src/pg_tde_event_capture.c | 2 +- 4 files changed, 9 insertions(+), 11 deletions(-) diff --git a/contrib/pg_tde/src/access/pg_tde_tdemap.c b/contrib/pg_tde/src/access/pg_tde_tdemap.c index f285f2e24a0a8..6c1f146e306d3 100644 --- a/contrib/pg_tde/src/access/pg_tde_tdemap.c +++ b/contrib/pg_tde/src/access/pg_tde_tdemap.c @@ -529,7 +529,7 @@ pg_tde_delete_principal_key(Oid dbOid) char path[MAXPGPATH]; Assert(LWLockHeldByMeInMode(tde_lwlock_enc_keys(), LW_EXCLUSIVE)); - Assert(pg_tde_count_relations(dbOid) == 0); + Assert(pg_tde_count_encryption_keys(dbOid) == 0); pg_tde_set_db_file_path(dbOid, path); @@ -672,17 +672,15 @@ pg_tde_find_map_entry(const RelFileLocator *rlocator, TDEMapEntryType key_type, } /* - * Counts number of encrypted objects in a database. + * Counts number of encryption keys in a key file. * * Does not check if objects actually exist but just that they have keys in - * the map file. For the only current caller, checking if we can use - * FILE_COPY, this is good enough but for other workloads where a false - * positive is more harmful this might not be. + * the key file. * - * Works even if the database has no map file. + * Works even if the database has no key file. */ int -pg_tde_count_relations(Oid dbOid) +pg_tde_count_encryption_keys(Oid dbOid) { char db_map_path[MAXPGPATH]; File map_fd; diff --git a/contrib/pg_tde/src/catalog/tde_principal_key.c b/contrib/pg_tde/src/catalog/tde_principal_key.c index 38f962b4794f2..4e030c364298e 100644 --- a/contrib/pg_tde/src/catalog/tde_principal_key.c +++ b/contrib/pg_tde/src/catalog/tde_principal_key.c @@ -700,7 +700,7 @@ pg_tde_delete_key(PG_FUNCTION_ARGS) * If database has something encryted, we can try to fallback to the * default principal key */ - if (pg_tde_count_relations(MyDatabaseId) != 0) + if (pg_tde_count_encryption_keys(MyDatabaseId) != 0) { default_principal_key = GetPrincipalKeyNoDefault(DEFAULT_DATA_TDE_OID, LW_EXCLUSIVE); if (default_principal_key == NULL) @@ -785,7 +785,7 @@ pg_tde_delete_default_key(PG_FUNCTION_ARGS) * delete default principal key if there are encrypted tables in * the database. */ - if (pg_tde_count_relations(dbOid) != 0) + if (pg_tde_count_encryption_keys(dbOid) != 0) { ereport(ERROR, errmsg("cannot delete default principal key"), diff --git a/contrib/pg_tde/src/include/access/pg_tde_tdemap.h b/contrib/pg_tde/src/include/access/pg_tde_tdemap.h index f3177544cf551..e6d6d982404ec 100644 --- a/contrib/pg_tde/src/include/access/pg_tde_tdemap.h +++ b/contrib/pg_tde/src/include/access/pg_tde_tdemap.h @@ -92,7 +92,7 @@ extern bool pg_tde_has_smgr_key(RelFileLocator rel); extern InternalKey *pg_tde_get_smgr_key(RelFileLocator rel); extern void pg_tde_free_key_map_entry(RelFileLocator rel); -extern int pg_tde_count_relations(Oid dbOid); +extern int pg_tde_count_encryption_keys(Oid dbOid); extern void pg_tde_delete_tde_files(Oid dbOid); diff --git a/contrib/pg_tde/src/pg_tde_event_capture.c b/contrib/pg_tde/src/pg_tde_event_capture.c index 9f089f5c36eee..3dcb787c7dd87 100644 --- a/contrib/pg_tde/src/pg_tde_event_capture.c +++ b/contrib/pg_tde/src/pg_tde_event_capture.c @@ -643,7 +643,7 @@ pg_tde_proccess_utility(PlannedStmt *pstmt, int count; LWLockAcquire(tde_lwlock_enc_keys(), LW_SHARED); - count = pg_tde_count_relations(dbOid); + count = pg_tde_count_encryption_keys(dbOid); LWLockRelease(tde_lwlock_enc_keys()); if (count > 0) From 218abaf4453b47235e4b27e4e6a7107a1dc8bd2d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anders=20=C3=85strand?= Date: Wed, 23 Jul 2025 10:29:37 +0200 Subject: [PATCH 2/2] PG-1658 Remove server key when removing default key Previously this was left behind even if the default key was deleted. Check if any WAL encryption keys exist and allow removal if there are none. --- .../pg_tde/expected/delete_principal_key.out | 40 +++++++++++++++++++ contrib/pg_tde/sql/delete_principal_key.sql | 11 +++++ .../pg_tde/src/catalog/tde_principal_key.c | 18 ++++++++- 3 files changed, 67 insertions(+), 2 deletions(-) diff --git a/contrib/pg_tde/expected/delete_principal_key.out b/contrib/pg_tde/expected/delete_principal_key.out index 550bcc217b2dd..92b8299c2b725 100644 --- a/contrib/pg_tde/expected/delete_principal_key.out +++ b/contrib/pg_tde/expected/delete_principal_key.out @@ -134,6 +134,46 @@ SELECT pg_tde_delete_key(); (1 row) +-- Delete default key even if it's configured for a database or server key, as +-- long as it's unused. Regardless how the key was set, we unset it if it's the +-- same key as is used as a default key. This is probably a bug. +SELECT pg_tde_set_default_key_using_global_key_provider('test-db-key','file-provider'); + pg_tde_set_default_key_using_global_key_provider +-------------------------------------------------- + +(1 row) + +SELECT pg_tde_set_key_using_global_key_provider('test-db-key','file-provider'); + pg_tde_set_key_using_global_key_provider +------------------------------------------ + +(1 row) + +SELECT pg_tde_set_server_key_using_global_key_provider('test-db-key','file-provider'); +WARNING: The WAL encryption feature is currently in beta and may be unstable. Do not use it in production environments! + pg_tde_set_server_key_using_global_key_provider +------------------------------------------------- + +(1 row) + +SELECT pg_tde_delete_default_key(); + pg_tde_delete_default_key +--------------------------- + +(1 row) + +SELECT pg_tde_key_info(); -- No key configured + pg_tde_key_info +----------------- + (,,,) +(1 row) + +SELECT pg_tde_server_key_info(); -- No key configured + pg_tde_server_key_info +------------------------ + (,,,) +(1 row) + SELECT pg_tde_delete_global_key_provider('file-provider'); pg_tde_delete_global_key_provider ----------------------------------- diff --git a/contrib/pg_tde/sql/delete_principal_key.sql b/contrib/pg_tde/sql/delete_principal_key.sql index abc4c574b5616..142eedde0a56d 100644 --- a/contrib/pg_tde/sql/delete_principal_key.sql +++ b/contrib/pg_tde/sql/delete_principal_key.sql @@ -53,5 +53,16 @@ SELECT pg_tde_delete_default_key(); DROP TABLE test_table; SELECT pg_tde_delete_key(); + +-- Delete default key even if it's configured for a database or server key, as +-- long as it's unused. Regardless how the key was set, we unset it if it's the +-- same key as is used as a default key. This is probably a bug. +SELECT pg_tde_set_default_key_using_global_key_provider('test-db-key','file-provider'); +SELECT pg_tde_set_key_using_global_key_provider('test-db-key','file-provider'); +SELECT pg_tde_set_server_key_using_global_key_provider('test-db-key','file-provider'); +SELECT pg_tde_delete_default_key(); +SELECT pg_tde_key_info(); -- No key configured +SELECT pg_tde_server_key_info(); -- No key configured + SELECT pg_tde_delete_global_key_provider('file-provider'); DROP EXTENSION pg_tde; diff --git a/contrib/pg_tde/src/catalog/tde_principal_key.c b/contrib/pg_tde/src/catalog/tde_principal_key.c index 4e030c364298e..d795eb014ee61 100644 --- a/contrib/pg_tde/src/catalog/tde_principal_key.c +++ b/contrib/pg_tde/src/catalog/tde_principal_key.c @@ -798,8 +798,22 @@ pg_tde_delete_default_key(PG_FUNCTION_ARGS) } /* - * Remove empty key map files for databases that has no encrypted tables - * as we cannot leave reference to the default principal key. + * The default key may have been used as server key, check if there are + * any WAL encryption keys that uses it. + */ + principal_key = GetPrincipalKeyNoDefault(GLOBAL_DATA_TDE_OID, LW_EXCLUSIVE); + if (pg_tde_is_same_principal_key(default_principal_key, principal_key)) + { + if (pg_tde_count_encryption_keys(GLOBAL_DATA_TDE_OID) != 0) + ereport(ERROR, + errmsg("cannot delete default principal key"), + errhint("There are WAL encryption keys.")); + dbs = lappend_oid(dbs, GLOBAL_DATA_TDE_OID); + } + + /* + * Remove empty key files for OIDs that have no encryption keys as we + * cannot leave references to the default principal key. */ foreach_oid(dbOid, dbs) {