Skip to content

Commit 4741b3f

Browse files
FxKuidanovinda
andauthored
copy rolconfig during password rotation (#2183)
* copy rolconfig during password rotation Co-authored-by: idanovinda <[email protected]>
1 parent 63c9f91 commit 4741b3f

File tree

8 files changed

+65
-25
lines changed

8 files changed

+65
-25
lines changed

e2e/tests/test_e2e.py

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -932,7 +932,8 @@ def verify_role():
932932
"AdminRole": "",
933933
"Origin": 2,
934934
"IsDbOwner": False,
935-
"Deleted": False
935+
"Deleted": False,
936+
"Rotated": False
936937
})
937938
return True
938939
except:
@@ -1472,9 +1473,9 @@ def test_password_rotation(self):
14721473
# create fake rotation users that should be removed by operator
14731474
# but have one that would still fit into the retention period
14741475
create_fake_rotation_user = """
1475-
CREATE ROLE foo_user201031 IN ROLE foo_user;
1476-
CREATE ROLE foo_user211031 IN ROLE foo_user;
1477-
CREATE ROLE foo_user"""+(today-timedelta(days=40)).strftime("%y%m%d")+""" IN ROLE foo_user;
1476+
CREATE USER foo_user201031 IN ROLE foo_user;
1477+
CREATE USER foo_user211031 IN ROLE foo_user;
1478+
CREATE USER foo_user"""+(today-timedelta(days=40)).strftime("%y%m%d")+""" IN ROLE foo_user;
14781479
"""
14791480
self.query_database(leader.metadata.name, "postgres", create_fake_rotation_user)
14801481

@@ -1491,6 +1492,12 @@ def test_password_rotation(self):
14911492
namespace="default",
14921493
body=secret_fake_rotation)
14931494

