Skip to content

Commit

Permalink
PG-1310: Allow users to enforce encyptioon
Browse files Browse the repository at this point in the history
This commmit mainly adds a new GUC variable,`pg_tde.enforce_encryption
= ON/OFF`. This can only be changed by superusers, and if it is ON,
`CREATE TABLE` and similar statements only allow users to use the
`tde_heap` access method. Similarly, `ALTER TABLE... SET ACCESS METHOD`
only allows the use of `tde_heap`, but other ALTER statements are
allowed on existing non encrypted tables.

The commit also refactores the GUC variables into a single file, and
extends the event trigger to handle the "alternative" `CREATE TABLE`
statements:

* `CREATE TABLE AS`
* `SELECT INTO`
* `CREATE MATERIALIZED VIEW`

Previously, these resulted in the creation of non encrypted files
even with the `tde_heap` access method.
  • Loading branch information
dutow committed Feb 3, 2025
1 parent b78d1af commit 5cc49b1
Show file tree
Hide file tree
Showing 11 changed files with 227 additions and 83 deletions.
1 change: 1 addition & 0 deletions contrib/pg_tde/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -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 \
Expand Down
24 changes: 24 additions & 0 deletions contrib/pg_tde/expected/change_access_method.out
Original file line number Diff line number Diff line change
Expand Up @@ -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;
27 changes: 27 additions & 0 deletions contrib/pg_tde/expected/change_access_method_basic.out
Original file line number Diff line number Diff line change
Expand Up @@ -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;
3 changes: 2 additions & 1 deletion contrib/pg_tde/meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -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/')
Expand Down
29 changes: 29 additions & 0 deletions contrib/pg_tde/sql/change_access_method.inc
Original file line number Diff line number Diff line change
Expand Up @@ -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;
19 changes: 2 additions & 17 deletions contrib/pg_tde/src/access/pg_tde_xlog_encrypt.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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)
{
Expand Down
19 changes: 1 addition & 18 deletions contrib/pg_tde/src/catalog/tde_principal_key.c
Original file line number Diff line number Diff line change
Expand Up @@ -40,11 +40,10 @@
#else
#include "pg_tde_fe.h"
#endif
#include "pg_tde_guc.h"

#include <sys/time.h>

bool AllowInheritGlobalProviders = true;

#ifndef FRONTEND

typedef struct TdePrincipalKeySharedState
Expand Down Expand Up @@ -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)
{
Expand Down
26 changes: 26 additions & 0 deletions contrib/pg_tde/src/include/pg_tde_guc.h
Original file line number Diff line number Diff line change
@@ -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 */
7 changes: 3 additions & 4 deletions contrib/pg_tde/src/pg_tde.c
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
#include "catalog/tde_global_space.h"
#include "utils/percona.h"
#endif
#include "pg_tde_guc.h"

#include <sys/stat.h>

Expand Down Expand Up @@ -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;
Expand Down
99 changes: 56 additions & 43 deletions contrib/pg_tde/src/pg_tde_event_capture.c
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -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.
Expand Down Expand Up @@ -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
Expand Down
Loading

0 comments on commit 5cc49b1

Please sign in to comment.