@@ -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,98 @@ 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+ changedKeys = true
1387+ } else if rotatedAt .AddDate (0 , 0 , duration ).Before (now ) {
1388+ secret .Data [extraKey ] = secret .Data ["FernetKeys0" ]
1389+ secret .Data ["FernetKeys0" ] = []byte (keystone .GenerateFernetKey ())
1390+ }
1391+
1392+ //
1393+ // Remove extra keys when FernetMaxActiveKeys changes
1394+ //
1395+ for {
1396+ _ , exists := secret .Data [extraKey ]
1397+ if ! exists {
1398+ break
1399+ }
1400+ changedKeys = true
1401+ i := 1
1402+ for {
1403+ key := fmt .Sprintf ("FernetKeys%d" , i )
1404+ i ++
1405+ nextKey := fmt .Sprintf ("FernetKeys%d" , i )
1406+ _ , exists = secret .Data [nextKey ]
1407+ if ! exists {
1408+ break
1409+ }
1410+ secret .Data [key ] = secret .Data [nextKey ]
1411+ delete (secret .Data , nextKey )
1412+ }
1413+ }
1414+
1415+ //
1416+ // Add extra keys when FernetMaxActiveKeys changes
1417+ //
1418+ lastKey := fmt .Sprintf ("FernetKeys%d" , numberKeys - 1 )
1419+ for {
1420+ _ , exists := secret .Data [lastKey ]
1421+ if exists {
1422+ break
1423+ }
1424+ changedKeys = true
1425+ i := 1
1426+ nextKeyValue := []byte (keystone .GenerateFernetKey ())
1427+ for {
1428+ key := fmt .Sprintf ("FernetKeys%d" , i )
1429+ i ++
1430+ keyValue , exists := secret .Data [key ]
1431+ secret .Data [key ] = nextKeyValue
1432+ nextKeyValue = keyValue
1433+ if ! exists {
1434+ break
1435+ }
1436+ }
1437+ }
1438+
1439+ if ! changedKeys {
1440+ return nil
1441+ }
1442+
1443+ fernetKeys := make (map [string ]string , len (secret .Data ))
1444+ for k , v := range secret .Data {
1445+ fernetKeys [k ] = string (v [:])
1446+ }
1447+
1448+ secret .Annotations [fernetAnnotation ] = now .Format (time .RFC3339 )
1449+
1450+ tmpl := []util.Template {
1451+ {
1452+ Name : secretName ,
1453+ Namespace : instance .Namespace ,
1454+ Type : util .TemplateTypeNone ,
1455+ CustomData : fernetKeys ,
1456+ Labels : labels ,
1457+ Annotations : secret .Annotations ,
1458+ },
1459+ }
1460+
1461+ err = oko_secret .EnsureSecrets (ctx , helper , instance , tmpl , envVars )
1462+ if err != nil {
1463+ return err
1464+ }
1465+ }
13671466
13681467 return nil
13691468}
0 commit comments