Skip to content
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

Implement interfaces and framework for multi tenancy #118

Closed
wants to merge 4 commits into from

Conversation

codeforall
Copy link
Contributor

The PR aims towards building support for multi tenancy and contains multiple commits for the following.

  • Establishing a Framework for Master Key.
  • New Shared Cache Management Framework.
  • Adding catalog table for managing key providers.
  • Revamping the Keyring API Interface and Integrating Master Key.

Individual commit messages contain a more detailed description of changes.

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_file_wallet(provider_name VARCHAR(128), file_path TEXT)
- pg_tde_add_wallet(provider_type VARCHAR(10),
                 provider_name VARCHAR(128), options JSON)
- pg_tde_add_vault_v2_wallet(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 text, provider_name text);
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.
Copy link
Collaborator

@dutow dutow left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I only added two minor comments/questions related to the public interface

{
case PROVIDER_FILE:
keyringFilePreloadCache();
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are you sure we don't need the preload function? For some keyrings, such as file, it makes sense to read everything at startup

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since we would only require one master key at any time for a database as no version number gets appended with the key name so IMHO reading only one required key should be enough.

LANGUAGE SQL;


CREATE OR REPLACE FUNCTION pg_tde_add_wallet(provider_type VARCHAR(10), provider_name VARCHAR(128), options JSON)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This wallet name seems strange to me, I would keep calling them provider

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Makes sense. I have changed the function names as you suggested in the latest (#121) PR

Comment on lines +13 to +26
CREATE OR REPLACE FUNCTION pg_tde_set_master_key(provider_type VARCHAR(10), provider_name VARCHAR(128), options JSON)
RETURNS INT
AS $$
INSERT INTO percona_tde.pg_tde_key_provider (keyring_type, provider_name, options) VALUES (provider_type, provider_name, options) RETURNING provider_id;
$$
LANGUAGE SQL;


CREATE OR REPLACE FUNCTION pg_tde_add_wallet(provider_type VARCHAR(10), provider_name VARCHAR(128), options JSON)
RETURNS INT
AS $$
INSERT INTO percona_tde.pg_tde_key_provider (keyring_type, provider_name, options) VALUES (provider_type, provider_name, options) RETURNING provider_id;
$$
LANGUAGE SQL;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is the difference between those functions?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The function was left as an oversight. fixed in the latest (#121) PR

@@ -13,9 +64,12 @@ RETURNS boolean
AS $$ SELECT amname = 'pg_tde' FROM pg_class INNER JOIN pg_am ON pg_am.oid = pg_class.relam WHERE relname = table_name $$
LANGUAGE SQL;

CREATE FUNCTION pg_tde_set_master_key(master_key_name text, provider_name text)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Given that #define TDE_KEY_NAME_LEN 256, should master_key_name be VARCHAR(256)?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in (#121) PR

Comment on lines +29 to +50
RETURNS INT
AS $$
SELECT pg_tde_add_wallet('file', provider_name,
json_object('type' VALUE 'file', 'path' VALUE file_path));
$$
LANGUAGE SQL;

CREATE OR REPLACE FUNCTION pg_tde_add_vault_v2_wallet(provider_name VARCHAR(128),
valut_token TEXT,
valut_url TEXT,
valut_mount_path TEXT,
valut_ca_path TEXT)
RETURNS INT
AS $$
SELECT pg_tde_add_wallet('vault-v2', provider_name,
json_object('type' VALUE 'vault-v2',
'url' VALUE valut_url,
'token' VALUE valut_token,
'mountPath' VALUE valut_mount_path,
'caPath' VALUE valut_ca_path));
$$
LANGUAGE SQL;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should be comments that these relations structure must be in sync with catalog/tde_keyring.c's macros

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in (#121) PR

#include "lib/dshash.h"
#include "utils/dsa.h"

typedef struct TDEShmemSetupRoutine
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Need comments on interface's functions - what should be implemented (what interface expects from them)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have added a few comments in shared memory .h file in the latest PR


RequestAddinShmemSpace(keyringCacheMemorySize());
RequestNamedLWLockTranche("pg_tde_tranche", required_locks);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should "pg_tde_tranche" be a macros?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

Comment on lines +387 to +388
masterKey = get_master_key_from_cache();
if (!masterKey)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But what should be done if masterKey exists here? It must be an error as in L361-370?
I think either get_master_key_from_cache() and get_master_key_info() checks in L361-380 should be under the lock or they should be duplicated after we acquired the lock (if we want to have cheap pre-check before locking - in that case it make sense to make a macros for that code) otherwise function will have unpredictable for the caller behaviour.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You are right. I have fixed it in #121

errmsg("Failed to open keyring file %s :%m", file_keyring->file_name)));
return KEYRING_CODE_RESOURCE_NOT_ACCESSABLE;
}
bytes_written = FileWrite(file, key, sizeof(keyInfo), 0, WAIT_EVENT_DATA_FILE_WRITE);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Doesn't this overwrite the file with a single key again and again, if we have multiple master keys?

The previous implementation also overwrote the file, but dumped the entire cache, not just one key.

Copy link
Contributor Author

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. Fixed in #121

@dAdAbird
Copy link
Member

Do we need this PR anymore (having #121)?

@codeforall codeforall closed this Feb 27, 2024
@codeforall
Copy link
Contributor Author

There is a new PR for the same. Closing this draft

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants