Skip to content

Commit

Permalink
PG-854: Global keyring support
Browse files Browse the repository at this point in the history
This commit adds a new GUC variable, `pg_tde.inherit_global_providers`
(ON by default) and changes several related API functions.

With these changes, it is now possible to use global key providers
as key providers for database principal keys, when the variable is
ON. When the variable is OFF existing global keyring uses will
continue to work, but no new database configuration is allowed based
on a global provider.

To allow a cleaner API with these changes, the following user
interface changes are also included:
* the rotate_principal_key functions are now gone. Principal keys
  now can be rotated with the set_principal_key function instead.
* The set_principal_key function has a global and a "normal"
  overload.
* The server (WAL) principal key is now rotated with a separate
  function.
* Automatic key versioning is removed: keys are saved on the keyrings
  exactly with the name as it was specified by the user. On disk
  storage is unchanged, existing keys will continue to work, but will
  display the numeric version tag in postgres after this commit.

There's also an internal change about keyring numbering: after this
commit, global providers are generated with negative numbers, and
local providers are generated with positive numbers (as before).
This allows the keyring code to differentiate between them without
changint he disk format, as it can only refer one database Oid - a
negative number identified a global key.

There's no update logic for this, when upgrading from the Beta, old
global keys will continue to exist with positive IDs, but new global
keys will receive negative IDs.
This means that old global keys won't be usable for local use,
set_principal_key displays an appropriate error message for this
situation.

The commit also renames a few internal variables/functions which
were related to the changes and used misleading naming.
  • Loading branch information
dutow committed Jan 13, 2025
1 parent a41f677 commit 03a6800
Show file tree
Hide file tree
Showing 16 changed files with 388 additions and 345 deletions.
1 change: 1 addition & 0 deletions .github/workflows/psp.yml
Original file line number Diff line number Diff line change
Expand Up @@ -40,4 +40,5 @@ jobs:
name: testlog-ubuntu-${{ matrix.ubuntu_version }}.04-meson-${{ matrix.build_type }}
path: |
src/build/testrun/
src/contrib/pg_tde/t/
retention-days: 3
5 changes: 5 additions & 0 deletions contrib/pg_tde/.vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"files.associations": {
"tde_principal_key.h": "c"
}
}
48 changes: 25 additions & 23 deletions contrib/pg_tde/pg_tde--1.0-beta2.sql
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
/* contrib/pg_tde/pg_tde--1.0.sql */
/* contrib/pg_tde/pg_tde--1.0-beta2.sql */

-- complain if script is sourced in psql, rather than via CREATE EXTENSION
\echo Use "CREATE EXTENSION pg_tde" to load this file. \quit
Expand Down Expand Up @@ -119,6 +119,15 @@ RETURNS SETOF record
AS 'MODULE_PATHNAME'
LANGUAGE C STRICT VOLATILE;

CREATE FUNCTION pg_tde_list_all_key_providers
(PG_TDE_GLOBAL, OUT id INT,
OUT provider_name VARCHAR(128),
OUT provider_type VARCHAR(10),
OUT options JSON)
RETURNS SETOF record
AS 'MODULE_PATHNAME'
LANGUAGE C STRICT VOLATILE;

-- Global Tablespace Key Provider Management
CREATE OR REPLACE FUNCTION pg_tde_add_key_provider(PG_TDE_GLOBAL, provider_type VARCHAR(10), provider_name VARCHAR(128), options JSON)
RETURNS INT
Expand Down Expand Up @@ -248,29 +257,31 @@ SELECT EXISTS (
)$$
LANGUAGE SQL;

CREATE FUNCTION pg_tde_rotate_principal_key_internal(new_principal_key_name VARCHAR(255) DEFAULT NULL, new_provider_name VARCHAR(255) DEFAULT NULL, ensure_new_key BOOLEAN DEFAULT TRUE, is_global BOOLEAN DEFAULT FALSE)
CREATE FUNCTION pg_tde_set_principal_key_internal(principal_key_name VARCHAR(255), is_global INT, provider_name VARCHAR(255), ensure_new_key BOOLEAN DEFAULT FALSE)
RETURNS boolean
AS 'MODULE_PATHNAME'
LANGUAGE C;

CREATE FUNCTION pg_tde_rotate_principal_key(new_principal_key_name VARCHAR(255) DEFAULT NULL, new_provider_name VARCHAR(255) DEFAULT NULL)
CREATE FUNCTION pg_tde_set_principal_key(principal_key_name VARCHAR(255), provider_name VARCHAR(255) DEFAULT NULL, ensure_new_key BOOLEAN DEFAULT FALSE)
RETURNS boolean
AS $$
SELECT pg_tde_rotate_principal_key_internal(new_principal_key_name, new_provider_name, TRUE, FALSE);
SELECT pg_tde_set_principal_key_internal(principal_key_name, 0, provider_name, ensure_new_key);
$$
LANGUAGE SQL;

