Skip to content

Commit 3fb3b34

Browse files
authored
change username in secret when switching rotation mode (zalando#2549)
1 parent e34f19b commit 3fb3b34

File tree

2 files changed

+56
-8
lines changed

2 files changed

+56
-8
lines changed

pkg/cluster/sync.go

+26-7
Original file line numberDiff line numberDiff line change
@@ -949,6 +949,8 @@ func (c *Cluster) rotatePasswordInSecret(
949949
err error
950950
nextRotationDate time.Time
951951
nextRotationDateStr string
952+
expectedUsername string
953+
rotationModeChanged bool
952954
updateSecretMsg string
953955
)
954956

@@ -969,17 +971,32 @@ func (c *Cluster) rotatePasswordInSecret(
969971
nextRotationDate = currentRotationDate
970972
}
971973

974+
// set username and check if it differs from current value in secret
975+
currentUsername := string(secret.Data["username"])
976+
if !slices.Contains(c.Spec.UsersWithInPlaceSecretRotation, secretUsername) {
977+
expectedUsername = fmt.Sprintf("%s%s", secretUsername, currentTime.Format(constants.RotationUserDateFormat))
978+
} else {
979+
expectedUsername = secretUsername
980+
}
981+
982+
// when changing to in-place rotation update secret immediatly
983+
// if currentUsername is longer we know it has a date suffix
984+
// the other way around we can wait until the next rotation date
985+
if len(currentUsername) > len(expectedUsername) {
986+
rotationModeChanged = true
987+
c.logger.Infof("updating secret %s after switching to in-place rotation mode for username: %s", secretName, string(secret.Data["username"]))
988+
}
989+
972990
// update password and next rotation date if configured interval has passed
973-
if currentTime.After(nextRotationDate) {
991+
if currentTime.After(nextRotationDate) || rotationModeChanged {
974992
// create rotation user if role is not listed for in-place password update
975993
if !slices.Contains(c.Spec.UsersWithInPlaceSecretRotation, secretUsername) {
976-
rotationUsername := fmt.Sprintf("%s%s", secretUsername, currentTime.Format(constants.RotationUserDateFormat))
977-
secret.Data["username"] = []byte(rotationUsername)
978-
c.logger.Infof("updating username in secret %s and creating rotation user %s in the database", secretName, rotationUsername)
994+
secret.Data["username"] = []byte(expectedUsername)
995+
c.logger.Infof("updating username in secret %s and creating rotation user %s in the database", secretName, expectedUsername)
979996
// whenever there is a rotation, check if old rotation users can be deleted
980997
*retentionUsers = append(*retentionUsers, secretUsername)
981998
} else {
982-
// when passwords of system users are rotated in place, pods have to be replaced
999+
// when passwords of system users are rotated in-place, pods have to be replaced
9831000
if roleOrigin == spec.RoleOriginSystem {
9841001
pods, err := c.listPods()
9851002
if err != nil {
@@ -993,7 +1010,7 @@ func (c *Cluster) rotatePasswordInSecret(
9931010
}
9941011
}
9951012

996-
// when password of connection pooler is rotated in place, pooler pods have to be replaced
1013+
// when password of connection pooler is rotated in-place, pooler pods have to be replaced
9971014
if roleOrigin == spec.RoleOriginConnectionPooler {
9981015
listOptions := metav1.ListOptions{
9991016
LabelSelector: c.poolerLabelsSet(true).String(),
@@ -1010,10 +1027,12 @@ func (c *Cluster) rotatePasswordInSecret(
10101027
}
10111028
}
10121029

1013-
// when password of stream user is rotated in place, it should trigger rolling update in FES deployment
1030+
// when password of stream user is rotated in-place, it should trigger rolling update in FES deployment
10141031
if roleOrigin == spec.RoleOriginStream {
10151032
c.logger.Warnf("password in secret of stream user %s changed", constants.EventStreamSourceSlotPrefix+constants.UserRoleNameSuffix)
10161033
}
1034+
1035+
secret.Data["username"] = []byte(secretUsername)
10171036
}
10181037
secret.Data["password"] = []byte(util.RandomPassword(constants.PasswordLength))
10191038
secret.Data["nextRotation"] = []byte(nextRotationDateStr)

pkg/cluster/sync_test.go

+30-1
Original file line numberDiff line numberDiff line change
@@ -624,6 +624,7 @@ func TestUpdateSecret(t *testing.T) {
624624
namespace := "default"
625625
dbname := "app"
626626
dbowner := "appowner"
627+
appUser := "foo"
627628
secretTemplate := config.StringTemplate("{username}.{cluster}.credentials")
628629
retentionUsers := make([]string, 0)
629630

@@ -635,7 +636,7 @@ func TestUpdateSecret(t *testing.T) {
635636
},
636637
Spec: acidv1.PostgresSpec{
637638
Databases: map[string]string{dbname: dbowner},
638-
Users: map[string]acidv1.UserFlags{"foo": {}, "bar": {}, dbowner: {}},
639+
Users: map[string]acidv1.UserFlags{appUser: {}, "bar": {}, dbowner: {}},
639640
UsersIgnoringSecretRotation: []string{"bar"},
640641
UsersWithInPlaceSecretRotation: []string{dbowner},
641642
Streams: []acidv1.Stream{
@@ -744,4 +745,32 @@ func TestUpdateSecret(t *testing.T) {
744745
}
745746
}
746747
}
748+
749+
// switch rotation for foo to in-place
750+
inPlaceRotationUsers := []string{dbowner, appUser}
751+
cluster.Spec.UsersWithInPlaceSecretRotation = inPlaceRotationUsers
752+
cluster.initUsers()
753+
cluster.syncSecrets()
754+
updatedSecret, err := cluster.KubeClient.Secrets(namespace).Get(context.TODO(), cluster.credentialSecretName(appUser), metav1.GetOptions{})
755+
assert.NoError(t, err)
756+
757+
// username in secret should be switched to original user
758+
currentUsername := string(updatedSecret.Data["username"])
759+
if currentUsername != appUser {
760+
t.Errorf("%s: updated secret does not contain correct username: expected %s, got %s", testName, appUser, currentUsername)
761+
}
762+
763+
// switch rotation back to rotation user
764+
inPlaceRotationUsers = []string{dbowner}
765+
cluster.Spec.UsersWithInPlaceSecretRotation = inPlaceRotationUsers
766+
cluster.initUsers()
767+
cluster.syncSecrets()
768+
updatedSecret, err = cluster.KubeClient.Secrets(namespace).Get(context.TODO(), cluster.credentialSecretName(appUser), metav1.GetOptions{})
769+
assert.NoError(t, err)
770+
771+
// username in secret will only be switched after next rotation date is passed
772+
currentUsername = string(updatedSecret.Data["username"])
773+
if currentUsername != appUser {
774+
t.Errorf("%s: updated secret does not contain expected username: expected %s, got %s", testName, appUser, currentUsername)
775+
}
747776
}

0 commit comments

Comments
 (0)