Skip to content

Commit 2bb7e98

Browse files
update individual role secrets from infrastructure roles (zalando#206)
* Track origin of roles. * Propagate changes on infrastructure roles to corresponding secrets. When the password in the infrastructure role is updated, re-generate the secret for that role. Previously, the password for an infrastructure role was always fetched from the secret, making any updates to such role a no-op after the corresponding secret had been generated.
1 parent 7b05758 commit 2bb7e98

File tree

7 files changed

+44
-9
lines changed

7 files changed

+44
-9
lines changed

pkg/cluster/cluster.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -656,10 +656,12 @@ func (c *Cluster) initSystemUsers() {
656656
// secrets, therefore, setting flags like SUPERUSER or REPLICATION
657657
// is not necessary here
658658
c.systemUsers[constants.SuperuserKeyName] = spec.PgUser{
659+
Origin: spec.RoleOriginSystem,
659660
Name: c.OpConfig.SuperUsername,
660661
Password: util.RandomPassword(constants.PasswordLength),
661662
}
662663
c.systemUsers[constants.ReplicationUserKeyName] = spec.PgUser{
664+
Origin: spec.RoleOriginSystem,
663665
Name: c.OpConfig.ReplicationUsername,
664666
Password: util.RandomPassword(constants.PasswordLength),
665667
}
@@ -680,6 +682,7 @@ func (c *Cluster) initRobotUsers() error {
680682
}
681683
if _, present := c.pgUsers[username]; !present {
682684
c.pgUsers[username] = spec.PgUser{
685+
Origin: spec.RoleOriginManifest,
683686
Name: username,
684687
Password: util.RandomPassword(constants.PasswordLength),
685688
Flags: flags,
@@ -723,6 +726,7 @@ func (c *Cluster) initHumanUsers() error {
723726
}
724727

725728
c.pgUsers[username] = spec.PgUser{
729+
Origin: spec.RoleOriginTeamsAPI,
726730
Name: username,
727731
Flags: flags,
728732
MemberOf: memberOf,

pkg/cluster/cluster_test.go

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -33,9 +33,9 @@ func TestInitRobotUsers(t *testing.T) {
3333
}{
3434
{
3535
manifestUsers: map[string]spec.UserFlags{"foo": {"superuser", "createdb"}},
36-
infraRoles: map[string]spec.PgUser{"foo": {Name: "foo", Password: "bar"}},
37-
result: map[string]spec.PgUser{"foo": {Name: "foo", Password: "bar",
38-
Flags: []string{"CREATEDB", "LOGIN", "SUPERUSER"}}},
36+
infraRoles: map[string]spec.PgUser{"foo": {Origin: spec.RoleOriginManifest, Name: "foo", Password: "bar"}},
37+
result: map[string]spec.PgUser{"foo": {Origin: spec.RoleOriginManifest,
38+
Name: "foo", Password: "bar", Flags: []string{"CREATEDB", "LOGIN", "SUPERUSER"}}},
3939
err: nil,
4040
},
4141
{
@@ -119,10 +119,11 @@ func TestInitHumanUsers(t *testing.T) {
119119
result map[string]spec.PgUser
120120
}{
121121
{
122-
existingRoles: map[string]spec.PgUser{"foo": {Name: "foo", Flags: []string{"NOLOGIN"}},
123-
"bar": {Name: "bar", Flags: []string{"NOLOGIN"}}},
122+
existingRoles: map[string]spec.PgUser{"foo": {Name: "foo", Origin: spec.RoleOriginTeamsAPI,
123+
Flags: []string{"NOLOGIN"}}, "bar": {Name: "bar", Flags: []string{"NOLOGIN"}}},
124124
teamRoles: []string{"foo"},
125-
result: map[string]spec.PgUser{"foo": {Name: "foo", MemberOf: []string{cl.OpConfig.PamRoleName}, Flags: []string{"LOGIN", "SUPERUSER"}},
125+
result: map[string]spec.PgUser{"foo": {Name: "foo", Origin: spec.RoleOriginTeamsAPI,
126+
MemberOf: []string{cl.OpConfig.PamRoleName}, Flags: []string{"LOGIN", "SUPERUSER"}},
126127
"bar": {Name: "bar", Flags: []string{"NOLOGIN"}}},
127128
},
128129
{

pkg/cluster/k8sres.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -646,8 +646,13 @@ func (c *Cluster) generateUserSecrets() (secrets map[string]*v1.Secret) {
646646
func (c *Cluster) generateSingleUserSecret(namespace string, pgUser spec.PgUser) *v1.Secret {
647647
//Skip users with no password i.e. human users (they'll be authenticated using pam)
648648
if pgUser.Password == "" {
649+
if pgUser.Origin != spec.RoleOriginTeamsAPI {
650+
c.logger.Warningf("could not generate secret for a non-teamsAPI role %q: role has no password",
651+
pgUser.Name)
652+
}
649653
return nil
650654
}
655+
651656
username := pgUser.Name
652657
secret := v1.Secret{
653658
ObjectMeta: metav1.ObjectMeta{

pkg/cluster/sync.go

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -322,6 +322,10 @@ func (c *Cluster) syncSecrets() error {
322322
if err2 != nil {
323323
return fmt.Errorf("could not get current secret: %v", err2)
324324
}
325+
if secretUsername != string(curSecret.Data["username"]) {
326+
c.logger.Warningf("secret %q does not contain the role %q", secretSpec.Name, secretUsername)
327+
continue
328+
}
325329
c.logger.Debugf("secret %q already exists, fetching its password", util.NameFromMeta(curSecret.ObjectMeta))
326330
if secretUsername == c.systemUsers[constants.SuperuserKeyName].Name {
327331
secretUsername = constants.SuperuserKeyName
@@ -333,8 +337,17 @@ func (c *Cluster) syncSecrets() error {
333337
userMap = c.pgUsers
334338
}
335339
pwdUser := userMap[secretUsername]
336-
pwdUser.Password = string(curSecret.Data["password"])
337-
userMap[secretUsername] = pwdUser
340+
// if this secret belongs to the infrastructure role and the password has changed - replace it in the secret
341+
if pwdUser.Password != string(curSecret.Data["password"]) && pwdUser.Origin == spec.RoleOriginInfrastructure {
342+
c.logger.Debugf("updating the secret %q from the infrastructure roles", secretSpec.Name)
343+
if _, err := c.KubeClient.Secrets(secretSpec.Namespace).Update(secretSpec); err != nil {
344+
return fmt.Errorf("could not update infrastructure role secret for role %q: %v", secretUsername, err)
345+
}
346+
} else {
347+
// for non-infrastructure role - update the role with the password from the secret
348+
pwdUser.Password = string(curSecret.Data["password"])
349+
userMap[secretUsername] = pwdUser
350+
}
338351

339352
continue
340353
} else {

pkg/controller/util.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,7 @@ Users:
116116
// in worst case we would have one line per user
117117
for i := 1; i <= len(data); i++ {
118118
properties := []string{"user", "password", "inrole"}
119-
t := spec.PgUser{}
119+
t := spec.PgUser{Origin: spec.RoleOriginInfrastructure}
120120
for _, p := range properties {
121121
key := fmt.Sprintf("%s%d", p, i)
122122
if val, present := data[key]; !present {

pkg/controller/util_test.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,7 @@ func TestGetInfrastructureRoles(t *testing.T) {
131131
map[string]spec.PgUser{
132132
"testrole": {
133133
Name: "testrole",
134+
Origin: spec.RoleOriginInfrastructure,
134135
Password: "testpassword",
135136
MemberOf: []string{"testinrole"},
136137
},

pkg/spec/types.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,16 @@ const (
3232
fileWithNamespace = "/var/run/secrets/kubernetes.io/serviceaccount/namespace"
3333
)
3434

35+
type RoleOrigin int
36+
37+
const (
38+
RoleOriginUnknown = iota
39+
RoleOriginInfrastructure
40+
RoleOriginManifest
41+
RoleOriginTeamsAPI
42+
RoleOriginSystem
43+
)
44+
3545
// ClusterEvent carries the payload of the Cluster TPR events.
3646
type ClusterEvent struct {
3747
EventTime time.Time
@@ -62,6 +72,7 @@ type PodEvent struct {
6272

6373
// PgUser contains information about a single user.
6474
type PgUser struct {
75+
Origin RoleOrigin
6576
Name string
6677
Password string
6778
Flags []string

0 commit comments

Comments
 (0)