Skip to content

Commit 9d965b6

Browse files
committed
PG-1257 Add functions for principal key removal
Add SQL functions that allow user to remove principal key. * Database level principal key can be removed if there are no encrypted tables or if there is default key. For the first case we just drop key map file completely, for the second we perform key rotation. * Default principal key can be removed if there are no databases that use it. Fix Fix
1 parent 69f7a38 commit 9d965b6

File tree

11 files changed

+427
-5
lines changed

11 files changed

+427
-5
lines changed

contrib/pg_tde/Makefile

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,8 @@ toast_decrypt \
2626
vault_v2_test \
2727
version \
2828
default_principal_key \
29-
create_database
29+
create_database \
30+
delete_principal_key
3031
TAP_TESTS = 1
3132
endif
3233

contrib/pg_tde/expected/default_principal_key.out

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,12 @@ SELECT * FROM test_enc;
146146
(3 rows)
147147

148148
DROP TABLE test_enc;
149+
SELECT pg_tde_delete_default_key();
150+
pg_tde_delete_default_key
151+
---------------------------
152+
153+
(1 row)
154+
149155
DROP EXTENSION pg_tde CASCADE;
150156
DROP EXTENSION pg_buffercache;
151157
DROP DATABASE regress_pg_tde_other;
Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
CREATE EXTENSION IF NOT EXISTS pg_tde;
2+
SELECT pg_tde_add_global_key_provider_file('file-vault','/tmp/pg_tde_test_keyring.per');
3+
pg_tde_add_global_key_provider_file
4+
-------------------------------------
5+
-7
6+
(1 row)
7+
8+
-- Set the local key and delete it without any encrypted tables
9+
-- Should succeed: nothing used the key
10+
SELECT pg_tde_set_key_using_global_key_provider('test-db-key','file-vault');
11+
pg_tde_set_key_using_global_key_provider
12+
------------------------------------------
13+
14+
(1 row)
15+
16+
SELECT key_provider_id, key_provider_name, key_name FROM pg_tde_key_info();
17+
key_provider_id | key_provider_name | key_name
18+
-----------------+-------------------+-------------
19+
-7 | file-vault | test-db-key
20+
(1 row)
21+
22+
SELECT pg_tde_delete_key();
23+
pg_tde_delete_key
24+
-------------------
25+
26+
(1 row)
27+
28+
-- Set local key, encrypt a table, and delete the key
29+
-- Should fail: the is no default key to fallback
30+
SELECT pg_tde_set_key_using_global_key_provider('test-db-key','file-vault');
31+
pg_tde_set_key_using_global_key_provider
32+
------------------------------------------
33+
34+
(1 row)
35+
36+
CREATE TABLE test_table (id int, data text) USING tde_heap;
37+
SELECT pg_tde_delete_key();
38+
ERROR: Cannot delete principal key, there are encrypted tables and default principal key is not set
39+
HINT: Use set_key interface to set the default principal key or decrypt all tables before deleting the principal key
40+
-- Decrypt the table and delete the key
41+
-- Should succeed: there is no more encrypted tables
42+
ALTER TABLE test_table SET ACCESS METHOD heap;
43+
SELECT pg_tde_delete_key();
44+
pg_tde_delete_key
45+
-------------------
46+
47+
(1 row)
48+
49+
-- Set local key, encrypt the table then delete teable and key
50+
-- Should succeed: the table is deleted and there are no more encrypted tables
51+
SELECT pg_tde_set_key_using_global_key_provider('test-db-key','file-vault');
52+
pg_tde_set_key_using_global_key_provider
53+
------------------------------------------
54+
55+
(1 row)
56+
57+
ALTER TABLE test_table SET ACCESS METHOD tde_heap;
58+
DROP TABLE test_table;
59+
SELECT pg_tde_delete_key();
60+
pg_tde_delete_key
61+
-------------------
62+
63+
(1 row)
64+
65+
-- Set default key, set regular key, create table, delete regular key
66+
-- Should succeed: regular key will be rotated to default key
67+
SELECT pg_tde_set_default_key_using_global_key_provider('defalut-key','file-vault');
68+
pg_tde_set_default_key_using_global_key_provider
69+
--------------------------------------------------
70+
71+
(1 row)
72+
73+
SELECT pg_tde_set_key_using_global_key_provider('test-db-key','file-vault');
74+
pg_tde_set_key_using_global_key_provider
75+
------------------------------------------
76+
77+
(1 row)
78+
79+
CREATE TABLE test_table (id int, data text) USING tde_heap;
80+
SELECT pg_tde_delete_key();
81+
pg_tde_delete_key
82+
-------------------
83+
84+
(1 row)
85+
86+
SELECT key_provider_id, key_provider_name, key_name FROM pg_tde_key_info();
87+
key_provider_id | key_provider_name | key_name
88+
-----------------+-------------------+-------------
89+
-7 | file-vault | defalut-key
90+
(1 row)
91+
92+
-- Try to delete key when default key is used
93+
-- Should fail: table already uses the default key, so there is no key to fallback to
94+
SELECT pg_tde_delete_key();
95+
ERROR: Cannot delete principal key, there are encrypted tables and default principal key used for the database
96+
-- Try to delete default key
97+
-- Should fail: default key is used by the table
98+
SELECT pg_tde_delete_default_key();
99+
ERROR: Cannot delete default principal key, it's used by 16384 database
100+
HINT: Use set_key interface to set the database principal key instead of default principal key
101+
-- Set regular principal key, delete default key
102+
-- Should succeed: the table will use the regular key
103+
SELECT pg_tde_set_key_using_global_key_provider('test-db-key','file-vault');
104+
pg_tde_set_key_using_global_key_provider
105+
------------------------------------------
106+
107+
(1 row)
108+
109+
SELECT pg_tde_delete_default_key();
110+
pg_tde_delete_default_key
111+
---------------------------
112+
113+
(1 row)
114+
115+
DROP TABLE test_table;
116+
DROP EXTENSION pg_tde;

