Conduktor offers crypto shredding: a cost-efficient and scalable solution for securely deleting encryption keys.Keys are centrally managed and securely stored, which ensures that deleting a key instantly makes all associated data unreadable, without the complexity of self-managed vaults, expensive per-user encryption or additional infrastructure overhead.
Crypto shredding is like throwing away the only key to a locked safe. Without the key, the contents are inaccessible forever.It’s the process of making encrypted data permanently unreadable by securely deleting the encryption keys.
Conduktor’s crypto shredding solution allows you to honor deletion requests and maintain compliance with regulations like GDPR (General Data Protection Regulation) and CCPA (California Consumer Privacy Act).It’s particularly valuable for organizations that need to operate at scale across large user bases or high-volume data sets, offering both substantial cost savings and improved performance compared to managing individual keys directly in an external KMS such as AWS.
Gateway can encrypt a combination of the record key, the record headers and the record value.
It can encrypt the entire key and record value, or individual fields.
It does this using an encryption key with an ID derived from a property of each record (e.g., a field or key value that uniquely identifies the associated user)
This encryption key is called a DEK (short for Data Encryption Key) and is how we will refer to it for the rest of the tutorial.
Set up a local Gateway to encrypt sensitive user data sent to a Kafka topic, using the Gateway KMS.
Decrypt data read from the same Kafka topic via Gateway (in order to demonstrate normal working conditions).
Apply crypto shredding to an EDEK so that associated data can no longer be decrypted by Gateway.
We’ll configure Gateway to encrypt sensitive customer data (password, visa) using a DEK derived from a unique customer identifier (userId).Other data (e.g., name) will be left unencrypted. All of the data will come from the same simple json payload. For example, we might expect to see
The following Docker Compose file will setup Kafka, the Conduktor platform and a Vault KMS instance to work together.It will then create a single Kafka topic called customers that the rest of the tutorial will instruct you how to make use of for the encryption and crypto shredding use case.
We’ll use the following encryption Interceptor configuration for this use case. Note that:
Encryption will only be applied to the customers topic (already automatically created).
Gateway will use (and create if necessary) a single Vault KMS key called /transit/keys/master-key as the KEK.
Each record will associate with an Encryption Keys Store Entry derived from the record value userId (as templated in the configuration).
Each Encryption Keys Store Entry will have a DEK generated by Gateway and stored as an EDEK generated by the KMS using the KEK.
Each DEK will be used by Gateway to encrypt the password and visa fields. For example, the sample JSON from earlier would have an Encryption Keys Store Entry with id sec-12345678 and the associated DEK would be used to encrypt both the password field admin123 and the visa field 4111111145551142.
This configuration requires raw JSON, AVRO or Protobuf record values to be sent. We’ll send JSON.
The Interceptor configuration can be applied to Gateway via the Conduktor CLI or Console UI. Choose your preferred format:
To deploy this Interceptor configuration, paste it into a file called encryption-interceptor.yaml in the same directory as your docker-compose.yaml and run the following command:
With encryption configured, we can now produce records via Gateway that will be encrypted at rest on disk in the backing Kafka cluster (and when re-reading via Kafka or Gateway).Here’s a high-level visualization of the produce process:Choose the Conduktor CLI or Console UI to produce and check the records for yourself:
Conduktor CLI
Console UI
Copy
Ask AI
# 1. Produce a record with '101' to topic 'customers' (we'll crypto shred this later)echo '{ "userId" : 101, "name" : "Joe Smith", "password" : "admin123", "visa" : 4111111145551142 }' | \docker compose exec -T kafka-client \ kafka-console-producer --bootstrap-server conduktor-gateway:6969 \ --topic customers# 2. Produce a record with '102' to topic 'customers' (we'll compare this to our crypto shredded record later)echo '{ "userId" : 102, "name" : "Mary Brown", "password" : "abc123", "visa" : 4111111111111111 }' | \docker compose exec -T kafka-client \ kafka-console-producer --bootstrap-server conduktor-gateway:6969 \ --topic customers# 3. Consume all the records in the 'customers' topic (use command 'ctrl-c' to exit)docker compose exec kafka-client kafka-console-consumer \ --bootstrap-server conduktor-gateway:6969 --topic customers --from-beginning
In the customers topic detail page open the Produce tab.
You should see a final output for the customer data stored in Kafka similar to below.Both password and visa are encrypted as configured, while other fields (e.g., name) remain unencrypted.
Gateway creates keys on demand, so now that we’ve produced two messages with different secretIds (sec-101 and sec-102), we’d expect to see the associated EDEKs persisted in a keystore.Because we are using the Gateway KMS we would also expect the two EDEKs to be persisted in a Kafka topic, so that we can later crypto shred them.The Vault KMS should only contain the master key (/transit/keys/master-key) which was used to encrypt each EDEK.The following diagram visualizes how the produce process works, including the caches which prevent either KMS being overwhelmed by high traffic.Pick the preferred option for inspecting the Vault tokens.
Conduktor CLI
Console UI
The following curl command should return master-key:
Copy
Ask AI
curl \ --header "X-Vault-Token: vault-plaintext-root-token" \ --request LIST \ http://localhost:8200/v1/transit/keys/
The following command should return exactly two EDEKs from the _conduktor_gateway_encryption_keys topic that Gateway uses as its Encryption Keys Store.
There are two records (one for each EDEK) that match the two keyIds.As the number of records with different keyIds grows, this can lead to substantial cost savings and improved performance compared to storing the same keys in the KMS.The record Key is composed of:
algorithm: hardcoded in the configuration and then used to generate the associated DEK in this record
keyId: templated in the configuration and evaluated using data from the associated record (the userId field)
uuid: uniquely generated per inserted record
The record Value is composed of:
edek: the DEK generated by Gateway and encrypted using the Gateway KMS.
The uuid field is necessary because it’s possible for two different Gateway nodes to process a record with the same userId for the first time at the same time.In such a scenario two Encryption Keys Store records with the same keyId but a different uuid would be created (thus avoiding a race condition).This means that it’s possible (although relatively rare) for the same keyId to have multiple EDEKs (i.e., multiple EDEKs to be assigned to the same user).
Make a copy of the UUID returned for "keyId":"gateway-kms://sec-101" as we’ll need it for crypto shredding the associated EDEK in step 7: Crypto shredding.
To deploy this Interceptor configuration, paste the yaml into a file called decryption-interceptor.yaml in the same directory as your docker-compose.yaml and run the following command:
Type Decrypt into the search bar and select Decrypt on consume any record fields encrypted by the gateway or the entire payload (the only available Interceptor).
Paste the JSON above into the text box (replacing the existing JSON) and click Deploy Interceptor at the bottom of the screen.
You should now be back on the Kafka Gateway page with two Interceptors configured: sensitive customer data encryption and the newly created sensitive customer data decryption.
With a decryption Interceptor configured on consumption (from the same topic we’ve been encrypting data), it’s now possible to decrypt and read the records.Here’s a diagram visualizing how the consume process works for our use case, including the caches which prevent either KMS being overwhelmed by high traffic:Pick the method for decrypting and consuming the records:
Although we now produce and consume un-encrypted data, the password and visa fields are still encrypted at rest (on disk in the Kafka broker).If you delete the decryption Interceptor, all of the fields would go back to being encrypted when consumed.
Records encrypted with Gateway KMS can have their EDEKs crypto shredded by tombstoning the associated record in the Encryption Keys Store topic.The same keyId can have multiple record entries, so it’s important to tombstone every record sharing the same keyId (each will have a unique uuid).We currently don’t offer an automated solution for this process; you have to read the entire topic manually and find every record with the keyId which needs tombstoning.For this tutorial, we only published two records and both had a different keyId, so we can be confident that only one record needs tombstoning.
Crypto shred using the CLI
Crypto shred using Console UI
Copy
Ask AI
# 1. Produce a record that tombstones the EDEK associated with # 'keyId': 'gateway-kms://sec-101'echo '{"algorithm":"AES128_GCM","keyId":"gateway-kms://sec-101","uuid":"<UUID_FOR_101_FROM_STEP_4>"}|NULL' | \ docker compose exec -T kafka-client \ kafka-console-producer --bootstrap-server conduktor-gateway:6969 \ --topic _conduktor_gateway_encryption_keys \ --property "parse.key=true" \ --property "key.separator=|" \ --property "null.marker=NULL"# 2. Consume all the records in the 'Encryption Keys Store' topic # (use command 'ctrl-c' to exit)docker compose exec kafka-client kafka-console-consumer \ --bootstrap-server conduktor-gateway:6969 --topic _conduktor_gateway_encryption_keys \ --from-beginning --property print.key=true --property key.separator="|"
In Console, go to Topics*. You should see all the topics, including Gateway’s internal configuration ones.
Type encryption into the search bar next to the Internal topics selector.
Click on the _conduktor_gateway_encryption_keys topic.
Find the record that includes "keyId":"gateway-kms://sec-101" and open it.
Click Reprocess message.
As the Target topic select _conduktor_gateway_encryption_keys.
Click Edit and publish. You should be redirected to a produce window with all of the message already filled out in the table.
The Value table entry contains the existing EDEK. Select Null from the drop-down text above it, so that the EDEK filled text box is removed from the screen.
Click Produce. The produced message with a now null value will be shown on the right-hand side.
Records in the Encryption Keys Store topic should now show the latest record value for the EDEK that we’ve crypto shredded as null (or empty, if consuming via the CLI).Gateway is now unable to decrypt data associated with it.To verify this, repeat step 4: Inspect the created keys.You should see something like the following (if using the UI, the order will be in reverse):
The latest record value for "keyId":"gateway-kms://sec-101" (or first entry if using the UI) should be null. This indicates that it’s been successfully crypto shredded.
There are two entries for "keyId":"gateway-kms://sec-101" because Kafka compaction isn’t instantaneous.Gateway guarantees to use the latest record and to prevent decryption if the associated EDEK has been shredded.However, the earlier record still exists on disk and its EDEK is available to anyone who consumes the Kafka topic directly.This means that it is technically possible for someone with access to the topic and the KEK to recover crypto shredded data. The key should eventually be deleted because the topic is configured for compaction, but there’s no guarantee of when that will occur.Reducing the time until encrypted encryption keys are deleted from disk is possible but beyond the scope of this tutorial.
8. Verify crypto shredded record no longer decrypts
Once an EDEK has been crypto shredded, Gateway will pass any associated records directly to consumers without attempting to decrypt them.The following diagram visualizes how the consume process works for our use case.
The record userId: 101 is associated with the crypto shredded keyId: sec-102 and is no longer decryptable.
The record userId: 102 is associated with keyId: sec-101 and is decryptable.
Gateway crypto shredding only applies to historical records.If a new record was produced for userId: 101 at this point, then a new EDEK would be created (with a new UUID that doesn’t conflict) and the new record would be decryptable.Historical records will remain crypto shredded.