Skip to content

CNTRLPLANE-3237: Error out when encryption-config Secret contains mismatching KMS providers - #2202

Merged
openshift-merge-bot[bot] merged 1 commit into
openshift:masterfrom
ardaguclu:kms-provider-sanity-check
May 7, 2026
Merged

openshift-merge-bot[bot] merged 1 commit into
openshift:masterfrom
ardaguclu:kms-provider-sanity-check

Conversation

@ardaguclu

@ardaguclu ardaguclu commented May 6, 2026

Copy link
Copy Markdown
Member

As promised in #2163 (comment), this PR errors out when there is a discrepancy between same KMS providers in different resources.

Summary by CodeRabbit

  • New Features

    • Added KMS provider configuration support with cross-resource consistency validation.
  • Bug Fixes

    • Improved error handling when deriving encryption configuration to avoid proceeding on conversion failures.
    • Early-failure on inconsistent KMS provider usage to prevent applying conflicting encryption settings.
  • Tests

    • Added and updated tests covering KMS provider validation and related error paths.

@openshift-ci-robot openshift-ci-robot added the jira/valid-reference Indicates that this PR references a valid Jira ticket of any type. label May 6, 2026
@openshift-ci-robot

openshift-ci-robot commented May 6, 2026

Copy link
Copy Markdown

@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:

As promised in #2163 (comment), this PR errors out when there is a discrepancy between same KMS providers in different resources.

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.

@ardaguclu

Copy link
Copy Markdown
Member Author

/hold
until #2163 merges

@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 May 6, 2026
@openshift-ci
openshift-ci Bot requested review from dgrisonnet and p0lyn0mial May 6, 2026 09:41
@coderabbitai

coderabbitai Bot commented May 6, 2026

Copy link
Copy Markdown

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

Walkthrough

FromEncryptionState now returns (*Config, error) and Config gains a KMSProviders map. The conversion aggregates and validates KMS provider configs by key ID (erroring on mismatches). Call sites and tests were updated to handle the error return.

Changes

KMS Provider aggregation, validation, and error propagation

Layer / File(s) Summary
Data Shape
pkg/operator/encryption/encryptiondata/config.go
Config now includes KMSProviders map[string]*configv1.KMSConfig.
Core Implementation
pkg/operator/encryption/encryptiondata/config.go
FromEncryptionState signature changed to (*Config, error); it collects KMS provider configs by key.Key.Name, validates consistency using equality.Semantic.DeepEqual, and returns an error on mismatches. Imports updated to include configv1 and equality.
Error Handling / Wiring
pkg/operator/encryption/controllers/migration_controller.go, pkg/operator/encryption/controllers/state_controller.go
Callers updated to capture (cfg, err) from FromEncryptionState and return early on error; subsequent logic unchanged.
Tests
pkg/operator/encryption/encryptiondata/config_test.go, pkg/operator/encryption/statemachine/transition_test.go
Tests updated to use the two-value return and fail on non-nil errors; new test TestFromEncryptionStateKMSProviderConfigValidation added to assert matching vs. mismatched KMS provider configs behavior.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

🚥 Pre-merge checks | ✅ 10 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 13.04% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Test Structure And Quality ❓ Inconclusive Custom check specifies Ginkgo test code requirements, but the PR contains standard Go tests (not Ginkgo). The check itself appears to be designed for a different codebase pattern. Clarify if this check should apply to standard Go tests or only Ginkgo tests. If standard Go tests apply: address pointer reuse in TestFromEncryptionStateKMSProviderConfigValidation (lines 726, 736).
✅ Passed checks (10 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title accurately summarizes the main change: adding error handling for mismatching KMS provider configurations in the encryption-config Secret.
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 stable and deterministic. The codebase uses standard Go testing, not Ginkgo. All test function and subtest names are static strings without dynamic content.
Microshift Test Compatibility ✅ Passed No Ginkgo e2e tests are added in this PR. All modified test files use standard Go testing.T pattern, which indicates they are unit tests. No Ginkgo imports found in any modified files.
Single Node Openshift (Sno) Test Compatibility ✅ Passed No Ginkgo e2e tests are added in this PR. All test changes are standard Go unit tests using the testing package. The SNO compatibility check is not applicable.
Topology-Aware Scheduling Compatibility ✅ Passed PR contains only encryption validation code changes. No scheduling constraints, manifests, pod affinity, topology spread, nodeSelectors, or PDB configurations added.
Ote Binary Stdout Contract ✅ Passed This is library-go repository code, not OTE binary code. All modified files are library packages with no process-level code or stdout writes. Check is not applicable.
Ipv6 And Disconnected Network Test Compatibility ✅ Passed Check not applicable. PR modifies standard Go unit tests using testing.T, not Ginkgo e2e tests. The custom check targets only Ginkgo tests with It(), Describe(), etc.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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

@ardaguclu
ardaguclu force-pushed the kms-provider-sanity-check branch from 87fe32b to 1cc15b6 Compare May 6, 2026 09:52

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🧹 Nitpick comments (3)
pkg/operator/encryption/controllers/state_controller_test.go (2)

715-1173: ⚡ Quick win

Add a negative regression case for the new mismatch check.

These new KMS scenarios only cover matching provider configs or different key IDs. The PR’s core behavior is rejecting the same key ID when different resources carry different provider configs, so that failure path still is not exercised here. A scenario that expects Sync() to error and the degraded condition to flip would lock the new behavior down.

🤖 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/controllers/state_controller_test.go` around lines
715 - 1173, Add a negative regression test case to the scenarios slice in
state_controller_test.go that exercises the new "mismatch" rejection: create
initialResources using the same KMS key ID (e.g., key "1") across two
resources/namespaces but supply different provider configs (use
encryptiontesting.CreateEncryptionKeySecretWithKMSConfig for one and
CreateEncryptionKeySecretWithCustomKMSConfig for the other) so Sync() should
return an error and flip degraded; set expectedActions to include the normal
reads but expect no create/update of the encryption-config secret, and implement
validateFunc to assert that the controller returned an error (or that degraded
condition was set) instead of creating/updating the secret so the failure path
for the mismatch check is covered.

1130-1143: ⚡ Quick win

Reuse the shared default KMS fixture here.

This block re-inlines the same Vault config that the rest of the file already pulls from encryptiontesting.DefaultKMSProviderConfig. Reusing the shared fixture would avoid one-off drift when the default test config changes.

🤖 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/controllers/state_controller_test.go` around lines
1130 - 1143, The test is re-inlining a Vault KMS config that already exists as a
shared fixture; replace the inlined map entry in KMSProviders with the shared
fixture by using encryptiontesting.DefaultKMSProviderConfig (instead of
constructing a configv1.VaultKMSConfig block), so the map becomes e.g.
KMSProviders: map[string]*configv1.KMSConfig{"2":
encryptiontesting.DefaultKMSProviderConfig} to avoid duplication and drift.
pkg/operator/encryption/testing/helpers.go (1)

101-118: ⚡ Quick win

Avoid sharing a mutable default KMS config across tests.

DefaultKMSProviderConfig is now a package-global *configv1.KMSConfig that gets threaded through multiple fixtures. One accidental mutation in a scenario will leak into unrelated tests. A small constructor that returns a fresh config for each call would make these helpers much safer.

🤖 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/testing/helpers.go` around lines 101 - 118, Replace
the package-global pointer DefaultKMSProviderConfig with a constructor function
(e.g., NewDefaultKMSProviderConfig) that returns a freshly allocated
*configv1.KMSConfig each call; update CreateEncryptionKeySecretWithKMSConfig to
call this constructor (or callers that rely on the default) and pass the
returned value into CreateEncryptionKeySecretWithCustomKMSConfig so tests get
isolated, non-shared configs and accidental mutations cannot leak across tests.
🤖 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.

Nitpick comments:
In `@pkg/operator/encryption/controllers/state_controller_test.go`:
- Around line 715-1173: Add a negative regression test case to the scenarios
slice in state_controller_test.go that exercises the new "mismatch" rejection:
create initialResources using the same KMS key ID (e.g., key "1") across two
resources/namespaces but supply different provider configs (use
encryptiontesting.CreateEncryptionKeySecretWithKMSConfig for one and
CreateEncryptionKeySecretWithCustomKMSConfig for the other) so Sync() should
return an error and flip degraded; set expectedActions to include the normal
reads but expect no create/update of the encryption-config secret, and implement
validateFunc to assert that the controller returned an error (or that degraded
condition was set) instead of creating/updating the secret so the failure path
for the mismatch check is covered.
- Around line 1130-1143: The test is re-inlining a Vault KMS config that already
exists as a shared fixture; replace the inlined map entry in KMSProviders with
the shared fixture by using encryptiontesting.DefaultKMSProviderConfig (instead
of constructing a configv1.VaultKMSConfig block), so the map becomes e.g.
KMSProviders: map[string]*configv1.KMSConfig{"2":
encryptiontesting.DefaultKMSProviderConfig} to avoid duplication and drift.

In `@pkg/operator/encryption/testing/helpers.go`:
- Around line 101-118: Replace the package-global pointer
DefaultKMSProviderConfig with a constructor function (e.g.,
NewDefaultKMSProviderConfig) that returns a freshly allocated
*configv1.KMSConfig each call; update CreateEncryptionKeySecretWithKMSConfig to
call this constructor (or callers that rely on the default) and pass the
returned value into CreateEncryptionKeySecretWithCustomKMSConfig so tests get
isolated, non-shared configs and accidental mutations cannot leak across tests.

ℹ️ Review info
⚙️ Run configuration

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

Review profile: CHILL

Plan: Enterprise

Run ID: a8cc2d5f-0da9-4d15-8291-d92890b790ab

📥 Commits

Reviewing files that changed from the base of the PR and between da43577 and 87fe32b.

📒 Files selected for processing (13)
  • pkg/operator/encryption/controllers/key_controller_test.go
  • pkg/operator/encryption/controllers/migration_controller.go
  • pkg/operator/encryption/controllers/state_controller.go
  • pkg/operator/encryption/controllers/state_controller_test.go
  • pkg/operator/encryption/encryptiondata/config.go
  • pkg/operator/encryption/encryptiondata/config_test.go
  • pkg/operator/encryption/encryptiondata/secret.go
  • pkg/operator/encryption/kms/helpers.go
  • pkg/operator/encryption/kms/helpers_test.go
  • pkg/operator/encryption/secrets/secrets_test.go
  • pkg/operator/encryption/statemachine/transition_test.go
  • pkg/operator/encryption/testing/helpers.go
  • test/e2e-encryption/encryption_test.go

@ardaguclu
ardaguclu force-pushed the kms-provider-sanity-check branch from 1cc15b6 to 9d684bf Compare May 6, 2026 11:39
@ardaguclu

Copy link
Copy Markdown
Member Author

/hold cancel

@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 May 6, 2026
}
if _, exists := kmsProviders[key.Key.Name]; !exists {
if provider, exists := kmsProviders[key.Key.Name]; exists {
if !reflect.DeepEqual(provider, key.KMSConfig.Provider) {

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.

since we're comparing api types, should we use the equality package from upstream? There's also maps.Equal, but I think equality is more appropriate

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.

Good idea, I'll use it

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.

+1

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.

Updated to use equality.Semantic.DeepEqual.

if _, exists := kmsProviders[key.Key.Name]; !exists {
if provider, exists := kmsProviders[key.Key.Name]; exists {
if !reflect.DeepEqual(provider, key.KMSConfig.Provider) {
return nil, fmt.Errorf("KMS provider config mismatch for keyID %s: configs from different resources must be identical", key.Key.Name)

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.

in what scenarios can this happen? When the user manually change the secrets? Or when there's a bug in our code?

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.

When the user manually change the secrets?

I think mostly this can cause

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.

Or when there's a bug in our code?

that too, that's why we want to add an extra check.

}
if _, exists := kmsProviders[key.Key.Name]; !exists {
if provider, exists := kmsProviders[key.Key.Name]; exists {
if !reflect.DeepEqual(provider, key.KMSConfig.Provider) {

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.

please add a comment explaining why we need an extra check.

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.

Updated

@ardaguclu
ardaguclu force-pushed the kms-provider-sanity-check branch from 9d684bf to ae1be62 Compare May 7, 2026 10:58
return make([]byte, 16)
}

func TestFromEncryptionStateKMSProviderConfigMismatch(t *testing.T) {

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.

nit: could you rework to table tests ?

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.

Updated for table tests

@p0lyn0mial

Copy link
Copy Markdown
Contributor

LGTM

@p0lyn0mial

Copy link
Copy Markdown
Contributor

I'm not tagging so that @bertinatto has a chance to look at the PR.

@ardaguclu
ardaguclu force-pushed the kms-provider-sanity-check branch from ae1be62 to c93db8d Compare May 7, 2026 11:29

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 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 718-737: The test "matching provider configs across resources"
currently reuses the same *configv1.KMSConfig pointer via
encryptiontesting.DefaultKMSProviderConfig for both resources; create two
separate but equal provider config objects instead (e.g., construct a new
apiserverconfigv1.KMSConfiguration/Provider object for the secrets entry and a
distinct one with the same fields for the configmaps entry) so that
state.KMSConfig.KMSConfig pointers are different while values match; update the
encryptionState entries' KMSConfig.Provider fields to reference these two
distinct instances rather than the single shared pointer.
🪄 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: d87cdd7f-08ce-4738-a10e-33623919b8cc

📥 Commits

Reviewing files that changed from the base of the PR and between ae1be62 and c93db8d.

📒 Files selected for processing (5)
  • pkg/operator/encryption/controllers/migration_controller.go
  • pkg/operator/encryption/controllers/state_controller.go
  • pkg/operator/encryption/encryptiondata/config.go
  • pkg/operator/encryption/encryptiondata/config_test.go
  • pkg/operator/encryption/statemachine/transition_test.go
🚧 Files skipped from review as they are similar to previous changes (3)
  • pkg/operator/encryption/controllers/migration_controller.go
  • pkg/operator/encryption/statemachine/transition_test.go
  • pkg/operator/encryption/encryptiondata/config.go

Comment on lines +718 to +737
name: "matching provider configs across resources",
encryptionState: map[schema.GroupResource]state.GroupResourceState{
{Resource: "secrets"}: {
ReadKeys: []state.KeyState{{
Key: apiserverconfigv1.Key{Name: "1", Secret: "AAAAAAAAAAAAAAAAAAAAAA=="},
Mode: state.KMS,
KMSConfig: &state.KMSConfig{
Encryption: &apiserverconfigv1.KMSConfiguration{APIVersion: "v2", Name: "1", Endpoint: "unix:///var/run/kmsplugin/kms-1.sock"},
Provider: encryptiontesting.DefaultKMSProviderConfig,
},
}},
},
{Resource: "configmaps"}: {
ReadKeys: []state.KeyState{{
Key: apiserverconfigv1.Key{Name: "1", Secret: "AAAAAAAAAAAAAAAAAAAAAA=="},
Mode: state.KMS,
KMSConfig: &state.KMSConfig{
Encryption: &apiserverconfigv1.KMSConfiguration{APIVersion: "v2", Name: "1", Endpoint: "unix:///var/run/kmsplugin/kms-1.sock"},
Provider: encryptiontesting.DefaultKMSProviderConfig,
},

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

Use distinct-but-equal provider configs in the “matching” test case.

Line 726 and Line 736 currently reuse the same *configv1.KMSConfig pointer. That can let a pointer-identity bug pass. Please construct two separate but equivalent provider config objects for the two resources.

🤖 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 718 -
737, The test "matching provider configs across resources" currently reuses the
same *configv1.KMSConfig pointer via encryptiontesting.DefaultKMSProviderConfig
for both resources; create two separate but equal provider config objects
instead (e.g., construct a new apiserverconfigv1.KMSConfiguration/Provider
object for the secrets entry and a distinct one with the same fields for the
configmaps entry) so that state.KMSConfig.KMSConfig pointers are different while
values match; update the encryptionState entries' KMSConfig.Provider fields to
reference these two distinct instances rather than the single shared pointer.

@openshift-ci

openshift-ci Bot commented May 7, 2026

Copy link
Copy Markdown
Contributor

@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.

@bertinatto bertinatto left a comment

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.

/lgtm

@openshift-ci openshift-ci Bot added the lgtm Indicates that a PR is ready to be merged. label May 7, 2026
@openshift-ci

openshift-ci Bot commented May 7, 2026

Copy link
Copy Markdown
Contributor

[APPROVALNOTIFIER] This PR is APPROVED

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

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 May 7, 2026
@openshift-merge-bot
openshift-merge-bot Bot merged commit 6769d5f into openshift:master May 7, 2026
6 checks passed
@ardaguclu
ardaguclu deleted the kms-provider-sanity-check branch May 7, 2026 16:12
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