contrib/pg_tde/pg_tde--1.0-rc.sql

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -454,6 +454,16 @@ RETURNS VOID
454454
LANGUAGE C
455455
AS 'MODULE_PATHNAME';
456456

457+
CREATE FUNCTION pg_tde_delete_key()
458+
RETURNS VOID
459+
LANGUAGE C
460+
AS 'MODULE_PATHNAME';
461+
462+
CREATE FUNCTION pg_tde_delete_default_key()
463+
RETURNS VOID
464+
LANGUAGE C
465+
AS 'MODULE_PATHNAME';
466+
457467
CREATE FUNCTION pg_tde_key_info()
458468
RETURNS TABLE ( key_name TEXT,
459469
key_provider_name TEXT,

contrib/pg_tde/sql/default_principal_key.sql

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,6 @@ SELECT key_provider_id, key_provider_name, key_name
8181
SELECT pg_buffercache_evict(bufferid) FROM pg_buffercache WHERE relfilenode = (SELECT relfilenode FROM pg_class WHERE oid = 'test_enc'::regclass);
8282

8383
SELECT * FROM test_enc;
84-
8584
DROP TABLE test_enc;
8685

8786
DROP EXTENSION pg_tde CASCADE;
@@ -94,6 +93,7 @@ SELECT * FROM test_enc;
9493

9594
DROP TABLE test_enc;
9695

96+
SELECT pg_tde_delete_default_key();
9797
DROP EXTENSION pg_tde CASCADE;
9898
DROP EXTENSION pg_buffercache;
9999

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
CREATE EXTENSION IF NOT EXISTS pg_tde;
2+
3+
SELECT pg_tde_add_global_key_provider_file('file-vault','/tmp/pg_tde_test_keyring.per');
4+
5+
-- Set the local key and delete it without any encrypted tables
6+
-- Should succeed: nothing used the key
7+
SELECT pg_tde_set_key_using_global_key_provider('test-db-key','file-vault');
8+
SELECT key_provider_id, key_provider_name, key_name FROM pg_tde_key_info();
9+
SELECT pg_tde_delete_key();
10+
11+
-- Set local key, encrypt a table, and delete the key
12+
-- Should fail: the is no default key to fallback
13+
SELECT pg_tde_set_key_using_global_key_provider('test-db-key','file-vault');
14+
CREATE TABLE test_table (id int, data text) USING tde_heap;
15+
SELECT pg_tde_delete_key();
16+
17+
-- Decrypt the table and delete the key
18+
-- Should succeed: there is no more encrypted tables
19+
ALTER TABLE test_table SET ACCESS METHOD heap;
20+
SELECT pg_tde_delete_key();
21+
22+
-- Set local key, encrypt the table then delete teable and key
23+
-- Should succeed: the table is deleted and there are no more encrypted tables
24+
SELECT pg_tde_set_key_using_global_key_provider('test-db-key','file-vault');
25+
ALTER TABLE test_table SET ACCESS METHOD tde_heap;
26+
DROP TABLE test_table;
27+
SELECT pg_tde_delete_key();
28+
29+
-- Set default key, set regular key, create table, delete regular key
30+
-- Should succeed: regular key will be rotated to default key
31+
SELECT pg_tde_set_default_key_using_global_key_provider('defalut-key','file-vault');
32+
SELECT pg_tde_set_key_using_global_key_provider('test-db-key','file-vault');
33+
CREATE TABLE test_table (id int, data text) USING tde_heap;
34+
SELECT pg_tde_delete_key();
35+
SELECT key_provider_id, key_provider_name, key_name FROM pg_tde_key_info();
36+
37+
-- Try to delete key when default key is used
38+
-- Should fail: table already uses the default key, so there is no key to fallback to
39+
SELECT pg_tde_delete_key();
40+
41+
-- Try to delete default key
42+
-- Should fail: default key is used by the table
43+
SELECT pg_tde_delete_default_key();
44+
45+
-- Set regular principal key, delete default key
46+
-- Should succeed: the table will use the regular key
47+
SELECT pg_tde_set_key_using_global_key_provider('test-db-key','file-vault');
48+
SELECT pg_tde_delete_default_key();
49+
50+
DROP TABLE test_table;
51+
DROP EXTENSION pg_tde;

contrib/pg_tde/src/access/pg_tde_tdemap.c

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -544,6 +544,87 @@ pg_tde_perform_rotate_key(TDEPrincipalKey *principal_key, TDEPrincipalKey *new_p
544544
}
545545
}
546546

