Skip to content

CNTRLPLANE-3237: Introduce getter for KMS SecretData#2260

Merged
openshift-merge-bot[bot] merged 1 commit into
openshift:masterfrom
ardaguclu:secret-data-getter
Jun 2, 2026
Merged

CNTRLPLANE-3237: Introduce getter for KMS SecretData#2260
openshift-merge-bot[bot] merged 1 commit into
openshift:masterfrom
ardaguclu:secret-data-getter

Conversation

@ardaguclu
Copy link
Copy Markdown
Member

@ardaguclu ardaguclu commented Jun 2, 2026

This PR introduces getter functionality, so that plugin lifecycle can safely query data in #2252

Summary by CodeRabbit

Release Notes

  • Refactor
    • Improved internal architecture for KMS secret data handling with enhanced accessor patterns for better encapsulation.
    • Strengthened encryption validation testing to ensure robust secret configuration verification.

@openshift-ci-robot openshift-ci-robot added the jira/valid-reference Indicates that this PR references a valid Jira ticket of any type. label Jun 2, 2026
@openshift-ci-robot
Copy link
Copy Markdown

openshift-ci-robot commented Jun 2, 2026

@ardaguclu: This pull request references CNTRLPLANE-3237 which is a valid jira issue.

Warning: The referenced jira issue has an invalid target version for the target branch this PR targets: expected the story to target the "5.0.0" version, but no target version was set.

Details

In response to this:

This PR introduces getter functionality, so that plugin lifecycle can safely query data in #2252

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the openshift-eng/jira-lifecycle-plugin repository.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Jun 2, 2026

Walkthrough

KMSPluginsSecretData and KMSSecretData types are refactored to provide encapsulated read access through new Get() accessor methods instead of exposing map fields directly. Internal storage becomes unexported, and all call sites throughout config implementation and tests are updated to use the new accessors with proper nil-safety handling.

Changes

KMS Secret Data Encapsulation

Layer / File(s) Summary
Accessor methods for secret data types
pkg/operator/encryption/encryptiondata/config.go, pkg/operator/encryption/state/types.go
New Get(keyID) method on KMSPluginsSecretData and Get(secretName, dataKey) method on KMSSecretData provide safe read access with nil-safety and missing-key handling; ByKeyID field is made unexported as byKeyID.
Config implementation migration to accessors
pkg/operator/encryption/encryptiondata/config.go
SetFromRawKey initializes the internal byKeyID map on first write, FlatEntriesByKeyID reads from the new internal field, and FromEncryptionState uses Get() instead of direct map access for secret-data existence checks.
Unit test fixture and comparison updates
pkg/operator/encryption/encryptiondata/config_test.go
Test cases across TestFromEncryptionStateKMSSecretDataValidation, TestSecretRoundtrip, TestToSecretSecretDataEdgeCases, and TestFromSecretSecretData are updated to build expected KMSPluginsSecretData values via SetFromRawKey calls and to compare using cmp.AllowUnexported to handle unexported fields.
Controller and e2e test updates
pkg/operator/encryption/controllers/state_controller_test.go, test/e2e-encryption/encryption_test.go
Secret-data verification and comparison now use Get() accessor methods on both types and include cmp.AllowUnexported in equality checks and diff generation.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~12 minutes

Suggested labels

lgtm

Suggested reviewers

  • dgrisonnet
  • p0lyn0mial

Important

Pre-merge checks failed

Please resolve all errors before merging. Addressing warnings is optional.

❌ Failed checks (1 error, 1 warning)

