@@ -10,6 +10,8 @@ import (
1010 "strings"
1111 "time"
1212
13+ configv1 "github.com/openshift/api/config/v1"
14+ "github.com/openshift/library-go/pkg/operator/encryption/kms"
1315 corev1 "k8s.io/api/core/v1"
1416 "k8s.io/apimachinery/pkg/api/errors"
1517 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@@ -39,6 +41,9 @@ import (
3941// greater than the last key's ID (the first key has a key ID of 1).
4042const encryptionSecretMigrationInterval = time .Hour * 24 * 7 // one week
4143
44+ // kmsHashesGetter is a function type for getting KMS config and key ID hashes
45+ var kmsHashesGetterFunc func (ctx context.Context , kmsConfig * configv1.KMSConfig ) (configHash string , keyIDHash []byte , err error )
46+
4247// keyController creates new keys if necessary. It
4348// * watches
4449// - secrets in openshift-config-managed
@@ -104,6 +109,8 @@ func NewKeyController(
104109 secretClient : secretClient ,
105110 }
106111
112+ kmsHashesGetterFunc = defaultGetKMSHashes
113+
107114 return factory .New ().
108115 WithSync (c .sync ).
109116 WithControllerInstanceName (c .controllerInstanceName ).
@@ -159,11 +166,21 @@ func (c *keyController) sync(ctx context.Context, syncCtx factory.SyncContext) (
159166}
160167
161168func (c * keyController ) checkAndCreateKeys (ctx context.Context , syncContext factory.SyncContext , encryptedGRs []schema.GroupResource ) error {
162- currentMode , externalReason , err := c .getCurrentModeAndExternalReason (ctx )
169+ currentMode , externalReason , kmsConfig , err := c .getCurrentModeAndExternalReason (ctx )
163170 if err != nil {
164171 return err
165172 }
166173
174+ // Compute KMS hashes if using KMS mode
175+ var kmsConfigHash string
176+ var kmsKeyHash []byte
177+ if currentMode == state .KMS && kmsConfig != nil {
178+ kmsConfigHash , kmsKeyHash , err = kmsHashesGetterFunc (ctx , kmsConfig )
179+ if err != nil {
180+ return err
181+ }
182+ }
183+
167184 currentConfig , desiredEncryptionState , secrets , isProgressingReason , err := statemachine .GetEncryptionConfigAndState (ctx , c .deployer , c .secretClient , c .encryptionSecretSelector , encryptedGRs )
168185 if err != nil {
169186 return err
@@ -191,7 +208,7 @@ func (c *keyController) checkAndCreateKeys(ctx context.Context, syncContext fact
191208
192209 var commonReason * string
193210 for gr , grKeys := range desiredEncryptionState {
194- latestKeyID , internalReason , needed := needsNewKey (grKeys , currentMode , externalReason , encryptedGRs )
211+ latestKeyID , internalReason , needed := needsNewKey (grKeys , currentMode , externalReason , encryptedGRs , kmsKeyHash )
195212 if ! needed {
196213 continue
197214 }
@@ -218,7 +235,7 @@ func (c *keyController) checkAndCreateKeys(ctx context.Context, syncContext fact
218235
219236 sort .Sort (sort .StringSlice (reasons ))
220237 internalReason := strings .Join (reasons , ", " )
221- keySecret , err := c .generateKeySecret (newKeyID , currentMode , internalReason , externalReason )
238+ keySecret , err := c .generateKeySecret (newKeyID , currentMode , internalReason , externalReason , kmsConfigHash , kmsKeyHash )
222239 if err != nil {
223240 return fmt .Errorf ("failed to create key: %v" , err )
224241 }
@@ -255,8 +272,8 @@ func (c *keyController) validateExistingSecret(ctx context.Context, keySecret *c
255272 return nil // we made this key earlier
256273}
257274
258- func (c * keyController ) generateKeySecret (keyID uint64 , currentMode state.Mode , internalReason , externalReason string ) (* corev1.Secret , error ) {
259- bs := crypto .ModeToNewKeyFunc [currentMode ]()
275+ func (c * keyController ) generateKeySecret (keyID uint64 , currentMode state.Mode , internalReason , externalReason string , kmsConfigHash string , kmsKeyIDHash [] byte ) (* corev1.Secret , error ) {
276+ bs := crypto.ModeToNewKeyFunc [currentMode ](kmsKeyIDHash )
260277 ks := state.KeyState {
261278 Key : apiserverv1.Key {
262279 Name : fmt .Sprintf ("%d" , keyID ),
@@ -265,40 +282,55 @@ func (c *keyController) generateKeySecret(keyID uint64, currentMode state.Mode,
265282 Mode : currentMode ,
266283 InternalReason : internalReason ,
267284 ExternalReason : externalReason ,
285+ KMSConfigHash : kmsConfigHash ,
268286 }
269287 return secrets .FromKeyState (c .instanceName , ks )
270288}
271289
272- func (c * keyController ) getCurrentModeAndExternalReason (ctx context.Context ) (state.Mode , string , error ) {
290+ // defaultGetKMSHashes is the default implementation of getting KMS hashes
291+ func defaultGetKMSHashes (ctx context.Context , kmsConfig * configv1.KMSConfig ) (string , []byte , error ) {
292+ _ , configHash , err := kms .GenerateUnixSocketPath (kmsConfig )
293+ if err != nil {
294+ return "" , nil , fmt .Errorf ("failed to generate KMS unix socket path: %w" , err )
295+ }
296+
297+ // TODO: We'll also need to obtain keyId from Status endpoint of KMS plugin to track the key rotation.
298+ keyId := "static-key-id"
299+ return configHash , kms .ComputeKMSKeyHash (configHash , keyId ), nil
300+ }
301+
302+ func (c * keyController ) getCurrentModeAndExternalReason (ctx context.Context ) (state.Mode , string , * configv1.KMSConfig , error ) {
273303 apiServer , err := c .apiServerClient .Get (ctx , "cluster" , metav1.GetOptions {})
274304 if err != nil {
275- return "" , "" , err
305+ return "" , "" , nil , err
276306 }
277307
278308 operatorSpec , _ , _ , err := c .operatorClient .GetOperatorState ()
279309 if err != nil {
280- return "" , "" , err
310+ return "" , "" , nil , err
281311 }
282312
283313 encryptionConfig , err := structuredUnsupportedConfigFrom (operatorSpec .UnsupportedConfigOverrides .Raw , c .unsupportedConfigPrefix )
284314 if err != nil {
285- return "" , "" , err
315+ return "" , "" , nil , err
286316 }
287317
288318 reason := encryptionConfig .Encryption .Reason
289319 switch currentMode := state .Mode (apiServer .Spec .Encryption .Type ); currentMode {
290320 case state .AESCBC , state .AESGCM , state .Identity : // secretbox is disabled for now
291- return currentMode , reason , nil
321+ return currentMode , reason , nil , nil
322+ case state .KMS :
323+ return currentMode , reason , apiServer .Spec .Encryption .KMS , nil
292324 case "" : // unspecified means use the default (which can change over time)
293- return state .DefaultMode , reason , nil
325+ return state .DefaultMode , reason , nil , nil
294326 default :
295- return "" , "" , fmt .Errorf ("unknown encryption mode configured: %s" , currentMode )
327+ return "" , "" , nil , fmt .Errorf ("unknown encryption mode configured: %s" , currentMode )
296328 }
297329}
298330
299331// needsNewKey checks whether a new key must be created for the given resource. If true, it also returns the latest
300332// used key ID and a reason string.
301- func needsNewKey (grKeys state.GroupResourceState , currentMode state.Mode , externalReason string , encryptedGRs []schema.GroupResource ) (uint64 , string , bool ) {
333+ func needsNewKey (grKeys state.GroupResourceState , currentMode state.Mode , externalReason string , encryptedGRs []schema.GroupResource , kmsKeyHash [] byte ) (uint64 , string , bool ) {
302334 // we always need to have some encryption keys unless we are turned off
303335 if len (grKeys .ReadKeys ) == 0 {
304336 return 0 , "key-does-not-exist" , currentMode != state .Identity
@@ -346,6 +378,17 @@ func needsNewKey(grKeys state.GroupResourceState, currentMode state.Mode, extern
346378 return latestKeyID , "external-reason-changed" , true
347379 }
348380
381+ // if we are using KMS, check if the KMS configuration or key ID hash has changed
382+ if currentMode == state .KMS {
383+ if latestKey .Key .Secret != base64 .StdEncoding .EncodeToString (kmsKeyHash ) {
384+ return latestKeyID , "kms-key-changed" , true
385+ }
386+
387+ // For KMS mode, we don't do time-based rotation
388+ // KMS keys are rotated externally by the KMS system
389+ return 0 , "" , false
390+ }
391+
349392 // we check for encryptionSecretMigratedTimestamp set by migration controller to determine when migration completed
350393 // this also generates back pressure for key rotation when migration takes a long time or was recently completed
351394 return latestKeyID , "rotation-interval-has-passed" , time .Since (latestKey .Migrated .Timestamp ) > encryptionSecretMigrationInterval
0 commit comments