547+
/*
548+
* Checks if the key map file for the given database has any non-empty entries.
549+
*
550+
* The caller must hold the lock on the files before calling this function.
551+
*/
552+
bool
553+
pg_tde_is_key_map_empty(Oid dbOid)
554+
{
555+
char path[MAXPGPATH];
556+
off_t pos;
557+
int fd;
558+
559+
pg_tde_set_db_file_path(dbOid, path);
560+
561+
fd = pg_tde_open_file_read(path, false, &pos);
562+
563+
while (1)
564+
{
565+
TDEMapEntry map_entry;
566+
567+
if (!pg_tde_read_one_map_entry(fd, &map_entry, &pos))
568+
break;
569+
570+
if (map_entry.type != MAP_ENTRY_EMPTY)
571+
{
572+
close(fd);
573+
return false;
574+
}
575+
}
576+
577+
close(fd);
578+
return true;
579+
}
580+
581+
582+
void
583+
pg_tde_delete_principal_key_redo(Oid dbOid)
584+
{
585+
LWLockAcquire(tde_lwlock_enc_keys(), LW_EXCLUSIVE);
586+
587+
/* This is called during recovery, so no XLog records allowed */
588+
pg_tde_delete_principal_key(dbOid, false);
589+
590+
LWLockRelease(tde_lwlock_enc_keys());
591+
}
592+
593+
/*
594+
* Deletes the principal key for the database. This fucntion checks if key map
595+
* file has any entries, and if not, it removes the file. Otherwise raises an error.
596+
*
597+
* The caller must hold an exclusive lock on the files before calling this function.
598+
*/
599+
void
600+
pg_tde_delete_principal_key(Oid dbOid, bool write_xlog)
601+
{
602+
char path[MAXPGPATH];
603+
604+
pg_tde_set_db_file_path(dbOid, path);
605+
606+
/*
607+
* Check that there aro no local keys in the map file, i.e. nothing is
608+
* encrypted with the principal key.
609+
*/
610+
if (!pg_tde_is_key_map_empty(dbOid))
611+
{
612+
ereport(ERROR,
613+
errcode(ERRCODE_INTERNAL_ERROR),
614+
errmsg("could not delete key, key map file is not empty"));
615+
}
616+
617+
if (write_xlog)
618+
{
619+
XLogBeginInsert();
620+
XLogRegisterData((char *) &dbOid, sizeof(Oid));
621+
XLogInsert(RM_TDERMGR_ID, XLOG_TDE_DELETE_PRINCIPAL_KEY);
622+
}
623+
624+
/* Remove whole key map file */
625+
durable_unlink(path, ERROR);
626+
}
627+
547628
/*
548629
* It's called by seg_write inside crit section so no pallocs, hence
549630
* needs keyfile_path

contrib/pg_tde/src/access/pg_tde_xlog.c

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,12 @@ tdeheap_rmgr_redo(XLogReaderState *record)
6767

6868
xl_tde_perform_rotate_key(xlrec);
6969
}
70+
else if (info == XLOG_TDE_DELETE_PRINCIPAL_KEY)
71+
{
72+
Oid dbOid = *((Oid *) XLogRecGetData(record));
73+
74+
pg_tde_delete_principal_key_redo(dbOid);
75+
}
7076
else if (info == XLOG_TDE_WRITE_KEY_PROVIDER)
7177
{
7278
KeyringProviderRecordInFile *xlrec = (KeyringProviderRecordInFile *) XLogRecGetData(record);

0 commit comments

Comments
 (0)