-
Notifications
You must be signed in to change notification settings - Fork 19
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Framework for multi-tenancy support #121
Conversation
This commit introduces a user catalog table, percona_tde.pg_tde_key_provider, within the percona_tde schema, as part of the pg_tde extension. The purpose of this table is to store essential provider information. The catalog accommodates various key providers, present and future, utilizing a JSON type options field to capture provider-specific details. To facilitate the creation of key providers, the commit introduces new SQL interfaces: - pg_tde_add_key_provider(provider_type VARCHAR(10), provider_name VARCHAR(128), options JSON) - pg_tde_add_key_provider_file(provider_name VARCHAR(128), file_path TEXT) - pg_tde_add_key_provider_vault_v2(provider_name VARCHAR(128), vault_token TEXT, vault_url TEXT, vault_mount_path TEXT, vault_ca_path TEXT) Additionally, the commit implements the C interface for catalog interaction, detailed in the 'tde_keyring.h' file. These changes lay the foundation for implementing multi-tenancy in pg_tde by eliminating the necessity of a 'keyring.json' file for configuring a cluster-wide key provider. With this enhancement, each database can have its dedicated key provider, added via SQL interface, removing the need for DBA intervention in TDE setup."
Up until now, pg_tde relied on a hard-coded master key name, primarily for proof-of-concept purposes. This commit introduces a more robust infrastructure for configuring the master key and managing a dynamic shared memory-based master-key cache to enhance accessibility. For user interaction, a new SQL interface is provided: - pg_tde_set_master_key(master_key_name VARCHAR(255), provider_name VARCHAR(255)); This interface enables users to set a master key for a specific database and make further enhancements toward implementing the multi-tenancy. In addition to the public SQL interface, the commit optimizes the internal master-key API. It introduces straightforward Get and Set functions, handling locking, retrieval, caching, and assignment of a master key for a database seamlessly. The commit also introduces a unified internal interface for requesting and utilizing shared memory, contributing to a more cohesive and efficient master key and cache management system.
This commit unifies the master-key and key-provider modules with the core of pg_tde, marking a significant evolution in the architecture. As part of this integration, the keyring API undergoes substantial changes to enhance flexibility and remove unnecessary components such as the key cache. As a result of the keyring refactoring, the file keyring is also rewritten, offering a template for implementing additional key providers for the extension. The modifications make the keyring API more pluggable, streamlining interactions and paving the way for future enhancements.
…rements This commit addresses PostgreSQL core's requirement for upfront information regarding the number of locks needed by the extension. Given the connection between locks and the shared memory interface, a new callback routine is introduced. This routine allows modules to specify the number of locks they require. In addition to this functionality, the commit includes code cleanups and adjustments to nomenclature for improved clarity and consistency.
sql/move_large_tuples.sql
Outdated
@@ -1,6 +1,9 @@ | |||
-- test pg_tde_move_encrypted_data() | |||
CREATE EXTENSION pg_tde; | |||
|
|||
SELECT pg_tde_add_key_provider_file('file-valut','/tmp/pg_tde_test_keyring.per'); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here and in other files: valut
looks like a typo
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed!
sql/toast_extended_storage.sql
Outdated
@@ -1,6 +1,9 @@ | |||
-- test https://github.com/Percona-Lab/pg_tde/issues/63 | |||
CREATE EXTENSION pg_tde; | |||
|
|||
SELECT pg_tde_add_key_provider_file('file-valut','/tmp/pg_tde_test_keyring.per'); | |||
--SELECT pg_tde_set_master_key('test-db-master-key','file-valut'); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why is this commented out?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Right now we need to figure out a way to delete the files created by extension if the extension is deleted. So I have intentionally commented out these lines to take care of that. IMHO we can handle this as a separate PR
src/catalog/tde_keyring.c
Outdated
keyring_name = TextDatumGetCString(datum); | ||
|
||
option_datum = heap_getattr(tuple, PG_TDE_KEY_PROVIDER_OPTIONS_ATTRNUM, tupDesc, &isnull); | ||
/*keyring_options = TextDatumGetCString(option_datum);*/ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why is this commented out?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed!.
src/catalog/tde_keyring.c
Outdated
FileKeyring *file_keyring = palloc0(sizeof(FileKeyring)); | ||
/* | ||
Datum file_type; | ||
file_type = DirectFunctionCall2(json_object_field_text, keyring_options, CStringGetTextDatum(FILE_KEYRING_TYPE_KEY)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why the duplicated commented out code?
src/include/catalog/tde_keyring.h
Outdated
{ | ||
ProviderType type; /* Must be the first field */ | ||
Oid keyId; | ||
char keyName[128]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Shouldn't we add a define for 128?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done!
src/include/catalog/tde_master_key.h
Outdated
{ | ||
Oid databaseId; | ||
uint32 keyVersion; | ||
Oid keyringId; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
indentation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done!
src/include/keyring/keyring_api.h
Outdated
} keyName; | ||
|
||
typedef struct keyData | ||
{ | ||
unsigned char data[32]; // maximum 256 bit encryption | ||
unsigned char data[MAX_KEY_DATA_SIZE]; /* maximum 256 bit encryption */ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think we need this comment at both places
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done!
src/include/keyring/keyring_api.h
Outdated
extern KeyringReturnCodes KeyringStoreKey(GenericKeyring *keyring, keyInfo *key, bool throw_error); | ||
extern keyInfo *KeyringGetKey(GenericKeyring *keyring, const char *key_name, bool throw_error, KeyringReturnCodes *returnCode); | ||
|
||
extern keyInfo *keyringGenerateNewKeyAndStore(GenericKeyring *keyring, const char *key_name, unsigned key_len, bool throw_error); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
KeyringGenerate
to match the other functions?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not fixed, the k
is still small, this file now has functions with different naming conventions
errmsg("Failed to open keyring file %s :%m", file_keyring->file_name))); | ||
return KEYRING_CODE_RESOURCE_NOT_ACCESSABLE; | ||
} | ||
/* Write key to the end of file */ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Shouldn't this function also report an error if the key already exists in the file?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You are right! Committed the fix
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It also makes to run pgindent
on new files.
@@ -18,9 +74,12 @@ RETURNS boolean | |||
AS 'MODULE_PATHNAME' | |||
LANGUAGE C; | |||
|
|||
CREATE FUNCTION pg_tde_set_master_key(master_key_name VARCHAR(255), provider_name VARCHAR(255)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In percona_tde.pg_tde_key_provider
, provider_name
len is 256
but here is 255
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for pointing it out. I've updated the catalog definition.
* push the master key for current database to the shared memory cache | ||
*/ | ||
static void | ||
push_master_key_to_cache(TDEMasterKey *masterKey) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As a TODO for the future: set a limit on the cache size (and add eviction policy). As I understand, now cache might grow indefinitely
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That is a correct observation. At the same time, we would only have one cache entry for each database, so I am not expecting the cache to grow insanely huge, So IMHO we can take care of this at a later stage. For now I have added a TODO note on the function.
Co-authored-by: Andrew Pogrebnoi <[email protected]>
Co-authored-by: Andrew Pogrebnoi <[email protected]>
Adjusting provider_name length in pg_tde_key_provider catalog definition and adding a TODO note for implementing an eviction policy for the master key cache.
This commit enhances the extension by adding a new mechanism to facilitate cleanup or setup procedures when the extension is installed in a database. The core addition is a function "pg_tde_extension_initialize" invoked upon executing the database's 'CREATE EXTENSION' command. To streamline future development and ensure extensibility, the commit introduces a callback registration mechanism. This enables any module to specify a callback function (registered using on_ext_install() ) to be invoked during extension creation. As of this commit, the callback functionality is explicitly utilized by the master key module to handle the cleanup of the master key information file. This file might persist in the database directory if the extension had been previously deleted in the same database. This enhancement paves the way for a more modular and maintainable extension architecture, allowing individual modules to manage their specific setup and cleanup tasks seamlessly." Commit also contains the test case adjustments.
src/catalog/tde_keyring.c
Outdated
keyring = load_keyring_provider_options(provider_type, option_datum); | ||
if (keyring) | ||
{ | ||
strncpy(keyring->keyName, keyring_name, sizeof(keyring->keyName)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We are missing a null check for keyring_name
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Catalog definition ensures that we don't get NULL keyring name
src/catalog/tde_keyring.c
Outdated
Datum file_path; | ||
FileKeyring *file_keyring = palloc0(sizeof(FileKeyring)); | ||
file_path = DirectFunctionCall2(json_object_field_text, keyring_options, CStringGetTextDatum(FILE_KEYRING_PATH_KEY)); | ||
/* TODO check NULL and verify type */ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Shouldn't be there an issue about this?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Adjusted the function definition to ensure we do not get a NULL JSON value
src/catalog/tde_master_key.c
Outdated
Assert(keyring != NULL); | ||
|
||
masterKeyInfo = palloc(sizeof(TDEMasterKeyInfo)); | ||
masterKeyInfo->keyId = 0; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@codeforall ^ still a question
src/catalog/tde_master_key.c
Outdated
|
||
masterKey = palloc(sizeof(TDEMasterKey)); | ||
masterKey->databaseId = MyDatabaseId; | ||
masterKey->keyVersion = 1; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
src/include/keyring/keyring_api.h
Outdated
extern KeyringReturnCodes KeyringStoreKey(GenericKeyring *keyring, keyInfo *key, bool throw_error); | ||
extern keyInfo *KeyringGetKey(GenericKeyring *keyring, const char *key_name, bool throw_error, KeyringReturnCodes *returnCode); | ||
|
||
extern keyInfo *keyringGenerateNewKeyAndStore(GenericKeyring *keyring, const char *key_name, unsigned key_len, bool throw_error); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not fixed, the k
is still small, this file now has functions with different naming conventions
src/keyring/keyring_file.c
Outdated
get_key_by_name(GenericKeyring* keyring, const char* key_name, bool throw_error, KeyringReturnCodes *returnCode) | ||
{ | ||
keyInfo* key = NULL; | ||
File file = -1; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
indentation (at multiple places in this file)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I tried to fix as many as I could find. It seems like my editor needs some setting fixes as everything appears correctly indented.
src/keyring/keyring_file.c
Outdated
|
||
datafile = keyringParseStringParam(dataO); | ||
static keyInfo* | ||
get_key_by_name(GenericKeyring* keyring, const char* key_name, bool throw_error, KeyringReturnCodes *returnCode) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
And this function also mixes naming styles (key_name and returnCode)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see no changes for keyring vault. Will it continue to work after these changes?
@@ -2,6 +2,9 @@ | |||
-- | |||
CREATE EXTENSION pg_tde; | |||
|
|||
SELECT pg_tde_add_key_provider_file('file-vault','/tmp/pg_tde_test_keyring.per'); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I realized an issue with these changes: now the tests are hardcoded to only run with keyring file, previously we ran them both with keyring file and vault. With these changes, effectively we no longer execute vault tests.
If we go in this direction, we have to duplicate all test files, and later when we add additional providers, copy and copy them again and again. I don't think this is a good approach, but I do not see how we can do it differently, as the tests would require SQL commands before execution.
Commit also contains the fix for review comments and a test case for Vault-V2 type key provider
The PR aims to build multi-tenancy support and contains multiple commits for the following.
Individual commit messages contain a more detailed description of changes.
Few Notes: