diff --git a/go.mod b/go.mod index 6eabf54ecb..101374f3ea 100644 --- a/go.mod +++ b/go.mod @@ -135,4 +135,7 @@ require ( sigs.k8s.io/yaml v1.6.0 // indirect ) -replace github.com/onsi/ginkgo/v2 => github.com/openshift/onsi-ginkgo/v2 v2.6.1-0.20251001123353-fd5b1fb35db1 +replace ( + github.com/onsi/ginkgo/v2 => github.com/openshift/onsi-ginkgo/v2 v2.6.1-0.20251001123353-fd5b1fb35db1 + github.com/openshift/library-go => github.com/bertinatto/library-go v0.0.0-20260521212646-57fab52e2504 +) diff --git a/go.sum b/go.sum index d013390c0c..656b7cac16 100644 --- a/go.sum +++ b/go.sum @@ -8,6 +8,8 @@ github.com/apparentlymart/go-cidr v1.0.1 h1:NmIwLZ/KdsjIUlhf+/Np40atNXm/+lZ5txfT github.com/apparentlymart/go-cidr v1.0.1/go.mod h1:EBcsNrHc3zQeuaeCeCtQruQm+n9/YjEn/vI25Lg7Gwc= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= +github.com/bertinatto/library-go v0.0.0-20260521212646-57fab52e2504 h1:LUvXyaZtY1Q4rYCQI8QF6UzseSaOCQPYJlvGovbXv0k= +github.com/bertinatto/library-go v0.0.0-20260521212646-57fab52e2504/go.mod h1:gKG9lctU0yEftSoT3DUyeIWz1oAgF0EHUpwI4pnCo4o= github.com/blang/semver/v4 v4.0.0 h1:1PFHFE6yCCTv8C1TeyNNarDzntLi7wMI5i/pzqYIsAM= github.com/blang/semver/v4 v4.0.0/go.mod h1:IbckMUScFkM3pff0VJDNKRiT6TG/YpiHIM2yvyW5YoQ= github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8= @@ -165,8 +167,6 @@ github.com/openshift/build-machinery-go v0.0.0-20250530140348-dc5b2804eeee h1:+S github.com/openshift/build-machinery-go v0.0.0-20250530140348-dc5b2804eeee/go.mod h1:8jcm8UPtg2mCAsxfqKil1xrmRMI3a+XU2TZ9fF8A7TE= github.com/openshift/client-go v0.0.0-20260512113608-deb4dc54551a h1:EKx2XhOKehd1C5ptY7IrLl4WV35E8kP0pRPnG5BUZXk= github.com/openshift/client-go v0.0.0-20260512113608-deb4dc54551a/go.mod h1:V933kvY/cb/Un7UCEOhXHUySNX327u7Epe8g9KNqg2Q= -github.com/openshift/library-go v0.0.0-20260520123929-8dbb42ebf1e9 h1:1ubwPydT+ABjfvmeiv4hoJQ0gIDCyMq/U5UyHrrpefA= -github.com/openshift/library-go v0.0.0-20260520123929-8dbb42ebf1e9/go.mod h1:gKG9lctU0yEftSoT3DUyeIWz1oAgF0EHUpwI4pnCo4o= github.com/openshift/onsi-ginkgo/v2 v2.6.1-0.20251001123353-fd5b1fb35db1 h1:PMTgifBcBRLJJiM+LgSzPDTk9/Rx4qS09OUrfpY6GBQ= github.com/openshift/onsi-ginkgo/v2 v2.6.1-0.20251001123353-fd5b1fb35db1/go.mod h1:7Du3c42kxCUegi0IImZ1wUQzMBVecgIHjR1C+NkhLQo= github.com/orisano/pixelmatch v0.0.0-20220722002657-fb0b55479cde/go.mod h1:nZgzbfBr3hhjoZnS66nKrHmduYNpc34ny7RK4z5/HM0= diff --git a/test/e2e-encryption-kms/encryption_kms.go b/test/e2e-encryption-kms/encryption_kms.go index 34569175c1..0450cc7d17 100644 --- a/test/e2e-encryption-kms/encryption_kms.go +++ b/test/e2e-encryption-kms/encryption_kms.go @@ -8,7 +8,6 @@ import ( g "github.com/onsi/ginkgo/v2" - configv1 "github.com/openshift/api/config/v1" "github.com/openshift/cluster-kube-apiserver-operator/pkg/operator/operatorclient" operatorencryption "github.com/openshift/cluster-kube-apiserver-operator/test/library/encryption" library "github.com/openshift/library-go/test/library/encryption" @@ -42,7 +41,7 @@ func testKMSEncryptionOnOff(t testing.TB) { // NOTE: This manual deployment is only required for KMS v1. In the future, // the platform will manage the KMS plugins, and this code will no longer be needed. librarykms.DeployUpstreamMockKMSPlugin(context.Background(), t, library.GetClients(t).Kube, librarykms.WellKnownUpstreamMockKMSPluginNamespace, librarykms.WellKnownUpstreamMockKMSPluginImage, librarykms.DefaultKMSPluginCount) - library.TestEncryptionTurnOnAndOff(t, library.OnOffScenario{ + library.TestEncryptionTurnOnAndOff(context.TODO(), t, library.OnOffScenario{ BasicScenario: library.BasicScenario{ Namespace: operatorclient.GlobalMachineSpecifiedConfigNamespace, LabelSelector: "encryption.apiserver.operator.openshift.io/component" + "=" + operatorclient.TargetNamespace, @@ -57,10 +56,7 @@ func testKMSEncryptionOnOff(t testing.TB) { AssertResourceNotEncryptedFunc: operatorencryption.AssertSecretOfLifeNotEncrypted, ResourceFunc: operatorencryption.SecretOfLife, ResourceName: "SecretOfLife", - EncryptionProvider: library.EncryptionProvider{APIServerEncryption: configv1.APIServerEncryption{ - Type: configv1.EncryptionTypeKMS, - KMS: librarykms.DefaultFakeKMSPluginConfig, - }}, + EncryptionProvider: librarykms.DefaultFakeVaultEncryptionProvider, }) } @@ -74,7 +70,7 @@ func testKMSEncryptionOnOff(t testing.TB) { // 6. Verifies secret is correctly encrypted after each migration func testKMSEncryptionProvidersMigration(t testing.TB) { librarykms.DeployUpstreamMockKMSPlugin(context.Background(), t, library.GetClients(t).Kube, librarykms.WellKnownUpstreamMockKMSPluginNamespace, librarykms.WellKnownUpstreamMockKMSPluginImage, librarykms.DefaultKMSPluginCount) - library.TestEncryptionProvidersMigration(t, library.ProvidersMigrationScenario{ + library.TestEncryptionProvidersMigration(context.TODO(), t, library.ProvidersMigrationScenario{ BasicScenario: library.BasicScenario{ Namespace: operatorclient.GlobalMachineSpecifiedConfigNamespace, LabelSelector: "encryption.apiserver.operator.openshift.io/component" + "=" + operatorclient.TargetNamespace, @@ -90,7 +86,7 @@ func testKMSEncryptionProvidersMigration(t testing.TB) { ResourceFunc: operatorencryption.SecretOfLife, ResourceName: "SecretOfLife", EncryptionProviders: library.ShuffleEncryptionProviders([]library.EncryptionProvider{ - {APIServerEncryption: configv1.APIServerEncryption{Type: configv1.EncryptionTypeKMS, KMS: librarykms.DefaultFakeKMSPluginConfig}}, + librarykms.DefaultFakeVaultEncryptionProvider, library.SupportedStaticEncryptionProviders[rand.IntN(len(library.SupportedStaticEncryptionProviders))], }), }) diff --git a/test/e2e-encryption-perf/encryption_perf_test.go b/test/e2e-encryption-perf/encryption_perf_test.go index 7540e280ee..6aa959dc55 100644 --- a/test/e2e-encryption-perf/encryption_perf_test.go +++ b/test/e2e-encryption-perf/encryption_perf_test.go @@ -31,7 +31,7 @@ var provider = flag.String("provider", "aescbc", "encryption provider used by th func TestPerfEncryption(tt *testing.T) { operatorClient := operatorencryption.GetOperator(tt) - library.TestPerfEncryption(tt, library.PerfScenario{ + library.TestPerfEncryption(context.TODO(), tt, library.PerfScenario{ BasicScenario: library.BasicScenario{ Namespace: operatorclient.GlobalMachineSpecifiedConfigNamespace, LabelSelector: "encryption.apiserver.operator.openshift.io/component" + "=" + operatorclient.TargetNamespace, diff --git a/test/e2e-encryption-rotation/encryption_rotation_test.go b/test/e2e-encryption-rotation/encryption_rotation_test.go index bb9e1bc065..3b1b59e900 100644 --- a/test/e2e-encryption-rotation/encryption_rotation_test.go +++ b/test/e2e-encryption-rotation/encryption_rotation_test.go @@ -19,7 +19,7 @@ var provider = flag.String("provider", "aescbc", "encryption provider used by th // rotation by setting the "encyrption.Reason" in the operator's configuration // file func TestEncryptionRotation(t *testing.T) { - library.TestEncryptionRotation(t, library.RotationScenario{ + library.TestEncryptionRotation(context.TODO(), t, library.RotationScenario{ BasicScenario: library.BasicScenario{ Namespace: operatorclient.GlobalMachineSpecifiedConfigNamespace, LabelSelector: "encryption.apiserver.operator.openshift.io/component" + "=" + operatorclient.TargetNamespace, diff --git a/test/e2e-encryption/encryption_test.go b/test/e2e-encryption/encryption_test.go index 4e49807269..62d691335e 100644 --- a/test/e2e-encryption/encryption_test.go +++ b/test/e2e-encryption/encryption_test.go @@ -1,6 +1,7 @@ package e2e_encryption import ( + "context" "flag" "fmt" "testing" @@ -14,7 +15,7 @@ import ( var provider = flag.String("provider", "aescbc", "encryption provider used by the tests") func TestEncryptionTypeIdentity(t *testing.T) { - library.TestEncryptionTypeIdentity(t, library.BasicScenario{ + library.TestEncryptionTypeIdentity(context.TODO(), t, library.BasicScenario{ Namespace: operatorclient.GlobalMachineSpecifiedConfigNamespace, LabelSelector: "encryption.apiserver.operator.openshift.io/component" + "=" + operatorclient.TargetNamespace, EncryptionConfigSecretName: fmt.Sprintf("encryption-config-%s", operatorclient.TargetNamespace), @@ -26,7 +27,7 @@ func TestEncryptionTypeIdentity(t *testing.T) { } func TestEncryptionTypeUnset(t *testing.T) { - library.TestEncryptionTypeUnset(t, library.BasicScenario{ + library.TestEncryptionTypeUnset(context.TODO(), t, library.BasicScenario{ Namespace: operatorclient.GlobalMachineSpecifiedConfigNamespace, LabelSelector: "encryption.apiserver.operator.openshift.io/component" + "=" + operatorclient.TargetNamespace, EncryptionConfigSecretName: fmt.Sprintf("encryption-config-%s", operatorclient.TargetNamespace), @@ -38,7 +39,7 @@ func TestEncryptionTypeUnset(t *testing.T) { } func TestEncryptionTurnOnAndOff(t *testing.T) { - library.TestEncryptionTurnOnAndOff(t, library.OnOffScenario{ + library.TestEncryptionTurnOnAndOff(context.TODO(), t, library.OnOffScenario{ BasicScenario: library.BasicScenario{ Namespace: operatorclient.GlobalMachineSpecifiedConfigNamespace, LabelSelector: "encryption.apiserver.operator.openshift.io/component" + "=" + operatorclient.TargetNamespace, diff --git a/test/e2e/encryption.go b/test/e2e/encryption.go index 12b7267144..b3e7a2083c 100644 --- a/test/e2e/encryption.go +++ b/test/e2e/encryption.go @@ -1,6 +1,7 @@ package e2e import ( + "context" "fmt" "testing" @@ -18,7 +19,7 @@ var _ = g.Describe("[sig-api-machinery] kube-apiserver operator", func() { }) func testEncryptionTypeAESCBC(t testing.TB) { - library.TestEncryptionTypeAESCBC(t, library.BasicScenario{ + library.TestEncryptionTypeAESCBC(context.TODO(), t, library.BasicScenario{ Namespace: operatorclient.GlobalMachineSpecifiedConfigNamespace, LabelSelector: "encryption.apiserver.operator.openshift.io/component" + "=" + operatorclient.TargetNamespace, EncryptionConfigSecretName: fmt.Sprintf("encryption-config-%s", operatorclient.TargetNamespace), diff --git a/vendor/github.com/openshift/library-go/pkg/operator/encryption/controllers/key_controller.go b/vendor/github.com/openshift/library-go/pkg/operator/encryption/controllers/key_controller.go index 3c930469d5..b0e1403993 100644 --- a/vendor/github.com/openshift/library-go/pkg/operator/encryption/controllers/key_controller.go +++ b/vendor/github.com/openshift/library-go/pkg/operator/encryption/controllers/key_controller.go @@ -42,6 +42,7 @@ const ( encryptionSecretMigrationInterval = time.Hour * 24 * 7 // one week kmsEndpointFormat = "unix:///var/run/kmsplugin/kms-%d.sock" defaultKMSTimeout = 10 * time.Second + openshiftConfigNS = "openshift-config" ) // keyController creates new keys if necessary. It @@ -117,6 +118,7 @@ func NewKeyController( apiServerInformer.Informer(), operatorClient.Informer(), kubeInformersForNamespaces.InformersFor("openshift-config-managed").Core().V1().Secrets().Informer(), + // TODO: add informer for openshift-config namespace to watch referenced Secrets for KMS plugin secret data changes deployer, ).ToController( c.controllerInstanceName, @@ -223,7 +225,7 @@ func (c *keyController) checkAndCreateKeys(ctx context.Context, syncContext fact sort.Sort(sort.StringSlice(reasons)) internalReason := strings.Join(reasons, ", ") - keySecret, err := c.generateKeySecret(newKeyID, currentMode, apiEncryptionConfiguration, internalReason, externalReason) + keySecret, err := c.generateKeySecret(ctx, newKeyID, currentMode, apiEncryptionConfiguration, internalReason, externalReason) if err != nil { return fmt.Errorf("failed to create key: %v", err) } @@ -260,7 +262,7 @@ func (c *keyController) validateExistingSecret(ctx context.Context, keySecret *c return nil // we made this key earlier } -func (c *keyController) generateKeySecret(keyID uint64, currentMode state.Mode, apiServerEncryption configv1.APIServerEncryption, internalReason, externalReason string) (*corev1.Secret, error) { +func (c *keyController) generateKeySecret(ctx context.Context, keyID uint64, currentMode state.Mode, apiServerEncryption configv1.APIServerEncryption, internalReason, externalReason string) (*corev1.Secret, error) { bs := crypto.ModeToNewKeyFunc[currentMode]() ks := state.KeyState{ Key: apiserverv1.Key{ @@ -281,6 +283,24 @@ func (c *keyController) generateKeySecret(keyID uint64, currentMode state.Mode, }, Plugin: apiServerEncryption.KMS, } + + if secretName, expectedKeys, err := referencedSecretName(apiServerEncryption.KMS); err != nil { + return nil, err + } else if len(secretName) > 0 { + refSecret, err := c.secretClient.Secrets(openshiftConfigNS).Get(ctx, secretName, metav1.GetOptions{}) + if err != nil { + return nil, fmt.Errorf("failed to get secret %s in %s: %w", secretName, openshiftConfigNS, err) + } + for _, key := range expectedKeys { + v, ok := refSecret.Data[key] + if !ok { + return nil, fmt.Errorf("secret %s in %s is missing required key %q", secretName, openshiftConfigNS, key) + } + if err := ks.KMS.PluginSecretData.Set(secretName, key, v); err != nil { + return nil, err + } + } + } } return secrets.FromKeyState(c.instanceName, ks) } @@ -383,6 +403,25 @@ func needsNewKey(grKeys state.GroupResourceState, currentMode state.Mode, extern return latestKeyID, "rotation-interval-has-passed", time.Since(latestKey.Migrated.Timestamp) > encryptionSecretMigrationInterval } +// referencedSecretName returns the name of the secret referenced by the KMS plugin +// config and the specific data keys to carry from that secret. Only the listed keys +// are copied into the Key Secret; any other data in the referenced secret is ignored. +func referencedSecretName(plugin configv1.KMSPluginConfig) (string, []string, error) { + switch plugin.Type { + case configv1.VaultKMSProvider: + switch plugin.Vault.Authentication.Type { + case configv1.VaultAuthenticationTypeAppRole: + // The Vault AppRole secret must contain "role-id" and "secret-id" keys. + // These are the only keys carried into the encryption key secret. + return plugin.Vault.Authentication.AppRole.Secret.Name, []string{"role-id", "secret-id"}, nil + default: + return "", nil, fmt.Errorf("unsupported Vault authentication type %q", plugin.Vault.Authentication.Type) + } + default: + return "", nil, fmt.Errorf("unsupported KMS provider type %q", plugin.Type) + } +} + // TODO make this un-settable once set // ex: we could require the tech preview no upgrade flag to be set before we will honor this field type unsupportedEncryptionConfig struct { diff --git a/vendor/github.com/openshift/library-go/pkg/operator/encryption/encryptiondata/config.go b/vendor/github.com/openshift/library-go/pkg/operator/encryption/encryptiondata/config.go index 40a855a81f..c4b7706af7 100644 --- a/vendor/github.com/openshift/library-go/pkg/operator/encryption/encryptiondata/config.go +++ b/vendor/github.com/openshift/library-go/pkg/operator/encryption/encryptiondata/config.go @@ -30,6 +30,48 @@ type Config struct { // KMSPlugins maps keyID to plugin-specific configuration, // carried from Key Secrets into the encryption-config Secret. KMSPlugins map[string]configv1.KMSPluginConfig + // KMSPluginsSecretData maps keyID to secret data carried from + // Key Secrets into the encryption-config Secret. + // Structure: keyID → secretName → dataKey → value. + KMSPluginsSecretData KMSPluginsSecretData +} + +// KMSPluginsSecretData maps keyID to secret data carried from Key Secrets into +// the encryption-config Secret. Structure: keyID → secretName → dataKey → value. +type KMSPluginsSecretData struct { + ByKeyID map[string]state.KMSSecretData +} + +// SetFromRawKey stores a value for the given keyID, splitting rawKey +// on "_" into secretName and dataKey. +func (d *KMSPluginsSecretData) SetFromRawKey(keyID, rawKey string, value []byte) error { + if len(keyID) == 0 { + return fmt.Errorf("keyID must not be empty") + } + if d.ByKeyID == nil { + d.ByKeyID = map[string]state.KMSSecretData{} + } + sd := d.ByKeyID[keyID] + if err := sd.SetFromRawKey(rawKey, value); err != nil { + return err + } + d.ByKeyID[keyID] = sd + return nil +} + +// FlatEntriesByKeyID returns the stored data as a map of keyID to flat entries, +// where each flat entry is keyed by "secretName_dataKey". +func (d *KMSPluginsSecretData) FlatEntriesByKeyID() map[string]map[string][]byte { + if d.ByKeyID == nil { + return nil + } + result := map[string]map[string][]byte{} + for keyID, sd := range d.ByKeyID { + if flat := sd.FlatEntries(); flat != nil { + result[keyID] = flat + } + } + return result } func (c *Config) HasEncryptionConfiguration() bool { @@ -40,6 +82,7 @@ func (c *Config) HasEncryptionConfiguration() bool { func FromEncryptionState(encryptionState map[schema.GroupResource]state.GroupResourceState) (*Config, error) { resourceConfigs := make([]apiserverconfigv1.ResourceConfiguration, 0, len(encryptionState)) var kmsPlugins map[string]configv1.KMSPluginConfig + var kmsPluginsSecretData KMSPluginsSecretData for gr, grKeys := range encryptionState { resourceConfigs = append(resourceConfigs, apiserverconfigv1.ResourceConfiguration{ @@ -47,11 +90,11 @@ func FromEncryptionState(encryptionState map[schema.GroupResource]state.GroupRes Providers: stateToProviders(gr.Resource, grKeys), }) - // Collect KMS plugin configs from read keys (which already include the write key). - // We iterate over encryptionState which is keyed by GroupResource, so the same - // keyID is seen once per resource (e.g. key "1" for secrets and key "1" for configmaps). - // Since all resources share the same Key Secret, the plugin config is identical - // across duplicates and we only need to keep the first occurrence. + // Collect KMS plugin configs and secret data from read keys (which already + // include the write key). We iterate over encryptionState which is keyed by + // GroupResource, so the same keyID is seen once per resource. Since all + // resources share the same Key Secret, the plugin config and secret data are + // identical across duplicates and we only need to keep the first occurrence. for _, key := range grKeys.ReadKeys { if key.HasKMSPlugin() { if kmsPlugins == nil { @@ -67,6 +110,19 @@ func FromEncryptionState(encryptionState map[schema.GroupResource]state.GroupRes kmsPlugins[key.Key.Name] = key.KMS.Plugin } } + if key.HasKMSSecretData() { + if existing, exists := kmsPluginsSecretData.ByKeyID[key.Key.Name]; exists { + if !equality.Semantic.DeepEqual(existing, key.KMS.PluginSecretData) { + return nil, fmt.Errorf("KMS secret data mismatch for keyID %s: secret data from different resources must be identical", key.Key.Name) + } + } else { + for rawKey, value := range key.KMS.PluginSecretData.FlatEntries() { + if err := kmsPluginsSecretData.SetFromRawKey(key.Key.Name, rawKey, value); err != nil { + return nil, fmt.Errorf("failed to copy secret data for keyID %s: %w", key.Key.Name, err) + } + } + } + } } } @@ -76,8 +132,9 @@ func FromEncryptionState(encryptionState map[schema.GroupResource]state.GroupRes }) return &Config{ - Encryption: &apiserverconfigv1.EncryptionConfiguration{Resources: resourceConfigs}, - KMSPlugins: kmsPlugins, + Encryption: &apiserverconfigv1.EncryptionConfiguration{Resources: resourceConfigs}, + KMSPlugins: kmsPlugins, + KMSPluginsSecretData: kmsPluginsSecretData, }, nil } diff --git a/vendor/github.com/openshift/library-go/pkg/operator/encryption/encryptiondata/secret.go b/vendor/github.com/openshift/library-go/pkg/operator/encryption/encryptiondata/secret.go index 0df884c62c..b2d025e19f 100644 --- a/vendor/github.com/openshift/library-go/pkg/operator/encryption/encryptiondata/secret.go +++ b/vendor/github.com/openshift/library-go/pkg/operator/encryption/encryptiondata/secret.go @@ -4,6 +4,7 @@ import ( "fmt" "sort" "strconv" + "strings" corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/equality" @@ -13,15 +14,42 @@ import ( configv1 "github.com/openshift/api/config/v1" "github.com/openshift/library-go/pkg/operator/encryption/encoding" - "github.com/openshift/library-go/pkg/operator/encryption/kms" "github.com/openshift/library-go/pkg/operator/encryption/state" ) -// EncryptionConfSecretName is the name of the final encryption config secret that is revisioned per apiserver rollout. -const EncryptionConfSecretName = "encryption-config" +const pluginConfigDataKeyPrefix = "kms-plugin-config-" -// EncryptionConfSecretKey is the map data key used to store the raw bytes of the final encryption config. -const EncryptionConfSecretKey = "encryption-config" +// toPluginConfigSecretDataKeyFor constructs the data key for storing a KMS plugin config in the encryption-config Secret. +// The keyID must be a valid non-negative integer string. +func toPluginConfigSecretDataKeyFor(keyID string) (string, error) { + if _, err := strconv.ParseUint(keyID, 10, 64); err != nil { + return "", fmt.Errorf("invalid keyID %q: must be a non-negative integer", keyID) + } + return pluginConfigDataKeyPrefix + keyID, nil +} + +// keyIDFromPluginConfigSecretDataKey extracts the keyID from a kms-plugin-config data key. +// Returns the keyID and true if the key matches the "kms-plugin-config-" pattern. +func keyIDFromPluginConfigSecretDataKey(dataKey string) (string, bool, error) { + keyID, found := strings.CutPrefix(dataKey, pluginConfigDataKeyPrefix) + if !found || len(keyID) == 0 { + return "", false, nil + } + if _, err := strconv.ParseUint(keyID, 10, 64); err != nil { + return "", false, fmt.Errorf("invalid keyID %q: must be a non-negative integer", keyID) + } + return keyID, true, nil +} + +const ( + // EncryptionConfSecretName is the name of the final encryption config secret that is revisioned per apiserver rollout. + EncryptionConfSecretName = "encryption-config" + // EncryptionConfSecretKey is the map data key used to store the raw bytes of the final encryption config. + EncryptionConfSecretKey = "encryption-config" + // encryptionConfigSecretDataPrefix is the data key prefix for KMS plugin secret + // data entries in the encryption-config Secret. Full key: "kms-plugin-secret-{secretName}_{dataKey}-{keyID}". + encryptionConfigSecretDataPrefix = "kms-plugin-secret-" +) func FromSecret(encryptionConfigSecret *corev1.Secret) (*Config, error) { data, ok := encryptionConfigSecret.Data[EncryptionConfSecretKey] @@ -36,7 +64,7 @@ func FromSecret(encryptionConfigSecret *corev1.Secret) (*Config, error) { for key, value := range encryptionConfigSecret.Data { // Not all data keys are plugin configs — the Secret also contains the // encryption-config entry, so skip keys that don't match the pattern. - keyID, found, err := kms.KeyIDFromPluginConfigSecretDataKey(key) + keyID, found, err := keyIDFromPluginConfigSecretDataKey(key) if err != nil { return nil, fmt.Errorf("failed to extract keyID from data key %s: %w", key, err) } @@ -56,7 +84,26 @@ func FromSecret(encryptionConfigSecret *corev1.Secret) (*Config, error) { kmsPlugins[keyID] = pluginConfig } - return &Config{Encryption: encryptionConfig, KMSPlugins: kmsPlugins}, nil + // Extract secret data entries from the encryption-config Secret. + // Data keys follow the format "kms-plugin-secret-{secretName}_{dataKey}-{keyID}" + // (e.g. "kms-plugin-secret-app-role_role-id-1"). keyIDFromSecretDataKey + // returns the keyID (e.g. "1") and the combined key (e.g. "app-role_role-id"), + // which is then split on "_" to recover secretName and dataKey. + var kmsPluginsSecretData KMSPluginsSecretData + for key, value := range encryptionConfigSecret.Data { + keyID, rawKey, found, err := parseSecretDataKey(key) + if err != nil { + return nil, fmt.Errorf("failed to parse secret data key %s: %w", key, err) + } + if !found { + continue + } + if err := kmsPluginsSecretData.SetFromRawKey(keyID, rawKey, value); err != nil { + return nil, fmt.Errorf("failed to set key %s: %w", key, err) + } + } + + return &Config{Encryption: encryptionConfig, KMSPlugins: kmsPlugins, KMSPluginsSecretData: kmsPluginsSecretData}, nil } func ToSecret(ns, name string, secretData *Config) (*corev1.Secret, error) { @@ -93,13 +140,22 @@ func ToSecret(ns, name string, secretData *Config) (*corev1.Secret, error) { if err != nil { return nil, fmt.Errorf("failed to encode KMS plugin config for key %s: %w", keyID, err) } - dataKey, err := kms.ToPluginConfigSecretDataKeyFor(keyID) + dataKey, err := toPluginConfigSecretDataKeyFor(keyID) if err != nil { return nil, err } s.Data[dataKey] = encodedPlugin } + // Write secret data entries to the encryption-config Secret. + // Each entry from FlatEntries() (e.g. "app-role_role-id") is combined with the keyID + // (e.g. "1") to produce "kms-plugin-secret-app-role_role-id-1". + for keyID, flatEntries := range secretData.KMSPluginsSecretData.FlatEntriesByKeyID() { + for flatKey, value := range flatEntries { + s.Data[encryptionConfigSecretDataPrefix+flatKey+"-"+keyID] = value + } + } + return s, nil } @@ -145,3 +201,19 @@ func ExtractUniqueAndSortedKMSConfigurations(secretData *Config) ([]*apiserverco }) return result, nil } + +func parseSecretDataKey(dataKey string) (keyID, rawKey string, found bool, err error) { + rest, found := strings.CutPrefix(dataKey, encryptionConfigSecretDataPrefix) + if !found { + return "", "", false, nil + } + i := strings.LastIndex(rest, "-") + if i < 1 { + return "", "", false, nil + } + keyID = rest[i+1:] + if _, err := strconv.ParseUint(keyID, 10, 64); err != nil { + return "", "", false, fmt.Errorf("invalid keyID %q: must be a non-negative integer", keyID) + } + return keyID, rest[:i], true, nil +} diff --git a/vendor/github.com/openshift/library-go/pkg/operator/encryption/kms/helpers.go b/vendor/github.com/openshift/library-go/pkg/operator/encryption/kms/helpers.go index 8a87f83e6b..c5031ce2ab 100644 --- a/vendor/github.com/openshift/library-go/pkg/operator/encryption/kms/helpers.go +++ b/vendor/github.com/openshift/library-go/pkg/operator/encryption/kms/helpers.go @@ -2,38 +2,12 @@ package kms import ( "fmt" - "strconv" - "strings" "github.com/openshift/api/features" "github.com/openshift/library-go/pkg/operator/configobserver/featuregates" corev1 "k8s.io/api/core/v1" ) -const pluginConfigDataKeyPrefix = "kms-plugin-config-" - -// ToPluginConfigSecretDataKeyFor constructs the data key for storing a KMS plugin config in the encryption-config Secret. -// The keyID must be a valid non-negative integer string. -func ToPluginConfigSecretDataKeyFor(keyID string) (string, error) { - if _, err := strconv.ParseUint(keyID, 10, 64); err != nil { - return "", fmt.Errorf("invalid keyID %q: must be a non-negative integer", keyID) - } - return pluginConfigDataKeyPrefix + keyID, nil -} - -// KeyIDFromPluginConfigSecretDataKey extracts the keyID from a kms-plugin-config data key. -// Returns the keyID and true if the key matches the "kms-plugin-config-" pattern. -func KeyIDFromPluginConfigSecretDataKey(dataKey string) (string, bool, error) { - keyID, found := strings.CutPrefix(dataKey, pluginConfigDataKeyPrefix) - if !found || len(keyID) == 0 { - return "", false, nil - } - if _, err := strconv.ParseUint(keyID, 10, 64); err != nil { - return "", false, fmt.Errorf("invalid keyID %q: must be a non-negative integer", keyID) - } - return keyID, true, nil -} - // AddKMSPluginVolumeAndMountToPodSpec conditionally adds the KMS plugin volume mount to the specified container. // It assumes the pod spec does not already contain the KMS volume or mount; no deduplication is performed. // Deprecated: this is a temporary solution to get KMS TP v1 out. We should come up with a different approach afterwards. diff --git a/vendor/github.com/openshift/library-go/pkg/operator/encryption/secrets/secrets.go b/vendor/github.com/openshift/library-go/pkg/operator/encryption/secrets/secrets.go index e2e59efd6d..341cabbd80 100644 --- a/vendor/github.com/openshift/library-go/pkg/operator/encryption/secrets/secrets.go +++ b/vendor/github.com/openshift/library-go/pkg/operator/encryption/secrets/secrets.go @@ -6,6 +6,7 @@ import ( "encoding/json" "fmt" "strconv" + "strings" "time" corev1 "k8s.io/api/core/v1" @@ -76,16 +77,25 @@ func ToKeyState(s *corev1.Secret) (state.KeyState, error) { // encryption mode. return state.KeyState{}, fmt.Errorf("%s can not be empty, when mode is KMS", EncryptionSecretKMSEncryptionConfig) } - if v, ok := s.Data[EncryptionSecretKMSPluginConfig]; ok && len(v) > 0 { + if v, ok := s.Data[encryptionSecretKMSPluginConfig]; ok && len(v) > 0 { kmsConfig, err := encoding.DecodeKMSPluginConfig(v) if err != nil { - return state.KeyState{}, fmt.Errorf("secret %s/%s has invalid %s data: %w", s.Namespace, s.Name, EncryptionSecretKMSPluginConfig, err) + return state.KeyState{}, fmt.Errorf("secret %s/%s has invalid %s data: %w", s.Namespace, s.Name, encryptionSecretKMSPluginConfig, err) } key.KMS.Plugin = kmsConfig } else { // encryption.apiserver.operator.openshift.io-kms-plugin-config data field is required for KMS // encryption mode. - return state.KeyState{}, fmt.Errorf("%s can not be empty, when mode is KMS", EncryptionSecretKMSPluginConfig) + return state.KeyState{}, fmt.Errorf("%s can not be empty, when mode is KMS", encryptionSecretKMSPluginConfig) + } + for dataKey, value := range s.Data { + rawKey, found := strings.CutPrefix(dataKey, encryptionSecretKMSSecretDataPrefix) + if !found { + continue + } + if err := key.KMS.PluginSecretData.SetFromRawKey(rawKey, value); err != nil { + return state.KeyState{}, fmt.Errorf("secret %s/%s has malformed secret data key %q: %w", s.Namespace, s.Name, dataKey, err) + } } key.Mode = keyMode default: @@ -106,7 +116,7 @@ func FromKeyState(component string, ks state.KeyState) (*corev1.Secret, error) { } if ks.Mode == state.KMS && (!ks.HasKMSEncryption() || !ks.HasKMSPlugin()) { - return nil, fmt.Errorf("%s or %s can not be empty, when mode is KMS", EncryptionSecretKMSEncryptionConfig, EncryptionSecretKMSPluginConfig) + return nil, fmt.Errorf("%s or %s can not be empty, when mode is KMS", EncryptionSecretKMSEncryptionConfig, encryptionSecretKMSPluginConfig) } s := &corev1.Secret{ @@ -156,7 +166,13 @@ func FromKeyState(component string, ks state.KeyState) (*corev1.Secret, error) { if err != nil { return nil, err } - s.Data[EncryptionSecretKMSPluginConfig] = pluginData + s.Data[encryptionSecretKMSPluginConfig] = pluginData + } + + if ks.HasKMSSecretData() { + for flatKey, value := range ks.KMS.PluginSecretData.FlatEntries() { + s.Data[encryptionSecretKMSSecretDataPrefix+flatKey] = value + } } return s, nil diff --git a/vendor/github.com/openshift/library-go/pkg/operator/encryption/secrets/types.go b/vendor/github.com/openshift/library-go/pkg/operator/encryption/secrets/types.go index 2795afbc0d..bf152dee31 100644 --- a/vendor/github.com/openshift/library-go/pkg/operator/encryption/secrets/types.go +++ b/vendor/github.com/openshift/library-go/pkg/operator/encryption/secrets/types.go @@ -54,9 +54,14 @@ const ( // encryption configuration for KMS mode in the encryption-key secret. EncryptionSecretKMSEncryptionConfig = "encryption.apiserver.operator.openshift.io-kms-encryption-config" - // EncryptionSecretKMSPluginConfig is the data field key that stores the serialized KMS plugin + // encryptionSecretKMSPluginConfig is the data field key that stores the serialized KMS plugin // configuration for KMS mode in the encryption-key secret. - EncryptionSecretKMSPluginConfig = "encryption.apiserver.operator.openshift.io-kms-plugin-config" + encryptionSecretKMSPluginConfig = "encryption.apiserver.operator.openshift.io-kms-plugin-config" + + // encryptionSecretKMSSecretDataPrefix is the data field key prefix for secret data values + // fetched from the referenced secret in openshift-config. The full data key is + // constructed as prefix + secretName + separator + dataKey. + encryptionSecretKMSSecretDataPrefix = "encryption.apiserver.operator.openshift.io-kms-plugin-secret-" ) // MigratedGroupResources is the data structured stored in the diff --git a/vendor/github.com/openshift/library-go/pkg/operator/encryption/state/types.go b/vendor/github.com/openshift/library-go/pkg/operator/encryption/state/types.go index acaf493377..b7163c41e7 100644 --- a/vendor/github.com/openshift/library-go/pkg/operator/encryption/state/types.go +++ b/vendor/github.com/openshift/library-go/pkg/operator/encryption/state/types.go @@ -1,6 +1,8 @@ package state import ( + "fmt" + "strings" "time" configv1 "github.com/openshift/api/config/v1" @@ -15,6 +17,11 @@ const ( KubernetesDescriptionScaryValue = `WARNING: DO NOT EDIT. Altering of the encryption secrets will render you cluster inaccessible. Catastrophic data loss can occur from the most minor changes.` + + // secretDataKeySeparator separates the secret name from the data key. + // Underscore is used because it is forbidden in Kubernetes secret/configmap + // names, preventing collisions. + secretDataKeySeparator = "_" ) // GroupResourceState represents, for a single group resource, the write and read keys in a @@ -53,6 +60,10 @@ func (k *KeyState) HasKMSPlugin() bool { return k != nil && k.KMS != nil && k.KMS.Plugin != (configv1.KMSPluginConfig{}) } +func (k *KeyState) HasKMSSecretData() bool { + return k != nil && k.KMS != nil && len(k.KMS.PluginSecretData.Entries) > 0 +} + // KMSState stores all KMS encryption mode related configurations type KMSState struct { // Encoded EncryptionConfig that stores the KMS related fields @@ -60,6 +71,58 @@ type KMSState struct { // Plugin stores KMS plugin specific configurations Plugin configv1.KMSPluginConfig + + // PluginSecretData stores data key-value pairs fetched from referenced secrets. + PluginSecretData KMSSecretData +} + +// KMSSecretData stores data key-value pairs fetched from referenced secrets. +// Entries maps secret names to their data key-value pairs. +type KMSSecretData struct { + Entries map[string]map[string][]byte +} + +func (d *KMSSecretData) Set(secretName, dataKey string, value []byte) error { + if len(secretName) == 0 || len(dataKey) == 0 || len(value) == 0 { + return fmt.Errorf("secretName, dataKey, and value must not be empty") + } + if strings.Contains(secretName, "_") { + return fmt.Errorf("secret name %q must not contain underscores", secretName) + } + if d.Entries == nil { + d.Entries = map[string]map[string][]byte{} + } + if d.Entries[secretName] == nil { + d.Entries[secretName] = map[string][]byte{} + } + d.Entries[secretName][dataKey] = value + return nil +} + +// SetFromRawKey splits a combined key of the form "secretName_dataKey" +// and stores the value. +func (d *KMSSecretData) SetFromRawKey(rawKey string, value []byte) error { + parts := strings.SplitN(rawKey, secretDataKeySeparator, 2) + if len(parts) != 2 { + return fmt.Errorf("invalid combined key %q: expected format {secretName}%s{dataKey}", rawKey, secretDataKeySeparator) + } + return d.Set(parts[0], parts[1], value) +} + +// FlatEntries returns the stored data as a flat map keyed by "secretName_dataKey". +// "_" separates secretName from dataKey because "_" is forbidden in +// Kubernetes secret names, making the split unambiguous. +func (d *KMSSecretData) FlatEntries() map[string][]byte { + if d.Entries == nil { + return nil + } + result := map[string][]byte{} + for secretName, keys := range d.Entries { + for dataKey, value := range keys { + result[secretName+secretDataKeySeparator+dataKey] = value + } + } + return result } type MigrationState struct { diff --git a/vendor/github.com/openshift/library-go/test/library/encryption/helpers.go b/vendor/github.com/openshift/library-go/test/library/encryption/helpers.go index b32bb13f84..890a52ef3c 100644 --- a/vendor/github.com/openshift/library-go/test/library/encryption/helpers.go +++ b/vendor/github.com/openshift/library-go/test/library/encryption/helpers.go @@ -56,9 +56,8 @@ type EncryptionKeyMeta struct { type UpdateUnsupportedConfigFunc func(raw []byte) error -func SetAndWaitForEncryptionType(t testing.TB, provider EncryptionProvider, defaultTargetGRs []schema.GroupResource, namespace, labelSelector string) ClientSet { +func SetAndWaitForEncryptionType(ctx context.Context, t testing.TB, provider EncryptionProvider, defaultTargetGRs []schema.GroupResource, namespace, labelSelector string) ClientSet { t.Helper() - ctx := context.TODO() t.Logf("Starting encryption e2e test for %q mode", provider.Type) diff --git a/vendor/github.com/openshift/library-go/test/library/encryption/kms/vault.go b/vendor/github.com/openshift/library-go/test/library/encryption/kms/vault.go index 601002754d..5585ec5a2a 100644 --- a/vendor/github.com/openshift/library-go/test/library/encryption/kms/vault.go +++ b/vendor/github.com/openshift/library-go/test/library/encryption/kms/vault.go @@ -42,6 +42,11 @@ var DefaultVaultEncryptionProvider = library.EncryptionProvider{ Setup: ensureDefaultVaultAppRoleSecret, } +var DefaultFakeVaultEncryptionProvider = library.EncryptionProvider{ + APIServerEncryption: DefaultFakeKMSPluginConfig, + Setup: ensureDefaultVaultAppRoleSecret, +} + // DefaultVaultKMSPluginConfig is the standard Vault KMS encryption config // used by CI e2e tests. var DefaultVaultKMSPluginConfig = configv1.APIServerEncryption{ @@ -65,18 +70,22 @@ var DefaultVaultKMSPluginConfig = configv1.APIServerEncryption{ } // DefaultFakeKMSPluginConfig is a fake Vault KMS configuration used by unit tests. -var DefaultFakeKMSPluginConfig = configv1.KMSPluginConfig{ - Type: configv1.VaultKMSProvider, - Vault: configv1.VaultKMSPluginConfig{ - KMSPluginImage: WellKnownUpstreamMockKMSPluginImage, - VaultAddress: "https://vault.example.com", - Authentication: configv1.VaultAuthentication{ - Type: configv1.VaultAuthenticationTypeAppRole, - AppRole: configv1.VaultAppRoleAuthentication{ - Secret: configv1.VaultSecretReference{Name: "vault-approle-secret"}, +var DefaultFakeKMSPluginConfig = configv1.APIServerEncryption{ + Type: configv1.EncryptionTypeKMS, + KMS: configv1.KMSPluginConfig{ + Type: configv1.VaultKMSProvider, + Vault: configv1.VaultKMSPluginConfig{ + KMSPluginImage: WellKnownUpstreamMockKMSPluginImage, + VaultAddress: "https://vault.example.com", + Authentication: configv1.VaultAuthentication{ + Type: configv1.VaultAuthenticationTypeAppRole, + AppRole: configv1.VaultAppRoleAuthentication{ + Secret: configv1.VaultSecretReference{Name: defaultVaultAppRoleSecretName}, + }, }, + TransitKey: "test-transit-key", + TransitMount: defaultVaultTransitMount, }, - TransitKey: "test-transit-key", }, } @@ -97,8 +106,8 @@ func ensureDefaultVaultAppRoleSecret(ctx context.Context, t testing.TB) { }, Type: corev1.SecretTypeOpaque, Data: map[string][]byte{ - "roleID": creds.Data["role-id"], - "secretID": creds.Data["secret-id"], + "role-id": creds.Data["role-id"], + "secret-id": creds.Data["secret-id"], }, } recorder := events.NewInMemoryRecorder("vault-approle-secret-setup", clock.RealClock{}) diff --git a/vendor/github.com/openshift/library-go/test/library/encryption/perf_scenarios.go b/vendor/github.com/openshift/library-go/test/library/encryption/perf_scenarios.go index c45a28e26e..9e3a7cebfd 100644 --- a/vendor/github.com/openshift/library-go/test/library/encryption/perf_scenarios.go +++ b/vendor/github.com/openshift/library-go/test/library/encryption/perf_scenarios.go @@ -1,6 +1,7 @@ package encryption import ( + "context" "testing" "time" @@ -22,13 +23,13 @@ type PerfScenario struct { EncryptionProvider EncryptionProvider } -func TestPerfEncryption(t *testing.T, scenario PerfScenario) { +func TestPerfEncryption(ctx context.Context, t *testing.T, scenario PerfScenario) { e := NewE(t, PrintEventsOnFailure(scenario.OperatorNamespace)) migrationStartedCh := make(chan time.Time, 1) populateDatabase(e, scenario.DBLoaderWorkers, scenario.DBLoaderFunc, scenario.AssertDBPopulatedFunc) watchForMigrationControllerProgressingConditionAsync(e, scenario.GetOperatorConditionsFunc, migrationStartedCh) - endTimeStamp := runTestEncryption(t, scenario) + endTimeStamp := runTestEncryption(ctx, t, scenario) select { case migrationStarted := <-migrationStartedCh: @@ -38,9 +39,9 @@ func TestPerfEncryption(t *testing.T, scenario PerfScenario) { } } -func runTestEncryption(tt *testing.T, scenario PerfScenario) time.Time { +func runTestEncryption(ctx context.Context, tt *testing.T, scenario PerfScenario) time.Time { var ts time.Time - TestEncryptionType(tt, BasicScenario{ + TestEncryptionType(ctx, tt, BasicScenario{ Namespace: scenario.Namespace, LabelSelector: scenario.LabelSelector, EncryptionConfigSecretName: scenario.EncryptionConfigSecretName, diff --git a/vendor/github.com/openshift/library-go/test/library/encryption/scenarios.go b/vendor/github.com/openshift/library-go/test/library/encryption/scenarios.go index 8d8c62c937..dd1a3fcc4b 100644 --- a/vendor/github.com/openshift/library-go/test/library/encryption/scenarios.go +++ b/vendor/github.com/openshift/library-go/test/library/encryption/scenarios.go @@ -31,20 +31,18 @@ type BasicScenario struct { type EncryptionProvider struct { configv1.APIServerEncryption // Setup is called once before the provider is first used. May be nil. - // Context is accepted as an explicit argument because testing.TB.Context() - // is not supported by all implementations (e.g. Ginkgo's GinkgoTBWrapper). Setup func(ctx context.Context, t testing.TB) } -func TestEncryptionTypeIdentity(t testing.TB, scenario BasicScenario) { +func TestEncryptionTypeIdentity(ctx context.Context, t testing.TB, scenario BasicScenario) { e := NewE(t, PrintEventsOnFailure(scenario.OperatorNamespace)) - clientSet := SetAndWaitForEncryptionType(e, EncryptionProvider{APIServerEncryption: configv1.APIServerEncryption{Type: configv1.EncryptionTypeIdentity}}, scenario.TargetGRs, scenario.Namespace, scenario.LabelSelector) + clientSet := SetAndWaitForEncryptionType(ctx, e, EncryptionProvider{APIServerEncryption: configv1.APIServerEncryption{Type: configv1.EncryptionTypeIdentity}}, scenario.TargetGRs, scenario.Namespace, scenario.LabelSelector) scenario.AssertFunc(e, clientSet, configv1.EncryptionTypeIdentity, scenario.Namespace, scenario.LabelSelector) } -func TestEncryptionTypeUnset(t testing.TB, scenario BasicScenario) { +func TestEncryptionTypeUnset(ctx context.Context, t testing.TB, scenario BasicScenario) { e := NewE(t, PrintEventsOnFailure(scenario.OperatorNamespace)) - clientSet := SetAndWaitForEncryptionType(e, EncryptionProvider{}, scenario.TargetGRs, scenario.Namespace, scenario.LabelSelector) + clientSet := SetAndWaitForEncryptionType(ctx, e, EncryptionProvider{}, scenario.TargetGRs, scenario.Namespace, scenario.LabelSelector) scenario.AssertFunc(e, clientSet, configv1.EncryptionTypeIdentity, scenario.Namespace, scenario.LabelSelector) } @@ -59,40 +57,40 @@ func resolveProvider(t testing.TB, defaultType configv1.EncryptionType, provider return EncryptionProvider{APIServerEncryption: configv1.APIServerEncryption{Type: defaultType}} } -func TestEncryptionTypeAESCBC(t testing.TB, scenario BasicScenario, providers ...EncryptionProvider) { +func TestEncryptionTypeAESCBC(ctx context.Context, t testing.TB, scenario BasicScenario, providers ...EncryptionProvider) { provider := resolveProvider(t, configv1.EncryptionTypeAESCBC, providers) e := NewE(t, PrintEventsOnFailure(scenario.OperatorNamespace)) - clientSet := SetAndWaitForEncryptionType(e, provider, scenario.TargetGRs, scenario.Namespace, scenario.LabelSelector) + clientSet := SetAndWaitForEncryptionType(ctx, e, provider, scenario.TargetGRs, scenario.Namespace, scenario.LabelSelector) scenario.AssertFunc(e, clientSet, provider.Type, scenario.Namespace, scenario.LabelSelector) AssertEncryptionConfig(e, clientSet, scenario.EncryptionConfigSecretName, scenario.EncryptionConfigSecretNamespace, scenario.TargetGRs) } -func TestEncryptionTypeAESGCM(t testing.TB, scenario BasicScenario, providers ...EncryptionProvider) { +func TestEncryptionTypeAESGCM(ctx context.Context, t testing.TB, scenario BasicScenario, providers ...EncryptionProvider) { provider := resolveProvider(t, configv1.EncryptionTypeAESGCM, providers) e := NewE(t, PrintEventsOnFailure(scenario.OperatorNamespace)) - clientSet := SetAndWaitForEncryptionType(e, provider, scenario.TargetGRs, scenario.Namespace, scenario.LabelSelector) + clientSet := SetAndWaitForEncryptionType(ctx, e, provider, scenario.TargetGRs, scenario.Namespace, scenario.LabelSelector) scenario.AssertFunc(e, clientSet, provider.Type, scenario.Namespace, scenario.LabelSelector) AssertEncryptionConfig(e, clientSet, scenario.EncryptionConfigSecretName, scenario.EncryptionConfigSecretNamespace, scenario.TargetGRs) } -func TestEncryptionTypeKMS(t testing.TB, scenario BasicScenario, providers ...EncryptionProvider) { +func TestEncryptionTypeKMS(ctx context.Context, t testing.TB, scenario BasicScenario, providers ...EncryptionProvider) { provider := resolveProvider(t, configv1.EncryptionTypeKMS, providers) e := NewE(t, PrintEventsOnFailure(scenario.OperatorNamespace)) - clientSet := SetAndWaitForEncryptionType(e, provider, scenario.TargetGRs, scenario.Namespace, scenario.LabelSelector) + clientSet := SetAndWaitForEncryptionType(ctx, e, provider, scenario.TargetGRs, scenario.Namespace, scenario.LabelSelector) scenario.AssertFunc(e, clientSet, provider.Type, scenario.Namespace, scenario.LabelSelector) AssertEncryptionConfig(e, clientSet, scenario.EncryptionConfigSecretName, scenario.EncryptionConfigSecretNamespace, scenario.TargetGRs) } -func TestEncryptionType(t testing.TB, scenario BasicScenario, provider EncryptionProvider) { +func TestEncryptionType(ctx context.Context, t testing.TB, scenario BasicScenario, provider EncryptionProvider) { switch provider.Type { case configv1.EncryptionTypeAESCBC: - TestEncryptionTypeAESCBC(t, scenario, provider) + TestEncryptionTypeAESCBC(ctx, t, scenario, provider) case configv1.EncryptionTypeAESGCM: - TestEncryptionTypeAESGCM(t, scenario, provider) + TestEncryptionTypeAESGCM(ctx, t, scenario, provider) case configv1.EncryptionTypeKMS: - TestEncryptionTypeKMS(t, scenario, provider) + TestEncryptionTypeKMS(ctx, t, scenario, provider) case configv1.EncryptionTypeIdentity, "": - TestEncryptionTypeIdentity(t, scenario) + TestEncryptionTypeIdentity(ctx, t, scenario) default: t.Fatalf("Unknown encryption type: %s", provider.Type) } @@ -113,28 +111,28 @@ type testStep struct { testFunc func(testing.TB) } -func TestEncryptionTurnOnAndOff(t testing.TB, scenario OnOffScenario) { +func TestEncryptionTurnOnAndOff(ctx context.Context, t testing.TB, scenario OnOffScenario) { scenarios := []testStep{ {name: fmt.Sprintf("CreateAndStore%s", scenario.ResourceName), testFunc: func(t testing.TB) { e := NewE(t) scenario.CreateResourceFunc(e, GetClients(e), scenario.Namespace) }}, - {name: fmt.Sprintf("On%s", strings.ToUpper(string(scenario.EncryptionProvider.Type))), testFunc: func(t testing.TB) { TestEncryptionType(t, scenario.BasicScenario, scenario.EncryptionProvider) }}, + {name: fmt.Sprintf("On%s", strings.ToUpper(string(scenario.EncryptionProvider.Type))), testFunc: func(t testing.TB) { TestEncryptionType(ctx, t, scenario.BasicScenario, scenario.EncryptionProvider) }}, {name: fmt.Sprintf("Assert%sEncrypted", scenario.ResourceName), testFunc: func(t testing.TB) { e := NewE(t) scenario.AssertResourceEncryptedFunc(e, GetClients(e), scenario.ResourceFunc(e, scenario.Namespace)) }}, - {name: "OffIdentity", testFunc: func(t testing.TB) { TestEncryptionTypeIdentity(t, scenario.BasicScenario) }}, + {name: "OffIdentity", testFunc: func(t testing.TB) { TestEncryptionTypeIdentity(ctx, t, scenario.BasicScenario) }}, {name: fmt.Sprintf("Assert%sNotEncrypted", scenario.ResourceName), testFunc: func(t testing.TB) { e := NewE(t) scenario.AssertResourceNotEncryptedFunc(e, GetClients(e), scenario.ResourceFunc(e, scenario.Namespace)) }}, - {name: fmt.Sprintf("On%sSecond", strings.ToUpper(string(scenario.EncryptionProvider.Type))), testFunc: func(t testing.TB) { TestEncryptionType(t, scenario.BasicScenario, scenario.EncryptionProvider) }}, + {name: fmt.Sprintf("On%sSecond", strings.ToUpper(string(scenario.EncryptionProvider.Type))), testFunc: func(t testing.TB) { TestEncryptionType(ctx, t, scenario.BasicScenario, scenario.EncryptionProvider) }}, {name: fmt.Sprintf("Assert%sEncryptedSecond", scenario.ResourceName), testFunc: func(t testing.TB) { e := NewE(t) scenario.AssertResourceEncryptedFunc(e, GetClients(e), scenario.ResourceFunc(e, scenario.Namespace)) }}, - {name: "OffIdentitySecond", testFunc: func(t testing.TB) { TestEncryptionTypeIdentity(t, scenario.BasicScenario) }}, + {name: "OffIdentitySecond", testFunc: func(t testing.TB) { TestEncryptionTypeIdentity(ctx, t, scenario.BasicScenario) }}, {name: fmt.Sprintf("Assert%sNotEncryptedSecond", scenario.ResourceName), testFunc: func(t testing.TB) { e := NewE(t) scenario.AssertResourceNotEncryptedFunc(e, GetClients(e), scenario.ResourceFunc(e, scenario.Namespace)) @@ -184,7 +182,7 @@ func ShuffleEncryptionProviders(providers []EncryptionProvider) []EncryptionProv // It creates a resource, migrates through each provider, // verifies the resource is encrypted after each migration, and finally // switches to identity (off). -func TestEncryptionProvidersMigration(t testing.TB, scenario ProvidersMigrationScenario) { +func TestEncryptionProvidersMigration(ctx context.Context, t testing.TB, scenario ProvidersMigrationScenario) { if len(scenario.EncryptionProviders) < 2 { t.Fatalf("ProvidersMigrationScenario requires at least 2 encryption providers, got %d", len(scenario.EncryptionProviders)) } @@ -211,7 +209,7 @@ func TestEncryptionProvidersMigration(t testing.TB, scenario ProvidersMigrationS } scenarios = append(scenarios, testStep{name: fmt.Sprintf("%s%s", prefix, strings.ToUpper(string(provider.Type))), testFunc: func(t testing.TB) { - TestEncryptionType(t, scenario.BasicScenario, provider) + TestEncryptionType(ctx, t, scenario.BasicScenario, provider) }}, testStep{name: fmt.Sprintf("Assert%sEncrypted", scenario.ResourceName), testFunc: func(t testing.TB) { e := NewE(t) @@ -222,7 +220,7 @@ func TestEncryptionProvidersMigration(t testing.TB, scenario ProvidersMigrationS // step 3: switch to identity (off) to verify the resource is re-written unencrypted scenarios = append(scenarios, testStep{name: fmt.Sprintf("OffIdentityAndAssert%sNotEncrypted", scenario.ResourceName), testFunc: func(t testing.TB) { - TestEncryptionTypeIdentity(t, scenario.BasicScenario) + TestEncryptionTypeIdentity(ctx, t, scenario.BasicScenario) e := NewE(t) scenario.AssertResourceNotEncryptedFunc(e, GetClients(e), scenario.ResourceFunc(e, scenario.Namespace)) }}) @@ -248,7 +246,7 @@ type RotationScenario struct { // TestEncryptionRotation first encrypts data with aescbc key // then it forces a key rotation by setting the "encyrption.Reason" in the operator's configuration file -func TestEncryptionRotation(t testing.TB, scenario RotationScenario) { +func TestEncryptionRotation(ctx context.Context, t testing.TB, scenario RotationScenario) { // test data ns := scenario.Namespace labelSelector := scenario.LabelSelector @@ -259,7 +257,7 @@ func TestEncryptionRotation(t testing.TB, scenario RotationScenario) { scenario.CreateResourceFunc(e, GetClients(e), ns) // step 2: run provided encryption scenario - TestEncryptionType(t, scenario.BasicScenario, scenario.EncryptionProvider) + TestEncryptionType(ctx, t, scenario.BasicScenario, scenario.EncryptionProvider) // step 3: take samples rawEncryptedResourceWithKey1 := scenario.GetRawResourceFunc(e, clientSet, ns) diff --git a/vendor/modules.txt b/vendor/modules.txt index d836054a00..f5f9042c61 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -407,7 +407,7 @@ github.com/openshift/client-go/security/informers/externalversions/internalinter github.com/openshift/client-go/security/informers/externalversions/security github.com/openshift/client-go/security/informers/externalversions/security/v1 github.com/openshift/client-go/security/listers/security/v1 -# github.com/openshift/library-go v0.0.0-20260520123929-8dbb42ebf1e9 +# github.com/openshift/library-go v0.0.0-20260520123929-8dbb42ebf1e9 => github.com/bertinatto/library-go v0.0.0-20260521212646-57fab52e2504 ## explicit; go 1.25.0 github.com/openshift/library-go/pkg/apiserver/jsonpatch github.com/openshift/library-go/pkg/assets @@ -1699,3 +1699,4 @@ sigs.k8s.io/structured-merge-diff/v6/value ## explicit; go 1.22 sigs.k8s.io/yaml # github.com/onsi/ginkgo/v2 => github.com/openshift/onsi-ginkgo/v2 v2.6.1-0.20251001123353-fd5b1fb35db1 +# github.com/openshift/library-go => github.com/bertinatto/library-go v0.0.0-20260521212646-57fab52e2504