Skip to content

Add temporary file encryption (encrypt_temp_files) - #2

Merged
jdatcmd merged 1 commit into
mainfrom
feature/temp-file-encryption
Jul 7, 2026
Merged

Add temporary file encryption (encrypt_temp_files)#2
jdatcmd merged 1 commit into
mainfrom
feature/temp-file-encryption

Conversation

@jdatcmd

@jdatcmd jdatcmd commented Jul 7, 2026

Copy link
Copy Markdown
Contributor

Summary

Adds temporary file encryption (encrypt_temp_files), which encrypts query-spill temporary files (external sorts, hash joins, and other BufFile spills that exceed work_mem). This closes a documented data-at-rest gap: previously those files were written to disk in plaintext.

Design

Core patch (0002-* in each version series, gated behind USE_TDE_HOOKS): a registrable hook on the temporary-file path. BufFileDumpBuffer passes each buffer through temp_file_encrypt_hook before writing, and BufFileLoadBuffer passes it through temp_file_decrypt_hook after reading, when the new encrypt_temp_files GUC is on. Core implements no cipher. With the flag off, the hooks and GUC do not exist and the tree builds as clean PostgreSQL.

Extension (src/open_pg_tde_tempfile.c): installs the hooks in _PG_init and supplies AES-128-CBC per 8 kB block (IV derived from block position; a partial trailing sub-block is masked with an AES-ECB keystream so ciphertext length equals plaintext length and file offsets are unchanged).

Key model: the key is generated once in the postmaster and inherited by every backend through fork, so parallel workers sharing a temporary file set use the same key. Temporary files never outlive the cluster, so the key is held only in memory and never written to disk. Once the server stops, the on-disk temporary data cannot be recovered, even with access to the storage media. This is a deliberate choice: ephemeral data does not need cross-restart decryption, so keeping zero key material on disk is a security benefit.

Testing

t/temp_file_encryption.pl verified on PostgreSQL 16, 17, and 18 (6 subtests each):

  • With encrypt_temp_files off, a canary string is present in the pgsql_tmp files (control).
  • With it on, the temp files are ciphertext (canary absent).
  • A 200,000-row spilling sort returns correct results either way, proving the encrypt-on-write / decrypt-on-read round trip.

Also verified per version:

  • Gate on builds and the feature works; gate off builds clean PostgreSQL with the encrypt_temp_files symbol absent from the binary.
  • The 0002 patch applies on top of 0001 and is pgindent-clean (0 residual diff). The module and test are pgindent/perltidy-clean.

Usage

shared_preload_libraries = 'open_pg_tde'
encrypt_temp_files = on

Docs

Updates the limitations page (temp files are now encryptable) and adds encrypt_temp_files to the GUC reference.

🤖 Generated with Claude Code

Encrypt query-spill temporary files (external sorts, hash joins, and other
BufFile spills that exceed work_mem), closing a documented data-at-rest gap.

Core patch (0002 in each version series, gated behind USE_TDE_HOOKS): a
registrable hook on the temporary-file path. BufFileDumpBuffer passes each
buffer through temp_file_encrypt_hook before writing, and BufFileLoadBuffer
passes it through temp_file_decrypt_hook after reading, when the new
encrypt_temp_files GUC is on. Core implements no cipher; with the flag off the
hooks and GUC do not exist and the tree builds as clean PostgreSQL.

Extension: open_pg_tde_tempfile.c installs the hooks in _PG_init and supplies
AES-128-CBC per 8 kB block (IV derived from block position, partial tail masked
with an AES-ECB keystream so length is preserved). The key is generated once in
the postmaster and inherited by every backend through fork, so parallel workers
sharing a temporary file set use the same key. Temporary files never outlive
the cluster, so the key is held only in memory and never written to disk; the
on-disk temporary data cannot be recovered once the server stops.

Verified on PostgreSQL 16, 17, and 18: t/temp_file_encryption.pl passes (with
the GUC off a canary is plaintext in pgsql_tmp; with it on the temp files are
ciphertext and a 200k-row spilling sort still returns correct results). Gate
off builds clean with the encrypt_temp_files symbol absent. The 0002 patch
applies on top of 0001 and is pgindent-clean; the module and test are
pgindent/perltidy-clean.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@jdatcmd
jdatcmd merged commit 73ce324 into main Jul 7, 2026
22 of 35 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants