diff --git a/contrib/pg_tde/Makefile b/contrib/pg_tde/Makefile index 52f61b954f172..e4891a38a777a 100644 --- a/contrib/pg_tde/Makefile +++ b/contrib/pg_tde/Makefile @@ -59,6 +59,7 @@ src/common/pg_tde_utils.o \ src/smgr/pg_tde_smgr.o \ src/pg_tde_defs.o \ src/pg_tde_event_capture.o \ +src/pg_tde_guc.o \ src/pg_tde.o \ src/libkmip/libkmip/src/kmip.o \ src/libkmip/libkmip/src/kmip_bio.o \ diff --git a/contrib/pg_tde/expected/change_access_method.out b/contrib/pg_tde/expected/change_access_method.out index e995bd2debc87..34c13058d3ac0 100644 --- a/contrib/pg_tde/expected/change_access_method.out +++ b/contrib/pg_tde/expected/change_access_method.out @@ -87,5 +87,29 @@ SELECT pg_tde_is_encrypted('country_table'); t (1 row) +CREATE TABLE country_table2 ( + country_id serial primary key, + country_name text unique not null, + continent text not null +); +SET pg_tde.enforce_encryption = ON; +CREATE TABLE country_table3 ( + country_id serial primary key, + country_name text unique not null, + continent text not null +) USING heap; +psql:sql/change_access_method.inc:54: ERROR: pg_tde.enforce_encryption is ON, only encrypted tables can be created. + +ALTER TABLE country_table SET access method heap; +psql:sql/change_access_method.inc:56: ERROR: pg_tde.enforce_encryption is ON, only encrypted tables can be created. +ALTER TABLE country_table2 SET access method :tde_am; +CREATE TABLE country_table3 ( + country_id serial primary key, + country_name text unique not null, + continent text not null +) using :tde_am; DROP TABLE country_table; +DROP TABLE country_table2; +DROP TABLE country_table3; +SET pg_tde.enforce_encryption = OFF; DROP EXTENSION pg_tde; diff --git a/contrib/pg_tde/expected/change_access_method_basic.out b/contrib/pg_tde/expected/change_access_method_basic.out index 414cda847bc69..5d0aca316d20c 100644 --- a/contrib/pg_tde/expected/change_access_method_basic.out +++ b/contrib/pg_tde/expected/change_access_method_basic.out @@ -112,5 +112,32 @@ SELECT pg_tde_is_encrypted('country_table'); t (1 row) +CREATE TABLE country_table2 ( + country_id serial primary key, + country_name text unique not null, + continent text not null +); +SET pg_tde.enforce_encryption = ON; +CREATE TABLE country_table3 ( + country_id serial primary key, + country_name text unique not null, + continent text not null +) USING heap; +psql:sql/change_access_method.inc:54: ERROR: pg_tde.enforce_encryption is ON, only encrypted tables can be created. + +ALTER TABLE country_table SET access method heap; +psql:sql/change_access_method.inc:56: ERROR: pg_tde.enforce_encryption is ON, only encrypted tables can be created. +ALTER TABLE country_table2 SET access method :tde_am; +psql:sql/change_access_method.inc:58: ERROR: pg_tde.enforce_encryption is ON, only encrypted tables can be created. +CREATE TABLE country_table3 ( + country_id serial primary key, + country_name text unique not null, + continent text not null +) using :tde_am; +psql:sql/change_access_method.inc:64: ERROR: pg_tde.enforce_encryption is ON, only encrypted tables can be created. DROP TABLE country_table; +DROP TABLE country_table2; +DROP TABLE country_table3; +psql:sql/change_access_method.inc:68: ERROR: table "country_table3" does not exist +SET pg_tde.enforce_encryption = OFF; DROP EXTENSION pg_tde; diff --git a/contrib/pg_tde/meson.build b/contrib/pg_tde/meson.build index ec4e1f9b12563..ce90a1feb1891 100644 --- a/contrib/pg_tde/meson.build +++ b/contrib/pg_tde/meson.build @@ -41,8 +41,9 @@ pg_tde_sources = files( 'src/common/pg_tde_shmem.c', 'src/common/pg_tde_utils.c', 'src/pg_tde_defs.c', - 'src/pg_tde.c', 'src/pg_tde_event_capture.c', + 'src/pg_tde_guc.c', + 'src/pg_tde.c', ) incdir = include_directories(src_version / 'include', 'src/include', '.', 'src/libkmip/libkmip/include/') diff --git a/contrib/pg_tde/sql/change_access_method.inc b/contrib/pg_tde/sql/change_access_method.inc index b82a4d5a67ffd..9a0c8a7fc08cd 100644 --- a/contrib/pg_tde/sql/change_access_method.inc +++ b/contrib/pg_tde/sql/change_access_method.inc @@ -39,5 +39,34 @@ INSERT INTO country_table (country_name, continent) SELECT * FROM country_table; SELECT pg_tde_is_encrypted('country_table'); +CREATE TABLE country_table2 ( + country_id serial primary key, + country_name text unique not null, + continent text not null +); + +SET pg_tde.enforce_encryption = ON; + +CREATE TABLE country_table3 ( + country_id serial primary key, + country_name text unique not null, + continent text not null +) USING heap; + +ALTER TABLE country_table SET access method heap; + +ALTER TABLE country_table2 SET access method :tde_am; + +CREATE TABLE country_table3 ( + country_id serial primary key, + country_name text unique not null, + continent text not null +) using :tde_am; + DROP TABLE country_table; +DROP TABLE country_table2; +DROP TABLE country_table3; + +SET pg_tde.enforce_encryption = OFF; + DROP EXTENSION pg_tde; diff --git a/contrib/pg_tde/src/access/pg_tde_xlog_encrypt.c b/contrib/pg_tde/src/access/pg_tde_xlog_encrypt.c index e706e4bb48e2d..3963e488250f0 100644 --- a/contrib/pg_tde/src/access/pg_tde_xlog_encrypt.c +++ b/contrib/pg_tde/src/access/pg_tde_xlog_encrypt.c @@ -31,13 +31,14 @@ #include "pg_tde_fe.h" #endif +#include "pg_tde_guc.h" + static XLogPageHeaderData DecryptCurrentPageHrd; static void SetXLogPageIVPrefix(TimeLineID tli, XLogRecPtr lsn, char *iv_prefix); #ifndef FRONTEND /* GUC */ -static bool EncryptXLog = false; static XLogPageHeaderData EncryptCurrentPageHrd; @@ -81,22 +82,6 @@ TDEXlogCheckSane(void) } } -void -XLogInitGUC(void) -{ - DefineCustomBoolVariable("pg_tde.wal_encrypt", /* name */ - "Enable/Disable encryption of WAL.", /* short_desc */ - NULL, /* long_desc */ - &EncryptXLog, /* value address */ - false, /* boot value */ - PGC_POSTMASTER, /* context */ - 0, /* flags */ - NULL, /* check_hook */ - NULL, /* assign_hook */ - NULL /* show_hook */ - ); -} - static int XLOGChooseNumBuffers(void) { diff --git a/contrib/pg_tde/src/catalog/tde_principal_key.c b/contrib/pg_tde/src/catalog/tde_principal_key.c index b507c89434a91..21c8e32ffaff4 100644 --- a/contrib/pg_tde/src/catalog/tde_principal_key.c +++ b/contrib/pg_tde/src/catalog/tde_principal_key.c @@ -40,11 +40,10 @@ #else #include "pg_tde_fe.h" #endif +#include "pg_tde_guc.h" #include -bool AllowInheritGlobalProviders = true; - #ifndef FRONTEND typedef struct TdePrincipalKeySharedState @@ -757,22 +756,6 @@ GetPrincipalKey(Oid dbOid, LWLockMode lockMode) #ifndef FRONTEND -void -PrincipalKeyGucInit() -{ - DefineCustomBoolVariable("pg_tde.inherit_global_providers", /* name */ - "Allow using global key providers for databases.", /* short_desc */ - NULL, /* long_desc */ - &AllowInheritGlobalProviders, /* value address */ - true, /* boot value */ - PGC_SUSET, /* context */ - 0, /* flags */ - NULL, /* check_hook */ - NULL, /* assign_hook */ - NULL /* show_hook */ - ); -} - static bool pg_tde_is_provider_used(Oid databaseOid, Oid providerId) { diff --git a/contrib/pg_tde/src/include/pg_tde_guc.h b/contrib/pg_tde/src/include/pg_tde_guc.h new file mode 100644 index 0000000000000..da7dae94e6552 --- /dev/null +++ b/contrib/pg_tde/src/include/pg_tde_guc.h @@ -0,0 +1,26 @@ +/*------------------------------------------------------------------------- + * + * pg_tde_guc.h + * GUC variables for pg_tde + * + * src/include/pg_tde_guc.h + * + *------------------------------------------------------------------------- + */ + +#ifndef TDE_GUC_H +#define TDE_GUC_H + + +#include "postgres.h" + +#ifndef FRONTEND + +extern bool AllowInheritGlobalProviders; +extern bool EncryptXLog; +extern bool EnforceEncryption; + +void TdeGucInit(void); + +#endif +#endif /* TDE_GUC_H */ diff --git a/contrib/pg_tde/src/pg_tde.c b/contrib/pg_tde/src/pg_tde.c index a1a325e1fc40a..836972a6476f8 100644 --- a/contrib/pg_tde/src/pg_tde.c +++ b/contrib/pg_tde/src/pg_tde.c @@ -39,6 +39,7 @@ #include "catalog/tde_global_space.h" #include "utils/percona.h" #endif +#include "pg_tde_guc.h" #include @@ -113,13 +114,11 @@ _PG_init(void) check_percona_api_version(); #endif + TdeGucInit(); + InitializePrincipalKeyInfo(); InitializeKeyProviderInfo(); -#ifdef PERCONA_EXT - XLogInitGUC(); -#endif - PrincipalKeyGucInit(); prev_shmem_request_hook = shmem_request_hook; shmem_request_hook = tde_shmem_request; prev_shmem_startup_hook = shmem_startup_hook; diff --git a/contrib/pg_tde/src/pg_tde_event_capture.c b/contrib/pg_tde/src/pg_tde_event_capture.c index 76f1e67004f2b..66e71904486d8 100644 --- a/contrib/pg_tde/src/pg_tde_event_capture.c +++ b/contrib/pg_tde/src/pg_tde_event_capture.c @@ -21,6 +21,7 @@ #include "commands/event_trigger.h" #include "common/pg_tde_utils.h" #include "pg_tde_event_capture.h" +#include "pg_tde_guc.h" #include "catalog/tde_principal_key.h" #include "miscadmin.h" #include "access/tableam.h" @@ -40,6 +41,40 @@ GetCurrentTdeCreateEvent(void) return &tdeCurrentCreateEvent; } +static void +checkEncryptionClause(const char *accessMethod) +{ + TDEPrincipalKey *principal_key; + + if (accessMethod && strcmp(accessMethod, "tde_heap") == 0) + { + tdeCurrentCreateEvent.encryptMode = true; + } + else if ((accessMethod == NULL || accessMethod[0] == 0) && strcmp(default_table_access_method, "tde_heap") == 0) + { + tdeCurrentCreateEvent.encryptMode = true; + } + + if (tdeCurrentCreateEvent.encryptMode) + { + LWLockAcquire(tde_lwlock_enc_keys(), LW_SHARED); + principal_key = GetPrincipalKey(MyDatabaseId, LW_SHARED); + LWLockRelease(tde_lwlock_enc_keys()); + if (principal_key == NULL) + { + ereport(ERROR, + (errmsg("failed to retrieve principal key. Create one using pg_tde_set_principal_key before using encrypted tables."))); + + } + } + + if (EnforceEncryption && !tdeCurrentCreateEvent.encryptMode) + { + ereport(ERROR, + (errmsg("pg_tde.enforce_encryption is ON, only encrypted tables can be created."))); + } +} + /* * pg_tde_ddl_command_start_capture is an event trigger function triggered * at the start of any DDL command execution. @@ -99,72 +134,50 @@ pg_tde_ddl_command_start_capture(PG_FUNCTION_ARGS) else if (IsA(parsetree, CreateStmt)) { CreateStmt *stmt = (CreateStmt *) parsetree; - TDEPrincipalKey *principal_key; + const char *accessMethod = stmt->accessMethod; tdeCurrentCreateEvent.eventType = TDE_TABLE_CREATE_EVENT; tdeCurrentCreateEvent.relation = stmt->relation; - if (stmt->accessMethod && strcmp(stmt->accessMethod, "tde_heap") == 0) - { - tdeCurrentCreateEvent.encryptMode = true; - } - else if ((stmt->accessMethod == NULL || stmt->accessMethod[0] == 0) && strcmp(default_table_access_method, "tde_heap") == 0) - { - tdeCurrentCreateEvent.encryptMode = true; - } - - if (tdeCurrentCreateEvent.encryptMode) - { - LWLockAcquire(tde_lwlock_enc_keys(), LW_SHARED); - principal_key = GetPrincipalKey(MyDatabaseId, LW_SHARED); - LWLockRelease(tde_lwlock_enc_keys()); - if (principal_key == NULL) - { - ereport(ERROR, - (errmsg("failed to retrieve principal key. Create one using pg_tde_set_principal_key before using encrypted tables."))); + checkEncryptionClause(accessMethod); + } + else if (IsA(parsetree, CreateTableAsStmt)) + { + CreateTableAsStmt *stmt = (CreateTableAsStmt *) parsetree; + const char *accessMethod = stmt->into->accessMethod; - } - } + tdeCurrentCreateEvent.eventType = TDE_TABLE_CREATE_EVENT; + tdeCurrentCreateEvent.relation = stmt->into->rel; + checkEncryptionClause(accessMethod); } else if (IsA(parsetree, AlterTableStmt)) { - LOCKMODE lockmode = AccessShareLock; /* TODO. Verify lock mode? */ AlterTableStmt *stmt = (AlterTableStmt *) parsetree; ListCell *lcmd; + bool isAccessMethodChange = false; + foreach(lcmd, stmt->cmds) { AlterTableCmd *cmd = (AlterTableCmd *) lfirst(lcmd); - if (cmd->subtype == AT_SetAccessMethod && - ((cmd->name != NULL && strcmp(cmd->name, "tde_heap") == 0) || - (cmd->name == NULL && strcmp(default_table_access_method, "tde_heap") == 0)) - ) + if (cmd->subtype == AT_SetAccessMethod) { - tdeCurrentCreateEvent.encryptMode = true; + const char *accessMethod = cmd->name; + + isAccessMethodChange = true; + tdeCurrentCreateEvent.eventType = TDE_TABLE_CREATE_EVENT; tdeCurrentCreateEvent.relation = stmt->relation; + checkEncryptionClause(accessMethod); } } - if (tdeCurrentCreateEvent.encryptMode) + if (EnforceEncryption && isAccessMethodChange && !tdeCurrentCreateEvent.encryptMode) { - TDEPrincipalKey *principal_key; - Oid relationId = RangeVarGetRelid(stmt->relation, NoLock, true); - Relation rel = table_open(relationId, lockmode); - - table_close(rel, lockmode); - - LWLockAcquire(tde_lwlock_enc_keys(), LW_SHARED); - principal_key = GetPrincipalKey(MyDatabaseId, LW_SHARED); - LWLockRelease(tde_lwlock_enc_keys()); - if (principal_key == NULL) - { - ereport(ERROR, - (errmsg("failed to retrieve principal key. Create one using pg_tde_set_principal_key before using encrypted tables."))); - - } + ereport(ERROR, + (errmsg("pg_tde.enforce_encryption is ON, only encrypted table access methods are allowed."))); } } #endif diff --git a/contrib/pg_tde/src/pg_tde_guc.c b/contrib/pg_tde/src/pg_tde_guc.c new file mode 100644 index 0000000000000..ce93ef0e26b2d --- /dev/null +++ b/contrib/pg_tde/src/pg_tde_guc.c @@ -0,0 +1,56 @@ + + +#include "pg_tde_guc.h" +#include "postgres.h" +#include "utils/guc.h" + +#ifndef FRONTEND + +bool AllowInheritGlobalProviders = true; +bool EncryptXLog = false; +bool EnforceEncryption = false; + +void +TdeGucInit(void) +{ + DefineCustomBoolVariable("pg_tde.inherit_global_providers", /* name */ + "Allow using global key providers for databases.", /* short_desc */ + NULL, /* long_desc */ + &AllowInheritGlobalProviders, /* value address */ + true, /* boot value */ + PGC_SUSET, /* context */ + 0, /* flags */ + NULL, /* check_hook */ + NULL, /* assign_hook */ + NULL /* show_hook */ + ); + +#ifdef PERCONA_EXT + DefineCustomBoolVariable("pg_tde.wal_encrypt", /* name */ + "Enable/Disable encryption of WAL.", /* short_desc */ + NULL, /* long_desc */ + &EncryptXLog, /* value address */ + false, /* boot value */ + PGC_POSTMASTER, /* context */ + 0, /* flags */ + NULL, /* check_hook */ + NULL, /* assign_hook */ + NULL /* show_hook */ + ); + + DefineCustomBoolVariable("pg_tde.enforce_encryption", /* name */ + "Only allow the creation of encrypted tables.", /* short_desc */ + NULL, /* long_desc */ + &EnforceEncryption, /* value address */ + false, /* boot value */ + PGC_SUSET, /* context */ + 0, /* flags */ + NULL, /* check_hook */ + NULL, /* assign_hook */ + NULL /* show_hook */ + ); + +#endif +} + +#endif