CREATE FUNCTION pg_tde_rotate_principal_key(PG_TDE_GLOBAL, new_principal_key_name VARCHAR(255) DEFAULT NULL, new_provider_name VARCHAR(255) DEFAULT NULL)
CREATE FUNCTION pg_tde_set_principal_key(principal_key_name VARCHAR(255), PG_TDE_GLOBAL, provider_name VARCHAR(255) DEFAULT NULL, ensure_new_key BOOLEAN DEFAULT FALSE)
RETURNS boolean
AS $$
SELECT pg_tde_rotate_principal_key_internal(new_principal_key_name, new_provider_name, TRUE, TRUE);
SELECT pg_tde_set_principal_key_internal(principal_key_name, 1, provider_name, ensure_new_key);
$$
LANGUAGE SQL;

CREATE FUNCTION pg_tde_set_principal_key(principal_key_name VARCHAR(255), provider_name VARCHAR(255), ensure_new_key BOOLEAN DEFAULT FALSE)
CREATE FUNCTION pg_tde_set_server_principal_key(principal_key_name VARCHAR(255), PG_TDE_GLOBAL, provider_name VARCHAR(255) DEFAULT NULL, ensure_new_key BOOLEAN DEFAULT FALSE)
RETURNS boolean
AS 'MODULE_PATHNAME'
LANGUAGE C;
AS $$
SELECT pg_tde_set_principal_key_internal(principal_key_name, 2, provider_name, ensure_new_key);
$$
LANGUAGE SQL;

CREATE FUNCTION pg_tde_alter_principal_key_keyring(new_provider_name VARCHAR(255))
RETURNS boolean
Expand All @@ -286,8 +297,6 @@ CREATE FUNCTION pg_tde_principal_key_info_internal(is_global BOOLEAN)
RETURNS TABLE ( principal_key_name text,
key_provider_name text,
key_provider_id integer,
principal_key_internal_name text,
principal_key_version integer,
key_createion_time timestamp with time zone)
AS 'MODULE_PATHNAME'
LANGUAGE C;
Expand All @@ -296,8 +305,6 @@ CREATE FUNCTION pg_tde_principal_key_info()
RETURNS TABLE ( principal_key_name text,
key_provider_name text,
key_provider_id integer,
principal_key_internal_name text,
principal_key_version integer,
key_createion_time timestamp with time zone)
AS $$
SELECT pg_tde_principal_key_info_internal(FALSE);
Expand All @@ -308,8 +315,6 @@ CREATE FUNCTION pg_tde_principal_key_info(PG_TDE_GLOBAL)
RETURNS TABLE ( principal_key_name text,
key_provider_name text,
key_provider_id integer,
principal_key_internal_name text,
principal_key_version integer,
key_createion_time timestamp with time zone)
AS $$
SELECT pg_tde_principal_key_info_internal(TRUE);
Expand Down Expand Up @@ -425,12 +430,10 @@ BEGIN
PERFORM pg_tde_grant_execute_privilege_on_function(target_user_or_role, 'pg_tde_add_key_provider_vault_v2', 'varchar, JSON, JSON,JSON,JSON');

PERFORM pg_tde_grant_execute_privilege_on_function(target_user_or_role, 'pg_tde_set_principal_key', 'varchar, varchar, BOOLEAN');
PERFORM pg_tde_grant_execute_privilege_on_function(target_user_or_role, 'pg_tde_set_principal_key', 'varchar, pg_tde_global, varchar, BOOLEAN');
PERFORM pg_tde_grant_execute_privilege_on_function(target_user_or_role, 'pg_tde_set_server_principal_key', 'varchar, pg_tde_global, varchar, BOOLEAN');
PERFORM pg_tde_grant_execute_privilege_on_function(target_user_or_role, 'pg_tde_alter_principal_key_keyring', 'varchar');

PERFORM pg_tde_grant_execute_privilege_on_function(target_user_or_role, 'pg_tde_rotate_principal_key', 'pg_tde_global, varchar, varchar');
PERFORM pg_tde_grant_execute_privilege_on_function(target_user_or_role, 'pg_tde_rotate_principal_key', 'varchar, varchar');
PERFORM pg_tde_grant_execute_privilege_on_function(target_user_or_role, 'pg_tde_rotate_principal_key_internal', 'varchar, varchar, BOOLEAN, BOOLEAN');

PERFORM pg_tde_grant_execute_privilege_on_function(target_user_or_role, 'pg_tde_grant_key_management_to_role', 'TEXT');
PERFORM pg_tde_grant_execute_privilege_on_function(target_user_or_role, 'pg_tde_revoke_key_management_from_role', 'TEXT');

Expand All @@ -456,6 +459,7 @@ AS $$
BEGIN
-- Start the transaction block for performing grants
PERFORM pg_tde_grant_execute_privilege_on_function(target_user_or_role, 'pg_tde_list_all_key_providers', 'OUT INT, OUT varchar, OUT varchar, OUT JSON');
PERFORM pg_tde_grant_execute_privilege_on_function(target_user_or_role, 'pg_tde_list_all_key_providers', 'pg_tde_global, OUT INT, OUT varchar, OUT varchar, OUT JSON');
PERFORM pg_tde_grant_execute_privilege_on_function(target_user_or_role, 'pg_tde_is_encrypted', 'VARCHAR');

PERFORM pg_tde_grant_execute_privilege_on_function(target_user_or_role, 'pg_tde_principal_key_info_internal', 'BOOLEAN');
Expand Down Expand Up @@ -494,12 +498,10 @@ BEGIN
PERFORM pg_tde_revoke_execute_privilege_on_function(target_user_or_role, 'pg_tde_add_key_provider_vault_v2', 'varchar, JSON, JSON,JSON,JSON');

PERFORM pg_tde_revoke_execute_privilege_on_function(target_user_or_role, 'pg_tde_set_principal_key', 'varchar, varchar, BOOLEAN');
PERFORM pg_tde_revoke_execute_privilege_on_function(target_user_or_role, 'pg_tde_set_principal_key', 'varchar, pg_tde_global, varchar, BOOLEAN');
PERFORM pg_tde_revoke_execute_privilege_on_function(target_user_or_role, 'pg_tde_set_server_principal_key', 'varchar, pg_tde_global, varchar, BOOLEAN');
PERFORM pg_tde_revoke_execute_privilege_on_function(target_user_or_role, 'pg_tde_alter_principal_key_keyring', 'varchar');

PERFORM pg_tde_revoke_execute_privilege_on_function(target_user_or_role, 'pg_tde_rotate_principal_key', 'pg_tde_global, varchar, varchar');
PERFORM pg_tde_revoke_execute_privilege_on_function(target_user_or_role, 'pg_tde_rotate_principal_key', 'varchar, varchar');
PERFORM pg_tde_revoke_execute_privilege_on_function(target_user_or_role, 'pg_tde_rotate_principal_key_internal', 'varchar, varchar, BOOLEAN, BOOLEAN');

PERFORM pg_tde_revoke_execute_privilege_on_function(target_user_or_role, 'pg_tde_grant_key_management_to_role', 'TEXT');
PERFORM pg_tde_revoke_execute_privilege_on_function(target_user_or_role, 'pg_tde_revoke_key_management_from_role', 'TEXT');

Expand Down
2 changes: 1 addition & 1 deletion contrib/pg_tde/src/access/pg_tde_xlog.c
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ tdeheap_rmgr_redo(XLogReaderState *record)

LWLockAcquire(tde_lwlock_enc_keys(), LW_EXCLUSIVE);
if (info == XLOG_TDE_ADD_PRINCIPAL_KEY)
save_principal_key_info(mkey);
create_principal_key_info(mkey);
else
update_principal_key_info(mkey);

Expand Down
5 changes: 2 additions & 3 deletions contrib/pg_tde/src/catalog/tde_global_space.c
Original file line number Diff line number Diff line change
Expand Up @@ -181,11 +181,10 @@ create_principal_key(const char *key_name, GenericKeyring *keyring, Oid dbOid)

principalKey = palloc(sizeof(TDEPrincipalKey));
principalKey->keyInfo.databaseId = dbOid;
principalKey->keyInfo.keyId.version = DEFAULT_PRINCIPAL_KEY_VERSION;
principalKey->keyInfo.keyringId = keyring->key_id;
principalKey->keyInfo.keyringId = keyring->keyring_id;
strncpy(principalKey->keyInfo.keyId.name, key_name, TDE_KEY_NAME_LEN);
snprintf(principalKey->keyInfo.keyId.versioned_name, TDE_KEY_NAME_LEN,
"%s_%d", principalKey->keyInfo.keyId.name, principalKey->keyInfo.keyId.version);
"%s", principalKey->keyInfo.keyId.name);
gettimeofday(&principalKey->keyInfo.creationTime, NULL);

