Skip to content

Commit 80e0bb0

Browse files
committed
PG-1867 Make pg_tde_restore_encrypt re-use old keys
Unfortunately the logic for generating a new key to protect the stream cipher used to encrypt the WAL stream in our restore command was based on totally incorrect assumptions due to how the recovery is implemented. Recovery is a state machine which can go back and forward between one mode where it streams from a primary and another where it first tries to fetch WAL from the archive and if that fails from the pg_wal directory, and in the pg_wal directory we may have files which are encrypted with whatever keys were there originally. To handle all the possible scenarios we remove the ability of pg_tde_restore_encrypt to generate new keys and just has it use whatever keys there are in the key file. This unfortunately means we open ourselves to some attacks on the stream cipher if the system is tricked into encrypting a different WAL stream at the same TLI and LSN as we already have encrypted. As far as I know this should be rare under normal operations since normally e.g. the WAL should be the same in the archive as the one in pg_wal or which we receive through streaming. Ideally we would want to fix this but for now it is better to have WAL encryption with this weakness than to not have it at all. This also incidentally fixes a bug we discovered caused by generating a new key only invalidating one key rather than all keys which should have become invalid, since we no longer generate a new key.
1 parent 1338ceb commit 80e0bb0

File tree

4 files changed

+28
-11
lines changed

4 files changed

+28
-11
lines changed

contrib/pg_tde/src/access/pg_tde_xlog_smgr.c

Lines changed: 23 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -266,16 +266,33 @@ TDEXLogSmgrInitWrite(bool encrypt_xlog)
266266
pfree(key);
267267
}
268268

269+
/*
270+
* Used by pg_tde_restore_encrypt to simulate being constantly in recovery
271+
* since the command does not have access to any information about if we are in
272+
* recovery or not.
273+
*
274+
* Creates a dummy key which points at the very end of the WAL stream.
275+
*/
269276
void
270-
TDEXLogSmgrInitWriteReuseKey()
277+
TDEXLogSmgrInitWriteOldKeys()
271278
{
272-
WalEncryptionKey *key = pg_tde_read_last_wal_key();
279+
WALKeyCacheRec *keys;
280+
WalEncryptionKey dummy = {
281+
.type = WAL_KEY_TYPE_UNENCRYPTED,
282+
.wal_start = {.tli = -1,.lsn = -1}
283+
};
273284

274-
if (key)
285+
EncryptionKey = dummy;
286+
TDEXLogSetEncKeyLocation(dummy.wal_start);
287+
288+
keys = pg_tde_get_wal_cache_keys();
289+
290+
if (keys == NULL)
275291
{
276-
EncryptionKey = *key;
277-
TDEXLogSetEncKeyLocation(EncryptionKey.wal_start);
278-
pfree(key);
292+
WalLocation start = {.tli = 1,.lsn = 0};
293+
294+
/* cache is empty, prefetch keys from disk */
295+
pg_tde_fetch_wal_keys(start);
279296
}
280297
}
281298

contrib/pg_tde/src/bin/pg_tde_restore_encrypt.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ write_encrypted_segment(const char *segpath, const char *segname, const char *tm
6868

6969
XLogFromFileName(segname, &tli, &segno, walsegsz);
7070

71-
TDEXLogSmgrInitWriteReuseKey();
71+
TDEXLogSmgrInitWriteOldKeys();
7272

7373
w = xlog_smgr->seg_write(segfd, buf.data, r, pos, tli, segno, walsegsz);
7474

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ extern Size TDEXLogEncryptStateSize(void);
1111
extern void TDEXLogShmemInit(void);
1212
extern void TDEXLogSmgrInit(void);
1313
extern void TDEXLogSmgrInitWrite(bool encrypt_xlog);
14-
extern void TDEXLogSmgrInitWriteReuseKey(void);
14+
extern void TDEXLogSmgrInitWriteOldKeys(void);
1515

1616
extern void TDEXLogCryptBuffer(const void *buf, void *out_buf, size_t count, off_t offset,
1717
TimeLineID tli, XLogSegNo segno, int segSize);

contrib/pg_tde/t/wal_archiving.pl

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@
5858
unlike(
5959
`strings $data_dir/pg_wal/0000000100000000000000??`,
6060
qr/foobar_enc/,
61-
'should not find foobar_enc in WAL');
61+
'should not find foobar_enc in WAL since it is encrypted');
6262

6363
$primary->stop;
6464

@@ -84,10 +84,10 @@
8484

8585
$data_dir = $replica->data_dir;
8686

87-
unlike(
87+
like(
8888
`strings $data_dir/pg_wal/0000000100000000000000??`,
8989
qr/foobar_plain/,
90-
'should not find foobar_plain in WAL since it is encrypted');
90+
'should find foobar_plain in WAL since we use the same key file');
9191
unlike(
9292
`strings $data_dir/pg_wal/0000000100000000000000??`,
9393
qr/foobar_enc/,

0 commit comments

Comments
 (0)