Repository navigation
Fix secret handling for map-valued connection properties #36740
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
harshach
wants to merge
7
commits into
open-metadata:main
Choose a base branch
from
harshach:agent/secrets-in-map-fields-like-k
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
+1,378
−108
Open
Changes from all commits
Commits
Show all changes
7 commits
Select commit
Hold shift + click to select a range
cdf5e17
fix: encrypt and mask secrets held in map-valued connection properties
harshach fbf2345
fix: keep the retarget guard effective for password-valued maps
harshach a510dd0
test: pin that rotating one secret inside a password map requires re-…
harshach 74c9b61
Fix G004: lazy %-style logging in kafka connection secret resolution
harshach 9f353db
fix: keep password-map entries distinct and empty entries intact in e…
harshach 6a56562
fix: count a changed reference at a password-map entry as an edit
harshach e05b1e1
fix: keep literal secret:-prefixed map values under the DB secrets pr…
harshach File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
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
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
55 changes: 55 additions & 0 deletions
55
...ns/2026-10-30-password-format-on-additionalproperties-makes-a-map-of-secrets.md
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,55 @@ | ||
| # A schema property whose additionalProperties declare format: password is a map of secrets | ||
|
|
||
| - **Status:** Accepted | ||
| - **Revisions:** v1 2026-10-30 (initial) | ||
| - **Deciders:** backend security | ||
| - **Guard:** `PasswordEntityMaskerTest`, `SecretsManagerLifecycleTest`, `KafkaConnectionMapSecretsTest` | ||
| - **Related:** ADR:2026-10-24-a-masked-connection-secret-is-kept-only-while-the-endpoint-is-unchanged, #33477 (secrets inside arrays) | ||
|
|
||
| ## Context | ||
|
|
||
| Map-valued connection properties — `consumerConfig` and `schemaRegistryConfig` on Kafka and | ||
| Redpanda, `dbtHttpHeaders` on a dbt HTTP pipeline, `headers` on a CustomLLM service — are the only | ||
| place for credentials such as `sasl.oauthbearer.client.secret`, `ssl.key.password` or a GitLab | ||
| `PRIVATE-TOKEN`. The secret framework had no notion of a map of secrets: `PasswordAnnotator` only | ||
| marked properties that are themselves `format: password`, and the walkers in `SecretsManager` and | ||
| `PasswordEntityMasker` had no branch for `java.util.Map`, so map entries were stored unencrypted, | ||
| never reached an external secrets manager, and were returned in clear to any authenticated user | ||
| (ANT-2026-INNF1N4V). #33477 closed the same gap for secrets inside arrays but not inside maps. | ||
|
|
||
| ## Decision | ||
|
|
||
| - A JSON-schema property whose `additionalProperties` declare `format: password` is a **map of | ||
| secrets**. `PasswordAnnotator` puts `@PasswordField` on the generated holder class's | ||
| `getAdditionalProperties()`, and every walker handles the map entry by entry. | ||
| - Every **string** entry is masked on read, encrypted at rest, and — with an external secrets | ||
| manager — stored as its own secret. Non-string entries (`session.timeout.ms: 6000`) keep their | ||
| type and are neither encrypted nor masked: the maps also carry numbers and booleans that are not | ||
| secrets. | ||
| - Each entry's secret id is `<entity secret path>/<field>/additionalproperties/<entry key>`, | ||
| lowercased and cleaned by the provider's id rules (dots become `_` under the default | ||
| configuration), and mask/unmask keys include the entry key, so a `*********` sent back by a | ||
| client is restored for that exact entry and `deleteSecrets` derives the same id. | ||
| - Ingestion clients resolve `secret:` references inside such maps themselves; the server never | ||
| fetches secrets. String-valued maps generate as `dict[str, CustomSecretStr]` and unwrap through | ||
| `get_secret_value()` (dbt); untyped maps stay `dict[str, Any]` and the connector resolves | ||
| references before use (`messaging/kafka/connection.py`). | ||
| - The mask is a **blanket**: harmless settings in the map (`security.protocol`, `group.id`) show | ||
| as `*********` too, because JSON Schema cannot mark individual keys of a free-form map. This is | ||
| the accepted cost of declaring the map at all; the alternative (typed fields for every possible | ||
| key) does not exist for maps like `dbtHttpHeaders`. | ||
|
|
||
| ## Consequences | ||
|
|
||
| - `connectionOptions`/`connectionArguments`, Trino `proxies` and NATS `additionalConfig` are | ||
| deliberately **not** declared password maps: doing so would mask every option they carry, not | ||
| only credentials. Declaring them needs a per-case decision. | ||
| - `ConnectionSecretsGuard` compares masked configs, and a masked map hides edits to non-secret | ||
| entries (a token endpoint URL) — so the guard treats a typed value at a masked map entry as a | ||
| retarget; see ADR:2026-10-24-a-masked-connection-secret-is-kept-only-while-the-endpoint-is-unchanged. | ||
| Because a rotated map secret looks exactly like a retargeted setting, rotating one entry | ||
| requires re-entering every secret of the connection. | ||
| - Rows saved before this record keep their map values in plaintext until the service or pipeline | ||
| is saved again; credentials already stored this way should be rotated. | ||
| - Revisit if a per-key secret declaration (typed fields for known keys, refused in the map) is | ||
| wanted for the Kafka maps, where masking harmless settings is the visible cost. |
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
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
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
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
Oops, something went wrong.
Oops, something went wrong.
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.
Uh oh!
There was an error while loading. Please reload this page.