keyInfo = KeyringGenerateNewKeyAndStore(keyring, principalKey->keyInfo.keyId.versioned_name, INTERNAL_KEY_LEN, false);
Expand Down
24 changes: 16 additions & 8 deletions contrib/pg_tde/src/catalog/tde_keyring.c
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
#include "catalog/tde_principal_key.h"
#include "access/skey.h"
#include "utils/lsyscache.h"
#include "utils/memutils.h"
#include "utils/snapmgr.h"
#include "utils/fmgroids.h"
#include "common/pg_tde_utils.h"
Expand Down Expand Up @@ -207,6 +208,7 @@ write_key_provider_info(KeyringProvideRecord *provider, Oid database_id,
off_t bytes_written = 0;
off_t curr_pos = 0;
int fd;
// Named max, but global key provider oids are stored as negative numbers!
int max_provider_id = 0;
char kp_info_path[MAXPGPATH] = {0};
KeyringProvideRecord existing_provider;
Expand Down Expand Up @@ -247,10 +249,14 @@ write_key_provider_info(KeyringProvideRecord *provider, Oid database_id,
return provider->provider_id;
}
}
if (max_provider_id < existing_provider.provider_id)
max_provider_id = existing_provider.provider_id;
if (max_provider_id < abs(existing_provider.provider_id))
max_provider_id = abs(existing_provider.provider_id);
}
provider->provider_id = max_provider_id + 1;
if(database_id == GLOBAL_DATA_TDE_OID)
{
provider->provider_id = -provider->provider_id;
}
curr_pos = lseek(fd, 0, SEEK_END);

/*
Expand Down Expand Up @@ -353,7 +359,7 @@ pg_tde_add_key_provider_internal(PG_FUNCTION_ARGS)
Datum
pg_tde_list_all_key_providers(PG_FUNCTION_ARGS)
{
List *all_providers = GetAllKeyringProviders(MyDatabaseId);
List *all_providers = GetAllKeyringProviders(PG_NARGS() == 1 ? GLOBAL_DATA_TDE_OID : MyDatabaseId);
ListCell *lc;
Tuplestorestate *tupstore;
TupleDesc tupdesc;
Expand Down Expand Up @@ -393,7 +399,7 @@ pg_tde_list_all_key_providers(PG_FUNCTION_ARGS)
GenericKeyring *keyring = (GenericKeyring *) lfirst(lc);
int i = 0;

values[i++] = Int32GetDatum(keyring->key_id);
values[i++] = Int32GetDatum(keyring->keyring_id);
values[i++] = CStringGetTextDatum(keyring->provider_name);
values[i++] = CStringGetTextDatum(get_keyring_provider_typename(keyring->type));
values[i++] = CStringGetTextDatum(keyring->options);
Expand All @@ -408,8 +414,9 @@ pg_tde_list_all_key_providers(PG_FUNCTION_ARGS)
GenericKeyring *
GetKeyProviderByID(int provider_id, Oid dbOid)
{
Oid realOid = provider_id < 0 ? GLOBAL_DATA_TDE_OID : dbOid;
GenericKeyring *keyring = NULL;
List *providers = scan_key_provider_file(PROVIDER_SCAN_BY_ID, &provider_id, dbOid);
List *providers = scan_key_provider_file(PROVIDER_SCAN_BY_ID, &provider_id, realOid);

if (providers != NIL)
{
Expand All @@ -425,8 +432,9 @@ GetKeyProviderByID(int provider_id, Oid dbOid)
GenericKeyring *
GetKeyProviderByID(int provider_id, Oid dbOid)
{
Oid realOid = provider_id < 0 ? GLOBAL_DATA_TDE_OID : dbOid;
GenericKeyring *keyring = NULL;
SimplePtrList *providers = scan_key_provider_file(PROVIDER_SCAN_BY_ID, &provider_id, dbOid);
SimplePtrList *providers = scan_key_provider_file(PROVIDER_SCAN_BY_ID, &provider_id, realOid);

if (providers != NULL)
{
Expand Down Expand Up @@ -543,7 +551,7 @@ load_keyring_provider_from_record(KeyringProvideRecord *provider)
keyring = load_keyring_provider_options(provider->provider_type, provider->options);
if (keyring)
{
keyring->key_id = provider->provider_id;
keyring->keyring_id = provider->provider_id;
strncpy(keyring->provider_name, provider->provider_name, sizeof(keyring->provider_name));
keyring->type = provider->provider_type;
strncpy(keyring->options, provider->options, sizeof(keyring->options));
Expand Down Expand Up @@ -662,7 +670,7 @@ debug_print_kerying(GenericKeyring *keyring)

elog(debug_level, "Keyring type: %d", keyring->type);
elog(debug_level, "Keyring name: %s", keyring->provider_name);
elog(debug_level, "Keyring id: %d", keyring->key_id);
elog(debug_level, "Keyring id: %d", keyring->keyring_id);
switch (keyring->type)
{
case FILE_KEY_PROVIDER:
Expand Down
Loading

0 comments on commit 03a6800

Please sign in to comment.