Skip to content

Commit c012cde

Browse files
committed
Update (rewrite) the old wal_keys file format
It does make sense to rewrite old files on server start (otherwise it'll slow down random read/write). But reading all _keys files and checking the magic number could slow the server start, especially if there are a lot of databases with encrypted tables. Moreover, we'd rewrite files once but would have to scan and read them on every start... That's why this commit introduces new suffixes to the filenames in the new format. That way, we would only scan the dir and read file names, instead of opening and reading each _keys file.
1 parent c8770a6 commit c012cde

File tree

3 files changed

+200
-5
lines changed

3 files changed

+200
-5
lines changed

contrib/pg_tde/src/access/pg_tde_xlog_keys.c

Lines changed: 198 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,10 @@
2323
#endif
2424

2525
#define PG_TDE_WAL_KEY_FILE_MAGIC_OLD 0x014B4557 /* old version ID value = WEK 01 */
26+
#define PG_TDE_WAL_KEY_FILE_NAME_OLD "wal_keys"
27+
2628
#define PG_TDE_WAL_KEY_FILE_MAGIC 0x024B4557 /* version ID value = WEK 02 */
27-
#define PG_TDE_WAL_KEY_FILE_NAME "wal_keys"
29+
#define PG_TDE_WAL_KEY_FILE_NAME "wal_keys_v2"
2830

2931
typedef struct WalKeyFileHeader
3032
{
@@ -844,11 +846,34 @@ pg_tde_get_server_key_info(void)
844846
*/
845847
fd = pg_tde_open_wal_key_file_basic(get_wal_key_file_path(), O_RDONLY, true);
846848

847-
/* The file does not exist. */
848-
if (fd < 0)
849-
return NULL;
849+
if (fd >= 0)
850+
pg_tde_wal_key_file_header_read(get_wal_key_file_path(), fd, &fheader, &bytes_read);
851+
else
852+
{
853+
/*
854+
* TODO: An ugly hack for now, we need to get a key info when rewriting
855+
* an old file...
856+
*/
857+
char old_wal_key_file_path[MAXPGPATH] = {0};
858+
859+
join_path_components(old_wal_key_file_path, pg_tde_get_data_dir(), PG_TDE_WAL_KEY_FILE_NAME_OLD);
850860

851-
pg_tde_wal_key_file_header_read(get_wal_key_file_path(), fd, &fheader, &bytes_read);
861+
fd = pg_tde_open_wal_key_file_basic(old_wal_key_file_path, O_RDONLY, true);
862+
863+
/* The file does not exist */
864+
if (fd < 0)
865+
return NULL;
866+
867+
bytes_read = pg_pread(fd, &fheader, sizeof(WalKeyFileHeader), 0);
868+
869+
if (bytes_read > 0 && (bytes_read != sizeof(WalKeyFileHeader)
870+
|| fheader.file_version != PG_TDE_WAL_KEY_FILE_MAGIC_OLD))
871+
{
872+
ereport(FATAL,
873+
errcode_for_file_access(),
874+
errmsg("old WAL key file \"%s\" is corrupted: %m", old_wal_key_file_path));
875+
}
876+
}
852877

853878
CloseTransientFile(fd);
854879

@@ -904,4 +929,172 @@ pg_tde_delete_server_key(void)
904929
/* Remove whole key map file */
905930
durable_unlink(get_wal_key_file_path(), ERROR);
906931
}
932+
933+
/*
934+
* Functions for rewriting old wal_keys into a new format file.
935+
*
936+
* TODO: The old format should be deprecated. And this code should be removed
937+
* eventually.
938+
*/
939+
static int
940+
pg_tde_open_old_wal_key_file_read(const char *filename,
941+
bool ignore_missing,
942+
off_t *curr_pos)
943+
{
944+
int fd;
945+
WalKeyFileHeader fheader;
946+
off_t bytes_read = 0;
947+
948+
Assert(LWLockHeldByMeInMode(tde_lwlock_enc_keys(), LW_SHARED) ||
949+
LWLockHeldByMeInMode(tde_lwlock_enc_keys(), LW_EXCLUSIVE));
950+
951+
fd = pg_tde_open_wal_key_file_basic(filename, O_RDONLY | PG_BINARY, ignore_missing);
952+
if (ignore_missing && fd < 0)
953+
return fd;
954+
955+
bytes_read = pg_pread(fd, &fheader, sizeof(WalKeyFileHeader), 0);
956+
957+
/* File is empty */
958+
if (bytes_read == 0)
959+
return fd;
960+
961+
if (bytes_read != sizeof(WalKeyFileHeader)
962+
|| fheader.file_version != PG_TDE_WAL_KEY_FILE_MAGIC_OLD)
963+
{
964+
ereport(FATAL,
965+
errcode_for_file_access(),
966+
errmsg("old WAL key file \"%s\" is corrupted: %m", filename));
967+
}
968+
*curr_pos = bytes_read;
969+
970+
return fd;
971+
}
972+
973+
static bool
974+
pg_tde_read_one_wal_key_file_old_entry(int fd,
975+
WalKeyFileEntryOld *entry,
976+
off_t *offset)
977+
{
978+
off_t bytes_read = 0;
979+
980+
Assert(entry);
981+
Assert(offset);
982+
983+
bytes_read = pg_pread(fd, entry, sizeof(WalKeyFileEntryOld), *offset);
984+
985+
/* We've reached the end of the file. */
986+
if (bytes_read != sizeof(WalKeyFileEntryOld))
987+
return false;
988+
989+
*offset += bytes_read;
990+
991+
return true;
992+
}
993+
994+
static WalEncryptionRange *
995+
pg_tde_wal_range_from_old_entry(const TDEPrincipalKey *principal_key, WalKeyFileEntryOld *entry)
996+
{
997+
WalEncryptionRange *range = tde_wal_prealloc_range == NULL ? palloc0_object(WalEncryptionRange) : tde_wal_prealloc_range;
998+
999+
tde_wal_prealloc_range = NULL;
1000+
1001+
Assert(principal_key);
1002+
1003+
range->type = entry->range_type;
1004+
range->start = entry->range_start;
1005+
range->end.tli = MaxTimeLineID;
1006+
range->end.lsn = MaxXLogRecPtr;
1007+
1008+
memcpy(range->key.base_iv, entry->key_base_iv, INTERNAL_KEY_IV_LEN);
1009+
if (!AesGcmDecrypt(principal_key->keyData, principal_key->keyLength,
1010+
entry->entry_iv, MAP_ENTRY_IV_SIZE,
1011+
(unsigned char *) entry, offsetof(WalKeyFileEntry, encrypted_key_data),
1012+
entry->encrypted_key_data, INTERNAL_KEY_OLD_LEN,
1013+
range->key.key,
1014+
entry->aead_tag, MAP_ENTRY_AEAD_TAG_SIZE))
1015+
ereport(ERROR,
1016+
errmsg("Failed to decrypt key, incorrect principal key or corrupted key file %u", principal_key->keyLength));
1017+
1018+
return range;
1019+
}
1020+
1021+
void
1022+
pg_tde_update_wal_keys_file(void)
1023+
{
1024+
DIR *dir;
1025+
LWLock *lock_pk = tde_lwlock_enc_keys();
1026+
struct dirent *file;
1027+
TDEPrincipalKey *principal_key = NULL;
1028+
TDESignedPrincipalKeyInfo signed_key_info;
1029+
1030+
/*
1031+
* No real need in lock here as the func should be called only on the server
1032+
* start, but GetPrincipalKey() expects lock.
1033+
*/
1034+
LWLockAcquire(lock_pk, LW_EXCLUSIVE);
1035+
1036+
/*
1037+
* No need to loop through readdir for wal_keys obviously, but it's rather
1038+
* to demo how it'll work for smgr _keys files...
1039+
*/
1040+
dir = opendir(pg_tde_get_data_dir());
1041+
if (dir == NULL && errno != ENOENT)
1042+
elog(ERROR, "could not open directory \"%s\": %m",
1043+
pg_tde_get_data_dir());
1044+
1045+
while (errno = 0, (file = readdir(dir)) != NULL)
1046+
{
1047+
if (strcmp(file->d_name, PG_TDE_WAL_KEY_FILE_NAME_OLD) == 0)
1048+
{
1049+
char wal_key_file_path[MAXPGPATH] = {0};
1050+
off_t read_pos,
1051+
write_pos;
1052+
int old_fd,
1053+
new_fd;
1054+
1055+
join_path_components(wal_key_file_path, pg_tde_get_data_dir(), PG_TDE_WAL_KEY_FILE_NAME_OLD);
1056+
1057+
old_fd = pg_tde_open_old_wal_key_file_read(wal_key_file_path, false, &read_pos);
1058+
1059+
/*
1060+
* The old file exists and it's not empty, hece a principal key
1061+
* should exist as well.
1062+
*/
1063+
if (principal_key == NULL)
1064+
{
1065+
principal_key = GetPrincipalKey(GLOBAL_DATA_TDE_OID, LW_EXCLUSIVE);
1066+
if (principal_key == NULL)
1067+
{
1068+
ereport(ERROR,
1069+
errmsg("could not get server principal key"),
1070+
errdetail("Failed to updated format of WAL keys."));
1071+
}
1072+
pg_tde_sign_principal_key_info(&signed_key_info, principal_key);
1073+
}
1074+
new_fd = pg_tde_open_wal_key_file_write(get_wal_key_file_path(), &signed_key_info, true, &write_pos);
1075+
1076+
while (1)
1077+
{
1078+
WalKeyFileEntryOld old_entry;
1079+
WalKeyFileEntry new_entry;
1080+
WalEncryptionRange *range;
1081+
1082+
if (!pg_tde_read_one_wal_key_file_old_entry(old_fd, &old_entry, &read_pos))
1083+
break;
1084+
1085+
range = pg_tde_wal_range_from_old_entry(principal_key, &old_entry);
1086+
pg_tde_initialize_wal_key_file_entry(&new_entry, principal_key, range);
1087+
pg_tde_write_one_wal_key_file_entry(new_fd, &new_entry, &write_pos, get_wal_key_file_path());
1088+
pfree(range);
1089+
}
1090+
1091+
CloseTransientFile(old_fd);
1092+
CloseTransientFile(new_fd);
1093+
durable_unlink(wal_key_file_path, ERROR);
1094+
}
1095+
}
1096+
1097+
closedir(dir);
1098+
LWLockRelease(lock_pk);
1099+
}
9071100
#endif

contrib/pg_tde/src/include/access/pg_tde_xlog_keys.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,5 +88,6 @@ extern void pg_tde_save_server_key(const TDEPrincipalKey *principal_key, bool wr
8888
extern void pg_tde_save_server_key_redo(const TDESignedPrincipalKeyInfo *signed_key_info);
8989
extern void pg_tde_wal_last_range_set_location(WalLocation loc);
9090
extern void pg_tde_wal_cache_extra_palloc(void);
91+
extern void pg_tde_update_wal_keys_file(void);
9192

9293
#endif /* PG_TDE_XLOG_KEYS_H */

contrib/pg_tde/src/pg_tde.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ tde_shmem_startup(void)
7070
PrincipalKeyShmemInit();
7171
TDEXLogShmemInit();
7272
TDEXLogSmgrInit();
73+
pg_tde_update_wal_keys_file();
7374
TDEXLogSmgrInitWrite(EncryptXLog, TdeKeyLength);
7475

7576
LWLockRelease(AddinShmemInitLock);

0 commit comments

Comments
 (0)