@@ -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,48 @@ 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+ numberKeys := int (* instance .Spec .FernetMaxActiveKeys )
1339+
13371340 secret , hash , err := oko_secret .GetSecret (ctx , helper , secretName , instance .Namespace )
1341+
13381342 if err != nil && ! k8s_errors .IsNotFound (err ) {
13391343 return err
13401344 } else if k8s_errors .IsNotFound (err ) {
13411345 fernetKeys := map [string ]string {
1342- "FernetKeys0" : keystone .GenerateFernetKey (),
1343- "FernetKeys1" : keystone .GenerateFernetKey (),
13441346 "CredentialKeys0" : keystone .GenerateFernetKey (),
13451347 "CredentialKeys1" : keystone .GenerateFernetKey (),
13461348 }
13471349
1350+ for i := 0 ; i < numberKeys ; i ++ {
1351+ fernetKeys [fmt .Sprintf ("FernetKeys%d" , i )] = keystone .GenerateFernetKey ()
1352+ }
1353+
1354+ annotations := map [string ]string {
1355+ fernetAnnotation : now .Format (time .RFC3339 )}
1356+
13481357 tmpl := []util.Template {
13491358 {
1350- Name : secretName ,
1351- Namespace : instance .Namespace ,
1352- Type : util .TemplateTypeNone ,
1353- CustomData : fernetKeys ,
1354- Labels : labels ,
1359+ Name : secretName ,
1360+ Namespace : instance .Namespace ,
1361+ Type : util .TemplateTypeNone ,
1362+ CustomData : fernetKeys ,
1363+ Labels : labels ,
1364+ Annotations : annotations ,
13551365 },
13561366 }
13571367 err := oko_secret .EnsureSecrets (ctx , helper , instance , tmpl , envVars )
@@ -1361,9 +1371,97 @@ func (r *KeystoneAPIReconciler) ensureFernetKeys(
13611371 } else {
13621372 // add hash to envVars
13631373 (* envVars )[secret .Name ] = env .SetValue (hash )
1364- }
13651374
1366- // TODO: fernet key rotation
1375+ changedKeys := false
1376+
1377+ extraKey := fmt .Sprintf ("FernetKeys%d" , numberKeys )
1378+
1379+ //
1380+ // Fernet Key rotation
1381+ //
1382+ rotatedAt , err := time .Parse (time .RFC3339 , secret .Annotations [fernetAnnotation ])
1383+ duration := int (* instance .Spec .FernetRotationDays )
1384+
1385+ if err != nil {
1386+ secret .Annotations [fernetAnnotation ] = now .Format (time .RFC3339 )
1387+ changedKeys = true
1388+ } else if rotatedAt .AddDate (0 , 0 , duration ).Before (now ) {
1389+ secret .Data [extraKey ] = secret .Data ["FernetKeys0" ]
1390+ secret .Data ["FernetKeys0" ] = []byte (keystone .GenerateFernetKey ())
1391+ }
1392+
1393+ //
1394+ // Remove extra keys when FernetMaxActiveKeys changes
1395+ //
1396+ for {
1397+ _ , exists := secret .Data [extraKey ]
1398+ if ! exists {
1399+ break
1400+ }
1401+ changedKeys = true
1402+ i := 1
1403+ for {
1404+ key := fmt .Sprintf ("FernetKeys%d" , i )
1405+ i ++
1406+ nextKey := fmt .Sprintf ("FernetKeys%d" , i )
1407+ _ , exists = secret .Data [nextKey ]
1408+ if ! exists {
1409+ break
1410+ }
1411+ secret .Data [key ] = secret .Data [nextKey ]
1412+ delete (secret .Data , nextKey )
1413+ }
1414+ }
1415+
1416+ //
1417+ // Add extra keys when FernetMaxActiveKeys changes
1418+ //
1419+ lastKey := fmt .Sprintf ("FernetKeys%d" , numberKeys - 1 )
1420+ for {
1421+ _ , exists := secret .Data [lastKey ]
1422+ if exists {
1423+ break
1424+ }
1425+ changedKeys = true
1426+ i := 1
1427+ nextKeyValue := []byte (keystone .GenerateFernetKey ())
1428+ for {
1429+ key := fmt .Sprintf ("FernetKeys%d" , i )
1430+ i ++
1431+ keyValue , exists := secret .Data [key ]
1432+ secret .Data [key ] = nextKeyValue
1433+ nextKeyValue = keyValue
1434+ if ! exists {
1435+ break
1436+ }
1437+ }
1438+ }
1439+
1440+ if ! changedKeys {
1441+ return nil
1442+ }
1443+
1444+ fernetKeys := make (map [string ]string , len (secret .Data ))
1445+ for k , v := range secret .Data {
1446+ fernetKeys [k ] = string (v [:])
1447+ }
1448+
1449+ tmpl := []util.Template {
1450+ {
1451+ Name : secretName ,
1452+ Namespace : instance .Namespace ,
1453+ Type : util .TemplateTypeNone ,
1454+ CustomData : fernetKeys ,
1455+ Labels : labels ,
1456+ Annotations : secret .Annotations ,
1457+ },
1458+ }
1459+
1460+ err = oko_secret .EnsureSecrets (ctx , helper , instance , tmpl , envVars )
1461+ if err != nil {
1462+ return err
1463+ }
1464+ }
13671465
13681466 return nil
13691467}
0 commit comments