1495+
# update rolconfig for foo_user that will be copied for new rotation user
1496+
alter_foo_user_search_path = """
1497+
ALTER ROLE foo_user SET search_path TO data;
1498+
"""
1499+
self.query_database(leader.metadata.name, "postgres", alter_foo_user_search_path)
1500+
14941501
# enable password rotation for all other users (foo_user)
14951502
# this will force a sync of secrets for further assertions
14961503
enable_password_rotation = {
@@ -1526,6 +1533,18 @@ def test_password_rotation(self):
15261533
self.eventuallyEqual(lambda: len(self.query_database(leader.metadata.name, "postgres", user_query)), 3,
15271534
"Found incorrect number of rotation users", 10, 5)
15281535

1536+
# check if rolconfig was passed from foo_user to foo_user+today
1537+
# and that no foo_user has been deprecated (can still login)
1538+
user_query = """
1539+
SELECT rolname
1540+
FROM pg_catalog.pg_roles
1541+
WHERE rolname LIKE 'foo_user%'
1542+
AND rolconfig = ARRAY['search_path=data']::text[]
1543+
AND rolcanlogin;
1544+
"""
1545+
self.eventuallyEqual(lambda: len(self.query_database(leader.metadata.name, "postgres", user_query)), 2,
1546+
"Rolconfig not applied to new rotation user", 10, 5)
1547+
15291548
# test that rotation_user can connect to the database
15301549
self.eventuallyEqual(lambda: len(self.query_database_with_user(leader.metadata.name, "postgres", "SELECT 1", "foo_user")), 1,
15311550
"Could not connect to the database with rotation user {}".format(rotation_user), 10, 5)
@@ -1559,7 +1578,7 @@ def test_password_rotation(self):
15591578
WHERE rolname LIKE 'foo_user%';
15601579
"""
15611580
self.eventuallyEqual(lambda: len(self.query_database(leader.metadata.name, "postgres", user_query)), 2,
1562-
"Found incorrect number of rotation users", 10, 5)
1581+
"Found incorrect number of rotation users after disabling password rotation", 10, 5)
15631582

15641583
@timeout_decorator.timeout(TEST_TIMEOUT_SEC)
15651584
def test_rolling_update_flag(self):

pkg/cluster/database.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -284,7 +284,7 @@ func (c *Cluster) cleanupRotatedUsers(rotatedUsers []string, db *sql.DB) error {
284284
retentionDate := time.Now().AddDate(0, 0, int(retenionDays)*-1)
285285

286286
for rotatedUser, dateSuffix := range extraUsers {
287-
userCreationDate, err := time.Parse("060102", dateSuffix)
287+
userCreationDate, err := time.Parse(constants.RotationUserDateFormat, dateSuffix)
288288
if err != nil {
289289
c.logger.Errorf("could not parse creation date suffix of user %q: %v", rotatedUser, err)
290290
continue

pkg/cluster/sync.go

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -656,7 +656,6 @@ func (c *Cluster) checkAndSetGlobalPostgreSQLConfiguration(pod *v1.Pod, effectiv
656656
}
657657

658658
func (c *Cluster) syncSecrets() error {
659-
660659
c.logger.Info("syncing secrets")
661660
c.setProcessName("syncing secrets")
662661
generatedSecrets := c.generateUserSecrets()
@@ -792,6 +791,7 @@ func (c *Cluster) updateSecret(
792791
pwdUser.Password = string(secret.Data["password"])
793792
// update membership if we deal with a rotation user
794793
if secretUsername != pwdUser.Name {
794+
pwdUser.Rotated = true
795795
pwdUser.MemberOf = []string{secretUsername}
796796
}
797797
userMap[userKey] = pwdUser
@@ -842,7 +842,7 @@ func (c *Cluster) rotatePasswordInSecret(
842842
if currentTime.After(nextRotationDate) {
843843
// create rotation user if role is not listed for in-place password update
844844
if !util.SliceContains(c.Spec.UsersWithInPlaceSecretRotation, secretUsername) {
845-
rotationUsername := fmt.Sprintf("%s%s", secretUsername, currentTime.Format("060102"))
845+
rotationUsername := fmt.Sprintf("%s%s", secretUsername, currentTime.Format(constants.RotationUserDateFormat))
846846
secret.Data["username"] = []byte(rotationUsername)
847847
c.logger.Infof("updating username in secret %s and creating rotation user %s in the database", secretName, rotationUsername)
848848
// whenever there is a rotation, check if old rotation users can be deleted
@@ -924,6 +924,12 @@ func (c *Cluster) syncRoles() (err error) {
924924
for _, u := range c.pgUsers {
925925
pgRole := u.Name
926926
userNames = append(userNames, pgRole)
927+
928+
// when a rotation happened add group role to query its rolconfig
929+
if u.Rotated {
930+
userNames = append(userNames, u.MemberOf[0])
931+
}
932+
927933
// add team member role name with rename suffix in case we need to rename it back
928934
if u.Origin == spec.RoleOriginTeamsAPI && c.OpConfig.EnableTeamMemberDeprecation {
929935
deletedUsers[pgRole+c.OpConfig.RoleDeletionSuffix] = pgRole
@@ -950,9 +956,21 @@ func (c *Cluster) syncRoles() (err error) {
950956
return fmt.Errorf("error getting users from the database: %v", err)
951957
}
952958

953-
// update pgUsers where a deleted role was found
954-
// so that they are skipped in ProduceSyncRequests
959+
DBUSERS:
955960
for _, dbUser := range dbUsers {
961+
// copy rolconfig to rotation users
962+
for pgUserName, pgUser := range c.pgUsers {
963+
if pgUser.Rotated && pgUser.MemberOf[0] == dbUser.Name {
964+
pgUser.Parameters = dbUser.Parameters
965+
c.pgUsers[pgUserName] = pgUser
966+
// remove group role from dbUsers to not count as deleted role
967+
delete(dbUsers, dbUser.Name)
968+
continue DBUSERS
969+
}
970+
}
971+
972+
// update pgUsers where a deleted role was found
973+
// so that they are skipped in ProduceSyncRequests
956974
originalUsername, foundDeletedUser := deletedUsers[dbUser.Name]
957975
// check if original user does not exist in dbUsers
958976
_, originalUserAlreadyExists := dbUsers[originalUsername]

pkg/cluster/sync_test.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import (
2222
"github.com/zalando/postgres-operator/pkg/spec"
2323
"github.com/zalando/postgres-operator/pkg/util"
2424
"github.com/zalando/postgres-operator/pkg/util/config"
25+
"github.com/zalando/postgres-operator/pkg/util/constants"
2526
"github.com/zalando/postgres-operator/pkg/util/k8sutil"
2627
"github.com/zalando/postgres-operator/pkg/util/patroni"
2728
"k8s.io/client-go/kubernetes/fake"
@@ -593,7 +594,7 @@ func TestUpdateSecret(t *testing.T) {
593594
t.Errorf("%s: username differs in updated secret: expected %s, got %s", testName, username, secretUsername)
594595
}
595596
} else {
596-
rotatedUsername := username + dayAfterTomorrow.Format("060102")
597+
rotatedUsername := username + dayAfterTomorrow.Format(constants.RotationUserDateFormat)
597598
if secretUsername != rotatedUsername {
598599
t.Errorf("%s: updated secret does not contain correct username: expected %s, got %s", testName, rotatedUsername, secretUsername)
599600
}

pkg/generated/clientset/versioned/fake/register.go

Lines changed: 7 additions & 7 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pkg/generated/clientset/versioned/scheme/register.go

Lines changed: 7 additions & 7 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pkg/spec/types.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ type PgUser struct {
5858
AdminRole string `yaml:"admin_role"`
5959
IsDbOwner bool `yaml:"is_db_owner"`
6060
Deleted bool `yaml:"deleted"`
61+
Rotated bool `yaml:"rotated"`
6162
}
6263

6364
func (user *PgUser) Valid() bool {

pkg/util/constants/roles.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,4 +20,5 @@ const (
2020
WriterRoleNameSuffix = "_writer"
2121
UserRoleNameSuffix = "_user"
2222
DefaultSearchPath = "\"$user\""
23+
RotationUserDateFormat = "060102"
2324
)

0 commit comments

Comments
 (0)