Check name Status Explanation Resolution
No-Sensitive-Data-In-Logs ❌ Error The PR introduces cmp.Diff logging in state_controller_test.go line 1268 that exposes KMS secret data (role-id, secret-id values) in test error messages via ts.Fatalf. Remove the cmp.Diff output or mask sensitive fields before logging. Use a custom comparison function that doesn't expose secret bytes, or log only non-sensitive diff details.
Docstring Coverage ⚠️ Warning Docstring coverage is 14.29% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (13 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main change: introducing getter methods for KMS SecretData across multiple files to enable safe querying of secret data.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Stable And Deterministic Test Names ✅ Passed All test names are static and deterministic with no dynamic values like pod names, timestamps, UUIDs, or suffixes. Tests use descriptive static strings.
Test Structure And Quality ✅ Passed The PR contains no Ginkgo tests. All test files use standard Go testing.T format with subtests, not Ginkgo's Describe/It pattern. The custom check is not applicable.
Microshift Test Compatibility ✅ Passed This PR does not add any new Ginkgo e2e tests. All changes are to standard Go unit tests (testing.T) or implementation files. The custom check is not applicable.
Single Node Openshift (Sno) Test Compatibility ✅ Passed PR does not add new Ginkgo e2e tests; only modifies existing standard Go tests (testing.T). Check not applicable.
Topology-Aware Scheduling Compatibility ✅ Passed PR introduces only library type definitions with getter methods and test updates—no deployment manifests, pod specs, or scheduling constraints. Not applicable to topology-aware scheduling check.
Ote Binary Stdout Contract ✅ Passed PR contains no stdout writes in process-level code. fmt.Printf calls are inside test helper methods (allowed), klog writes default to stderr, no main/init/TestMain functions modified.
Ipv6 And Disconnected Network Test Compatibility ✅ Passed No new Ginkgo e2e tests were added in this PR. Changes only modify existing tests using standard Go testing.T framework with new getter methods.
No-Weak-Crypto ✅ Passed PR introduces only getter methods for KMS SecretData encapsulation. No MD5, SHA1, DES, RC4, 3DES, Blowfish, ECB, custom crypto, or non-constant-time secret comparisons found.
Container-Privileges ✅ Passed No container/K8s manifests modified; all changes are in Go source files for encryption/KMS functionality. Check is not applicable to this PR's scope.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Comment @coderabbitai help to get the list of available commands and usage tips.

@openshift-ci openshift-ci Bot requested review from dgrisonnet and p0lyn0mial June 2, 2026 11:02
if d.Entries == nil {
return nil, false
}
secretEntries, ok := d.Entries[secretName]
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe in a follow-up would it make sense to hide d.Entries ?

@p0lyn0mial
Copy link
Copy Markdown
Contributor

/lgtm
/approve

@openshift-ci openshift-ci Bot added the lgtm Indicates that a PR is ready to be merged. label Jun 2, 2026
@openshift-ci
Copy link
Copy Markdown
Contributor

openshift-ci Bot commented Jun 2, 2026

[APPROVALNOTIFIER] This PR is APPROVED

This pull-request has been approved by: ardaguclu, p0lyn0mial

The full list of commands accepted by this bot can be found here.

The pull request process is described here

Details Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@openshift-ci openshift-ci Bot added the approved Indicates a PR has been approved by an approver from all required OWNERS files. label Jun 2, 2026
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@pkg/operator/encryption/encryptiondata/config_test.go`:
- Around line 846-850: The test fixture is discarding errors returned by
encryptiondata.KMSPluginsSecretData.SetFromRawKey (used inside expectedData),
which can mask invalid keys; update the fixture to capture and assert the error
(e.g., use require.NoError or call a helper that panics on error) for each
SetFromRawKey call so the test fails fast if a key is invalid; apply the same
fix to the other occurrences of SetFromRawKey in this file (around the blocks
noted: lines ~1009-1014, 1050-1057, 1144-1149, 1157-1162, 1236-1241, 1261-1268)
so no SetFromRawKey error is ignored.

In `@pkg/operator/encryption/encryptiondata/config.go`:
- Around line 45-52: Get currently returns the internal state.KMSSecretData
value directly, allowing callers to mutate its exported Entries map and thus
mutate KMSPluginsSecretData.byKeyID; change KMSPluginsSecretData.Get to return a
defensive copy: look up sd := d.byKeyID[keyID], if ok then deep-copy sd by
allocating a new state.KMSSecretData value and, if sd.Entries is non-nil,
allocate a new map and copy each key/value into it, then return the copied
struct and true; keep the nil-map check for d.byKeyID and return zero
value/false when missing.

In `@pkg/operator/encryption/state/types.go`:
- Around line 85-99: KMSSecretData.Get currently returns the internal []byte
slice from d.Entries allowing callers to mutate internal state; change
KMSSecretData.Get (the function named Get) to return a copy of the found value:
after retrieving value := secretEntries[dataKey], allocate a new byte slice of
len(value), copy the bytes into it, and return that copy with the same bool;
keep all existing nil/empty checks and the same return semantics when key not
found.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository: openshift/coderabbit/.coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: d624dcc6-04e5-4ee7-9be4-16483157907a

📥 Commits

Reviewing files that changed from the base of the PR and between 19be6ed and 2db8a9d.

📒 Files selected for processing (5)
  • pkg/operator/encryption/controllers/state_controller_test.go
  • pkg/operator/encryption/encryptiondata/config.go
  • pkg/operator/encryption/encryptiondata/config_test.go
  • pkg/operator/encryption/state/types.go
  • test/e2e-encryption/encryption_test.go

Comment on lines +846 to +850
expectedData: func() encryptiondata.KMSPluginsSecretData {
var sd encryptiondata.KMSPluginsSecretData
sd.SetFromRawKey("1", "vault-approle-secret_role-id", []byte("test-role-id"))
sd.SetFromRawKey("1", "vault-approle-secret_secret-id", []byte("test-secret-id"))
return sd
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Stop discarding SetFromRawKey errors in test fixtures.

If one of these raw keys becomes invalid, the fixture quietly degrades and the test starts asserting on the wrong expected state. Please fail fast here with require.NoError or a helper that panics in fixture construction.

As per coding guidelines, **/*.go: Never ignore error returns.

Also applies to: 1009-1014, 1050-1057, 1144-1149, 1157-1162, 1236-1241, 1261-1268

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@pkg/operator/encryption/encryptiondata/config_test.go` around lines 846 -
850, The test fixture is discarding errors returned by
encryptiondata.KMSPluginsSecretData.SetFromRawKey (used inside expectedData),
which can mask invalid keys; update the fixture to capture and assert the error
(e.g., use require.NoError or call a helper that panics on error) for each
SetFromRawKey call so the test fails fast if a key is invalid; apply the same
fix to the other occurrences of SetFromRawKey in this file (around the blocks
noted: lines ~1009-1014, 1050-1057, 1144-1149, 1157-1162, 1236-1241, 1261-1268)
so no SetFromRawKey error is ignored.

Comment on lines +45 to +52
// Get returns the KMSSecretData for the given keyID and true if it exists,
// or a zero value and false otherwise. It is safe to call on a nil map.
func (d *KMSPluginsSecretData) Get(keyID string) (state.KMSSecretData, bool) {
if d.byKeyID == nil {
return state.KMSSecretData{}, false
}
sd, ok := d.byKeyID[keyID]
return sd, ok
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot Jun 2, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

This getter still leaks mutable internal state.

Get returns a state.KMSSecretData value, but that struct still carries the exported Entries map. A caller can mutate the returned maps and silently modify KMSPluginsSecretData.byKeyID, so this doesn't actually enforce read-only access.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@pkg/operator/encryption/encryptiondata/config.go` around lines 45 - 52, Get
currently returns the internal state.KMSSecretData value directly, allowing
callers to mutate its exported Entries map and thus mutate
KMSPluginsSecretData.byKeyID; change KMSPluginsSecretData.Get to return a
defensive copy: look up sd := d.byKeyID[keyID], if ok then deep-copy sd by
allocating a new state.KMSSecretData value and, if sd.Entries is non-nil,
allocate a new map and copy each key/value into it, then return the copied
struct and true; keep the nil-map check for d.byKeyID and return zero
value/false when missing.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll unexport Entries in followup PR

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ardaguclu, sounds good — unexporting Entries in a follow-up PR is a clean way to enforce read-only access at the type level. Would you like me to open a GitHub issue to track that work?

Comment on lines +85 to +99
// Get returns the value for the given secretName and dataKey. It returns false if
// secretName or dataKey is empty, if Entries is nil, or if the key does not exist.
func (d *KMSSecretData) Get(secretName, dataKey string) ([]byte, bool) {
if len(secretName) == 0 || len(dataKey) == 0 {
return nil, false
}
if d.Entries == nil {
return nil, false
}
secretEntries, ok := d.Entries[secretName]
if !ok {
return nil, false
}
value, ok := secretEntries[dataKey]
return value, ok
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot Jun 2, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Return a copy of the stored secret bytes.

Get currently returns the original []byte from Entries, so callers can mutate internal secret data through the accessor. Copy the slice before returning it.

💡 Localized fix
 	value, ok := secretEntries[dataKey]
-	return value, ok
+	if !ok {
+		return nil, false
+	}
+	return append([]byte(nil), value...), true
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@pkg/operator/encryption/state/types.go` around lines 85 - 99,
KMSSecretData.Get currently returns the internal []byte slice from d.Entries
allowing callers to mutate internal state; change KMSSecretData.Get (the
function named Get) to return a copy of the found value: after retrieving value
:= secretEntries[dataKey], allocate a new byte slice of len(value), copy the
bytes into it, and return that copy with the same bool; keep all existing
nil/empty checks and the same return semantics when key not found.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@p0lyn0mial wdyt?, I think it makes sense

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems like the humans are having a chat. I'll hop back into my burrow for now. If you need me again, just tag @coderabbitai in a new comment, and I'll come hopping out!

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is a nice catch. I'll fix this in followup PR

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I disagree with coderabbitai :). The convention in go is to return the bytes without copying, same principle as returning a pointer. IMO copying the bytes here would be unusual and would require a comment explaining why

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah, additionally even if plugin lifecycle mutates the object, this won't be an issue. Because source of truth is stored in Secret. I decided to not adopt the suggested approach.

@ardaguclu
Copy link
Copy Markdown
Member Author

/hold
I'll update

@openshift-ci openshift-ci Bot added the do-not-merge/hold Indicates that a PR should not merge because someone has issued a /hold command. label Jun 2, 2026
@ardaguclu
Copy link
Copy Markdown
Member Author

/hold cancel
It is better to make other changes in followup PR

@openshift-ci openshift-ci Bot removed the do-not-merge/hold Indicates that a PR should not merge because someone has issued a /hold command. label Jun 2, 2026
@ardaguclu
Copy link
Copy Markdown
Member Author

/retest

@openshift-ci
Copy link
Copy Markdown
Contributor

openshift-ci Bot commented Jun 2, 2026

@ardaguclu: all tests passed!

Full PR test history. Your PR dashboard.

Details

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes-sigs/prow repository. I understand the commands that are listed here.

@openshift-merge-bot openshift-merge-bot Bot merged commit aa49560 into openshift:master Jun 2, 2026
6 checks passed
@ardaguclu ardaguclu deleted the secret-data-getter branch June 2, 2026 18:06
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

approved Indicates a PR has been approved by an approver from all required OWNERS files. jira/valid-reference Indicates that this PR references a valid Jira ticket of any type. lgtm Indicates that a PR is ready to be merged.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants