Skip to content

Commit 9a24d85

Browse files
committed
Introduce kms to kms migration
1 parent 7fd5f33 commit 9a24d85

3 files changed

Lines changed: 405 additions & 54 deletions

File tree

pkg/operator/encryption/controllers/key_controller.go

Lines changed: 77 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -201,7 +201,7 @@ func (c *keyController) checkAndCreateKeys(ctx context.Context, syncContext fact
201201

202202
var commonReason *string
203203
for gr, grKeys := range desiredEncryptionState {
204-
latestKeyID, internalReason, needed := needsNewKey(grKeys, currentMode, externalReason, encryptedGRs)
204+
latestKeyID, internalReason, needed := needsNewKey(grKeys, currentMode, externalReason, encryptedGRs, apiEncryptionConfiguration)
205205
if !needed {
206206
continue
207207
}
@@ -265,6 +265,63 @@ func (c *keyController) validateExistingSecret(ctx context.Context, keySecret *c
265265
return nil // we made this key earlier
266266
}
267267

268+
// kmsProviderConfig abstracts provider-specific KMS logic so that every
269+
// provider-type switch lives in a single factory (newKMSProviderConfig).
270+
type kmsProviderConfig interface {
271+
referencedSecretName() (string, []string, error)
272+
referencedConfigMapName() (string, []string, error)
273+
// migrationRequired reports whether switching from latest (stored in
274+
// the key secret) to this provider config requires a new encryption key.
275+
migrationRequired(latest configv1.KMSPluginConfig) bool
276+
}
277+
278+
func newKMSProviderConfig(plugin configv1.KMSPluginConfig) (kmsProviderConfig, error) {
279+
switch plugin.Type {
280+
case configv1.VaultKMSProvider:
281+
return &vaultProviderConfig{plugin.Vault}, nil
282+
default:
283+
return nil, fmt.Errorf("unsupported KMS provider type %q", plugin.Type)
284+
}
285+
}
286+
287+
type vaultProviderConfig struct {
288+
vault configv1.VaultKMSPluginConfig
289+
}
290+
291+
// referencedSecretName returns the name of the secret referenced by the KMS plugin
292+
// config and the specific data keys to carry from that secret. Only the listed keys
293+
// are copied into the Key Secret; any other data in the referenced secret is ignored.
294+
func (v *vaultProviderConfig) referencedSecretName() (string, []string, error) {
295+
switch v.vault.Authentication.Type {
296+
case configv1.VaultAuthenticationTypeAppRole:
297+
// The Vault AppRole secret must contain "role-id" and "secret-id" keys.
298+
// These are the only keys carried into the encryption key secret.
299+
return v.vault.Authentication.AppRole.Secret.Name, []string{"role-id", "secret-id"}, nil
300+
default:
301+
return "", nil, fmt.Errorf("unsupported Vault authentication type %q", v.vault.Authentication.Type)
302+
}
303+
}
304+
305+
// referencedConfigMapName returns the name of the configmap referenced by the KMS plugin
306+
// config and the specific data keys to carry from that configmap. Only the listed keys
307+
// are copied into the Key Secret; any other data in the referenced configmap is ignored.
308+
func (v *vaultProviderConfig) referencedConfigMapName() (string, []string, error) {
309+
if v.vault.TLS.CABundle.Name == "" {
310+
return "", nil, nil
311+
}
312+
return v.vault.TLS.CABundle.Name, []string{"ca-bundle.crt"}, nil
313+
}
314+
315+
func (v *vaultProviderConfig) migrationRequired(latest configv1.KMSPluginConfig) bool {
316+
if latest.Type != configv1.VaultKMSProvider {
317+
return true
318+
}
319+
return v.vault.VaultAddress != latest.Vault.VaultAddress ||
320+
v.vault.VaultNamespace != latest.Vault.VaultNamespace ||
321+
v.vault.TransitMount != latest.Vault.TransitMount ||
322+
v.vault.TransitKey != latest.Vault.TransitKey
323+
}
324+
268325
func (c *keyController) generateKeySecret(ctx context.Context, keyID uint64, currentMode state.Mode, apiServerEncryption configv1.APIServerEncryption, internalReason, externalReason string) (*corev1.Secret, error) {
269326
bs := crypto.ModeToNewKeyFunc[currentMode]()
270327
ks := state.KeyState{
@@ -287,7 +344,12 @@ func (c *keyController) generateKeySecret(ctx context.Context, keyID uint64, cur
287344
Plugin: apiServerEncryption.KMS,
288345
}
289346

290-
if secretName, expectedKeys, err := referencedSecretName(apiServerEncryption.KMS); err != nil {
347+
providerCfg, err := newKMSProviderConfig(apiServerEncryption.KMS)
348+
if err != nil {
349+
return nil, err
350+
}
351+
352+
if secretName, expectedKeys, err := providerCfg.referencedSecretName(); err != nil {
291353
return nil, err
292354
} else if len(secretName) > 0 {
293355
refSecret, err := c.secretClient.Secrets(openshiftConfigNS).Get(ctx, secretName, metav1.GetOptions{})
@@ -305,7 +367,7 @@ func (c *keyController) generateKeySecret(ctx context.Context, keyID uint64, cur
305367
}
306368
}
307369

308-
if cmName, expectedKeys, err := referencedConfigMapName(apiServerEncryption.KMS); err != nil {
370+
if cmName, expectedKeys, err := providerCfg.referencedConfigMapName(); err != nil {
309371
return nil, err
310372
} else if len(cmName) > 0 {
311373
refCM, err := c.configMapClient.ConfigMaps(openshiftConfigNS).Get(ctx, cmName, metav1.GetOptions{})
@@ -358,7 +420,7 @@ func (c *keyController) getCurrentModeReasonAndEncryptionConfig(ctx context.Cont
358420

359421
// needsNewKey checks whether a new key must be created for the given resource. If true, it also returns the latest
360422
// used key ID and a reason string.
361-
func needsNewKey(grKeys state.GroupResourceState, currentMode state.Mode, externalReason string, encryptedGRs []schema.GroupResource) (uint64, string, bool) {
423+
func needsNewKey(grKeys state.GroupResourceState, currentMode state.Mode, externalReason string, encryptedGRs []schema.GroupResource, apiServerEncryption configv1.APIServerEncryption) (uint64, string, bool) {
362424
// we always need to have some encryption keys unless we are turned off
363425
if len(grKeys.ReadKeys) == 0 {
364426
return 0, "key-does-not-exist", currentMode != state.Identity
@@ -403,13 +465,18 @@ func needsNewKey(grKeys state.GroupResourceState, currentMode state.Mode, extern
403465

404466
if currentMode == state.KMS {
405467
// We are here because Encryption Mode is not changed
468+
// However, we need to create a new key if migration-triggering fields
469+
// in the KMS provider configuration have changed.
470+
currentProviderCfg, err := newKMSProviderConfig(apiServerEncryption.KMS)
471+
if err != nil {
472+
return latestKeyID, fmt.Sprintf("unsupported-kms-provider-%s", apiServerEncryption.KMS.Type), true
473+
}
474+
if currentProviderCfg.migrationRequired(latestKey.KMS.Plugin) {
475+
return latestKeyID, "kms-provider-changed", true
476+
}
406477

407-
// For now in Tech Preview v1, we don't support configurational changes. Therefore,
408-
// it is pointless comparing the secrets.
409-
410-
// For KMS mode, we don't do time-based rotation. Therefore, we shortcut here
411-
// KMS keys are rotated externally by the KMS system.
412-
// Moreover, we don't trigger new key when external reason is changed.
478+
// For KMS mode, we don't do time-based rotation. KMS keys are rotated
479+
// externally by the KMS provider. Moreover, we don't trigger new key when external reason is changed.
413480
// Because it would lead to duplicate providers which is not allowed.
414481
return 0, "", false
415482
}
@@ -424,40 +491,6 @@ func needsNewKey(grKeys state.GroupResourceState, currentMode state.Mode, extern
424491
return latestKeyID, "rotation-interval-has-passed", time.Since(latestKey.Migrated.Timestamp) > encryptionSecretMigrationInterval
425492
}
426493

427-
// referencedSecretName returns the name of the secret referenced by the KMS plugin
428-
// config and the specific data keys to carry from that secret. Only the listed keys
429-
// are copied into the Key Secret; any other data in the referenced secret is ignored.
430-
func referencedSecretName(plugin configv1.KMSPluginConfig) (string, []string, error) {
431-
switch plugin.Type {
432-
case configv1.VaultKMSProvider:
433-
switch plugin.Vault.Authentication.Type {
434-
case configv1.VaultAuthenticationTypeAppRole:
435-
// The Vault AppRole secret must contain "role-id" and "secret-id" keys.
436-
// These are the only keys carried into the encryption key secret.
437-
return plugin.Vault.Authentication.AppRole.Secret.Name, []string{"role-id", "secret-id"}, nil
438-
default:
439-
return "", nil, fmt.Errorf("unsupported Vault authentication type %q", plugin.Vault.Authentication.Type)
440-
}
441-
default:
442-
return "", nil, fmt.Errorf("unsupported KMS provider type %q", plugin.Type)
443-
}
444-
}
445-
446-
// referencedConfigMapName returns the name of the configmap referenced by the KMS plugin
447-
// config and the specific data keys to carry from that configmap. Only the listed keys
448-
// are copied into the Key Secret; any other data in the referenced configmap is ignored.
449-
func referencedConfigMapName(plugin configv1.KMSPluginConfig) (string, []string, error) {
450-
switch plugin.Type {
451-
case configv1.VaultKMSProvider:
452-
if plugin.Vault.TLS.CABundle.Name == "" {
453-
return "", nil, nil
454-
}
455-
return plugin.Vault.TLS.CABundle.Name, []string{"ca-bundle.crt"}, nil
456-
default:
457-
return "", nil, fmt.Errorf("unsupported KMS provider type %q", plugin.Type)
458-
}
459-
}
460-
461494
// TODO make this un-settable once set
462495
// ex: we could require the tech preview no upgrade flag to be set before we will honor this field
463496
type unsupportedEncryptionConfig struct {

0 commit comments

Comments
 (0)