@@ -857,7 +857,6 @@ func (r *KeystoneAPIReconciler) reconcileNormal(
857857 //
858858 // Create secret holding fernet keys (for token and credential)
859859 //
860- // TODO key rotation
861860 err = r .ensureFernetKeys (ctx , instance , helper , & configMapVars )
862861 if err != nil {
863862 instance .Status .Conditions .Set (condition .FalseCondition (
@@ -1321,37 +1320,53 @@ func (r *KeystoneAPIReconciler) reconcileCloudConfig(
13211320 return oko_secret .EnsureSecrets (ctx , h , instance , secrets , nil )
13221321}
13231322
1324- // ensureFernetKeys - creates secret with fernet keys
1323+ // ensureFernetKeys - creates secret with fernet keys, rotates the keys
13251324func (r * KeystoneAPIReconciler ) ensureFernetKeys (
13261325 ctx context.Context ,
13271326 instance * keystonev1.KeystoneAPI ,
13281327 helper * helper.Helper ,
13291328 envVars * map [string ]env.Setter ,
13301329) error {
1330+ fernetAnnotation := labels .GetGroupLabel (keystone .ServiceName ) + "/rotatedat"
13311331 labels := labels .GetLabels (instance , labels .GetGroupLabel (keystone .ServiceName ), map [string ]string {})
1332+ now := time .Now ().UTC ()
13321333
13331334 //
13341335 // check if secret already exist
13351336 //
13361337 secretName := keystone .ServiceName
1338+ var numberKeys int
1339+ if instance .Spec .FernetMaxActiveKeys == nil {
1340+ numberKeys = keystone .DefaultFernetMaxActiveKeys
1341+ } else {
1342+ numberKeys = int (* instance .Spec .FernetMaxActiveKeys )
1343+ }
1344+
13371345 secret , hash , err := oko_secret .GetSecret (ctx , helper , secretName , instance .Namespace )
1346+
13381347 if err != nil && ! k8s_errors .IsNotFound (err ) {
13391348 return err
13401349 } else if k8s_errors .IsNotFound (err ) {
13411350 fernetKeys := map [string ]string {
1342- "FernetKeys0" : keystone .GenerateFernetKey (),
1343- "FernetKeys1" : keystone .GenerateFernetKey (),
13441351 "CredentialKeys0" : keystone .GenerateFernetKey (),
13451352 "CredentialKeys1" : keystone .GenerateFernetKey (),
13461353 }
13471354
1355+ for i := 0 ; i < numberKeys ; i ++ {
1356+ fernetKeys [fmt .Sprintf ("FernetKeys%d" , i )] = keystone .GenerateFernetKey ()
1357+ }
1358+
1359+ annotations := map [string ]string {
1360+ fernetAnnotation : now .Format (time .RFC3339 )}
1361+
13481362 tmpl := []util.Template {
13491363 {
1350- Name : secretName ,
1351- Namespace : instance .Namespace ,
1352- Type : util .TemplateTypeNone ,
1353- CustomData : fernetKeys ,
1354- Labels : labels ,
1364+ Name : secretName ,
1365+ Namespace : instance .Namespace ,
1366+ Type : util .TemplateTypeNone ,
1367+ CustomData : fernetKeys ,
1368+ Labels : labels ,
1369+ Annotations : annotations ,
13551370 },
13561371 }
13571372 err := oko_secret .EnsureSecrets (ctx , helper , instance , tmpl , envVars )
@@ -1361,9 +1376,99 @@ func (r *KeystoneAPIReconciler) ensureFernetKeys(
13611376 } else {
13621377 // add hash to envVars
13631378 (* envVars )[secret .Name ] = env .SetValue (hash )
1364- }
13651379
1366- // TODO: fernet key rotation
1380+ changedKeys := false
1381+
1382+ extraKey := fmt .Sprintf ("FernetKeys%d" , numberKeys )
1383+
1384+ //
1385+ // Fernet Key rotation
1386+ //
1387+ if secret .Annotations == nil {
1388+ secret .Annotations = map [string ]string {}
1389+ }
1390+ rotatedAt , err := time .Parse (time .RFC3339 , secret .Annotations [fernetAnnotation ])
1391+
1392+ var duration int
1393+ if instance .Spec .FernetRotationDays == nil {
1394+ duration = keystone .DefaultFernetRotationDays
1395+ } else {
1396+ duration = int (* instance .Spec .FernetRotationDays )
1397+ }
1398+
1399+ if err != nil {
1400+ changedKeys = true
1401+ } else if rotatedAt .AddDate (0 , 0 , duration ).Before (now ) {
1402+ secret .Data [extraKey ] = secret .Data ["FernetKeys0" ]
1403+ secret .Data ["FernetKeys0" ] = []byte (keystone .GenerateFernetKey ())
1404+ }
1405+
1406+ //
1407+ // Remove extra keys when FernetMaxActiveKeys changes
1408+ //
1409+ for {
1410+ _ , exists := secret .Data [extraKey ]
1411+ if ! exists {
1412+ break
1413+ }
1414+ changedKeys = true
1415+ i := 1
1416+ for {
1417+ key := fmt .Sprintf ("FernetKeys%d" , i )
1418+ i ++
1419+ nextKey := fmt .Sprintf ("FernetKeys%d" , i )
1420+ _ , exists = secret .Data [nextKey ]
1421+ if ! exists {
1422+ break
1423+ }
1424+ secret .Data [key ] = secret .Data [nextKey ]
1425+ delete (secret .Data , nextKey )
1426+ }
1427+ }
1428+
1429+ //
1430+ // Add extra keys when FernetMaxActiveKeys changes
1431+ //
1432+ lastKey := fmt .Sprintf ("FernetKeys%d" , numberKeys - 1 )
1433+ for {
1434+ _ , exists := secret .Data [lastKey ]
1435+ if exists {
1436+ break
1437+ }
1438+ changedKeys = true
1439+ i := 1
1440+ nextKeyValue := []byte (keystone .GenerateFernetKey ())
1441+ for {
1442+ key := fmt .Sprintf ("FernetKeys%d" , i )
1443+ i ++
1444+ keyValue , exists := secret .Data [key ]
1445+ secret .Data [key ] = nextKeyValue
1446+ nextKeyValue = keyValue
1447+ if ! exists {
1448+ break
1449+ }
1450+ }
1451+ }
1452+
1453+ if ! changedKeys {
1454+ return nil
1455+ }
1456+
1457+ fernetKeys := make (map [string ]string , len (secret .Data ))
1458+ for k , v := range secret .Data {
1459+ fernetKeys [k ] = string (v [:])
1460+ }
1461+
1462+ secret .Annotations [fernetAnnotation ] = now .Format (time .RFC3339 )
1463+
1464+ // use update to apply changes to the secret, since EnsureSecrets
1465+ // does not handle annotation updates, also CreateOrPatchSecret would
1466+ // preserve the existing annotation
1467+ err = helper .GetClient ().Update (ctx , secret , & client.UpdateOptions {})
1468+ if err != nil {
1469+ return err
1470+ }
1471+ }
13671472
13681473 return nil
13691474}
0 commit comments