Support encrypted table operations and server-side purge#5060
Conversation
dimas-b
left a comment
There was a problem hiding this comment.
Thanks for your contribution, @hkwi !
Given that it's a major change, would you mind starting a corresponding discussion on the dev ML for visibility?
This is just a preliminary review from my side, but in general the PR looks pretty solid.
| } | ||
|
|
||
| // We diverge from `BaseMetastoreTableOperations` in the below code block | ||
| if (null == existingLocation) { |
There was a problem hiding this comment.
Thanks for flagging this. I merged the current main, including #5057, without force-pushing. The conflict was in doCommit. I kept the MetadataWriteResult/writeSucceeded cleanup flow from #5057 unchanged and removed the writer-side encryption changes from that path. The remaining LocalIcebergCatalog change only preserves encryption context for the server-side purge task, so the two changes no longer overlap semantically.
|
Thanks @hkwi for working on it. The scope sounds good to me. |
| closeableGroup.addCloseable(metricsReporter()); | ||
| if (properties.containsKey(CatalogProperties.ENCRYPTION_KMS_TYPE) | ||
| || properties.containsKey(CatalogProperties.ENCRYPTION_KMS_IMPL)) { | ||
| keyManagementClient = EncryptionUtil.createKmsClient(properties); |
There was a problem hiding this comment.
I think we should document how the KMS client obtains credentials for the KMS store, especially when multiple KMS stores are used. Not a blocker, though. Either a follow up or documenting it in this PR would be nice.
There was a problem hiding this comment.
Thanks. I added a code comment at the dynamic KMS load site explaining that this follows the same structural pattern as CatalogUtil.loadFileIO: the configured KeyManagementClient is instantiated from trusted catalog properties, and credential resolution is delegated to that implementation. The cleanup task keeps those catalog KMS properties and the wrapped table keys in a task-only envelope rather than merging them into FileIO/storage properties. If a custom client selects among multiple KMS stores, that selection and credential handling remain the responsibility of that KeyManagementClient. Broader deployment documentation can follow separately.
|
|
||
| String newLocation = writeNewMetadataIfRequired(base == null, metadata); | ||
| // SnapshotProducer may already have used this manager to generate and register encryption | ||
| // keys while writing encrypted manifests. Recreating it here would discard those keys |
There was a problem hiding this comment.
Polaris Servers never write manifests, IIRC... or did I miss it?
There was a problem hiding this comment.
You are right: Polaris should not write manifests. I removed the writer-side EncryptionManager/FileIO initialization and the doCommit key propagation from LocalIcebergCatalog. The only server-side KMS path left is in cleanup tasks, where Polaris reads and decrypts manifest lists and manifests previously written by the engine so that purge can enumerate files to delete. I also added a comment at task creation to make this ownership boundary explicit.
|
|
||
| @Override | ||
| public EncryptionManager encryption() { | ||
| return encryption(currentMetadata); |
There was a problem hiding this comment.
Why is this override necessary? IIRC, Polaris only writes metadata files, which are not encrypted? 🤔
There was a problem hiding this comment.
Agreed. That override was only supporting writer-side encryption and is no longer necessary, so I removed it along with the writer-side encryption manager. Polaris continues to write the plaintext metadata JSON using the normal FileIO. A separate encryption-aware FileIO is reconstructed only by cleanup tasks to read encrypted manifest lists and manifests during server-side purge.
| .withProperty(TableProperties.FORMAT_VERSION, "3") | ||
| .withProperty(TableProperties.ENCRYPTION_TABLE_KEY, PolarisTestKms.MASTER_KEY_NAME) | ||
| .createTransaction(); | ||
| transaction.table().newFastAppend().appendFile(FILE_A).commit(); |
There was a problem hiding this comment.
This test appears to require more from LocalIcebergCatalog than what is necessary for usual Polaris operation.
Would it be possible to convert this to an integration test similar to RestCatalogRustFSSpecialIT?
Encrypted writes would normally be performed by the "engine" (represented by test) code, which Polaris should only handle purges. WDYT?
There was a problem hiding this comment.
Agreed. I replaced the LocalIcebergCatalog test with a REST integration test in RestCatalogFileIntegrationTest. The test-side client/engine writes encrypted data, manifest lists, and manifests, verifies their raw AGS1 headers, commits through REST, requests server-side purge, and verifies deletion. The Polaris production path only handles the purge. Because the corresponding Iceberg REST client encryption support in apache/iceberg#13225 is still pending, the test uses a test-only TableOperations adapter to model the engine-side behavior.
…server-purge-squashed # Conflicts: # runtime/service/src/main/java/org/apache/polaris/service/catalog/iceberg/LocalIcebergCatalog.java
|
Thanks for the preliminary review. I pushed an update that merges the current main and addresses the inline feedback: writer-side encryption was removed from LocalIcebergCatalog, the remaining KMS use is scoped to reading encrypted manifest lists/manifests during server-side purge, and the test was moved to a REST integration test where the test-side engine performs encrypted writes. Validation completed: format and compileAll passed; PolarisEncryptionUtilTest, TaskFileIOSupplierTest, and RestCatalogFileIntegrationTest.encryptedTableWrittenByClientCanBePurgedByServer passed. I will handle the requested dev mailing-list discussion separately; no ML post has been made yet. |
Summary
This draft adds Polaris-side Iceberg table encryption support for table operations and asynchronous server-side purge.
KeyManagementClientfrom catalog properties.EncryptionManagerandEncryptingFileIOfor encrypted tables.Related to #2829, which tracks the broader Iceberg encryption support discussion.
Motivation and scope
An Iceberg client can write an encrypted table, but Polaris also executes Iceberg I/O in server-side paths such as purge. Those paths need an encryption manager capable of reading encrypted manifest metadata; otherwise the existing cleanup handlers cannot enumerate and delete all files in an encrypted table.
This change uses Iceberg's existing catalog-property contract (
encryption.kms-type/encryption.kms-impland implementation-specific properties). The Polaris server and an Iceberg client may use the same KMS implementation and settings, but they are configured independently and only need to resolve the same table key ID. An out-of-treeKeyManagementClientcan be used when its implementation is present on the Polaris runtime classpath.This draft does not vend KMS configuration or credentials through the REST Catalog API, define key rotation, or attempt to cover all possible REST semantics for encrypted tables.
Implementation notes
Tests
The targeted tests pass for both NoSQL in-memory and relational catalog implementations, covering:
Commands run:
This PR does not enable or change coverage tooling.
Checklist
CHANGELOG.md(if needed; deferred while this is a draft)site/content/in-dev/unreleased(if needed; deferred while this is a draft)