Add a gRPC interceptor that seals and opens payloads - #267
Open
pseudomuto wants to merge 1 commit into
Open
Conversation
The vault knows how to seal and open payloads, but nothing sat in the request path to use it. This adds a client interceptor that does: it seals the payloads of every outbound request and opens the ones on every inbound response, including the payloads carried in gRPC error details. Search attributes are skipped in both directions, since the server indexes and queries them and a sealed one would be useless. Enabled and Vault are separate knobs. A vault without Enabled only opens, so switching encryption off does not strand data that was sealed while it was on, and payloads that were never sealed pass through untouched so mixed traffic is fine. Only sealing needs a namespace: a sealed message names the KEK that wrapped its DEK, so the two directions resolve it differently. NamespaceKey says where on the request context to find the namespace whose key should seal a payload. Nothing stamps that value yet, so an enabled Encryptor currently fails any request it would have to seal. That is deliberate. The namespace picks the KEK, and defaulting it would encrypt a tenant's data under a key that is not theirs, which is the quiet version of the failure this is meant to prevent. Reading the namespace off the incoming request comes next. Practically, this would fallback to the default key, but that's an implementation detail of the Vault rather than something outlined here. NB: This isn't wired up yet. The payload visitor is unary only, so replication streams are not covered by it.
pseudomuto
force-pushed
the
encryption-interceptor
branch
from
September 1, 2026 14:10
6bc0a97 to
8214ed7
Compare
hehaifengcn
reviewed
Sep 2, 2026
| } | ||
|
|
||
| unary, err := proxy.NewPayloadVisitorInterceptor(proxy.PayloadVisitorInterceptorOptions{ | ||
| Inbound: visitPayloads(in, codec.Chain.Decode, noNamespace), |
Collaborator
There was a problem hiding this comment.
so incoming payload to proxy is encrypted? what does noNamespace mean here?
| // Required when Enabled and unused otherwise, since only sealing needs a | ||
| // namespace. There is no default: the namespace picks the KEK, so a | ||
| // guess means encrypting a tenant's data under a key that is not theirs. | ||
| NamespaceKey any |
|
|
||
| return &proxy.VisitPayloadsOptions{ | ||
| ConcurrencyLimit: runtime.NumCPU(), | ||
| SkipSearchAttributes: true, |
Collaborator
There was a problem hiding this comment.
what's the implication to skip SA? does it mean SA value won't be encrypted?
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The vault knows how to seal and open payloads, but nothing in the request path used it. This adds a client interceptor that seals the payloads of every outbound request and opens the payloads of every inbound response, including those carried in gRPC error details. Search attributes are skipped in both directions because the server indexes and queries them, and a sealed one would be useless.
Enabled and Vault are separate knobs. A vault without Enabled only opens, so switching encryption off does not strand data that was sealed while it was on, and payloads that were never sealed pass through untouched so mixed traffic is fine. Only sealing needs a namespace: a sealed message names the KEK that wrapped its DEK, so the two directions resolve it differently.
NamespaceKey says where on the request context to find the namespace whose key should seal a payload. Nothing stamps that value yet, so an enabled Encryptor currently fails any request it would have to seal. That is deliberate. The namespace picks the KEK, and defaulting it would encrypt a tenant's data under a key that is not theirs, which is the quiet version of the failure this is meant to prevent. Reading the namespace off the incoming request comes next. Practically, this would fall back to the default key, but that's an implementation detail of the Vault rather than something outlined here.
NB: This isn't wired up yet. The payload visitor is unary only, so replication streams are not covered by it.