update individual role secrets from infrastructure roles (#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.
This commit is contained in:
parent
7b05758893
commit
2bb7e98268
|
|
@ -656,10 +656,12 @@ func (c *Cluster) initSystemUsers() {
|
||||||
// secrets, therefore, setting flags like SUPERUSER or REPLICATION
|
// secrets, therefore, setting flags like SUPERUSER or REPLICATION
|
||||||
// is not necessary here
|
// is not necessary here
|
||||||
c.systemUsers[constants.SuperuserKeyName] = spec.PgUser{
|
c.systemUsers[constants.SuperuserKeyName] = spec.PgUser{
|
||||||
|
Origin: spec.RoleOriginSystem,
|
||||||
Name: c.OpConfig.SuperUsername,
|
Name: c.OpConfig.SuperUsername,
|
||||||
Password: util.RandomPassword(constants.PasswordLength),
|
Password: util.RandomPassword(constants.PasswordLength),
|
||||||
}
|
}
|
||||||
c.systemUsers[constants.ReplicationUserKeyName] = spec.PgUser{
|
c.systemUsers[constants.ReplicationUserKeyName] = spec.PgUser{
|
||||||
|
Origin: spec.RoleOriginSystem,
|
||||||
Name: c.OpConfig.ReplicationUsername,
|
Name: c.OpConfig.ReplicationUsername,
|
||||||
Password: util.RandomPassword(constants.PasswordLength),
|
Password: util.RandomPassword(constants.PasswordLength),
|
||||||
}
|
}
|
||||||
|
|
@ -680,6 +682,7 @@ func (c *Cluster) initRobotUsers() error {
|
||||||
}
|
}
|
||||||
if _, present := c.pgUsers[username]; !present {
|
if _, present := c.pgUsers[username]; !present {
|
||||||
c.pgUsers[username] = spec.PgUser{
|
c.pgUsers[username] = spec.PgUser{
|
||||||
|
Origin: spec.RoleOriginManifest,
|
||||||
Name: username,
|
Name: username,
|
||||||
Password: util.RandomPassword(constants.PasswordLength),
|
Password: util.RandomPassword(constants.PasswordLength),
|
||||||
Flags: flags,
|
Flags: flags,
|
||||||
|
|
@ -723,6 +726,7 @@ func (c *Cluster) initHumanUsers() error {
|
||||||
}
|
}
|
||||||
|
|
||||||
c.pgUsers[username] = spec.PgUser{
|
c.pgUsers[username] = spec.PgUser{
|
||||||
|
Origin: spec.RoleOriginTeamsAPI,
|
||||||
Name: username,
|
Name: username,
|
||||||
Flags: flags,
|
Flags: flags,
|
||||||
MemberOf: memberOf,
|
MemberOf: memberOf,
|
||||||
|
|
|
||||||
|
|
@ -33,9 +33,9 @@ func TestInitRobotUsers(t *testing.T) {
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
manifestUsers: map[string]spec.UserFlags{"foo": {"superuser", "createdb"}},
|
manifestUsers: map[string]spec.UserFlags{"foo": {"superuser", "createdb"}},
|
||||||
infraRoles: map[string]spec.PgUser{"foo": {Name: "foo", Password: "bar"}},
|
infraRoles: map[string]spec.PgUser{"foo": {Origin: spec.RoleOriginManifest, Name: "foo", Password: "bar"}},
|
||||||
result: map[string]spec.PgUser{"foo": {Name: "foo", Password: "bar",
|
result: map[string]spec.PgUser{"foo": {Origin: spec.RoleOriginManifest,
|
||||||
Flags: []string{"CREATEDB", "LOGIN", "SUPERUSER"}}},
|
Name: "foo", Password: "bar", Flags: []string{"CREATEDB", "LOGIN", "SUPERUSER"}}},
|
||||||
err: nil,
|
err: nil,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
@ -119,10 +119,11 @@ func TestInitHumanUsers(t *testing.T) {
|
||||||
result map[string]spec.PgUser
|
result map[string]spec.PgUser
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
existingRoles: map[string]spec.PgUser{"foo": {Name: "foo", Flags: []string{"NOLOGIN"}},
|
existingRoles: map[string]spec.PgUser{"foo": {Name: "foo", Origin: spec.RoleOriginTeamsAPI,
|
||||||
"bar": {Name: "bar", Flags: []string{"NOLOGIN"}}},
|
Flags: []string{"NOLOGIN"}}, "bar": {Name: "bar", Flags: []string{"NOLOGIN"}}},
|
||||||
teamRoles: []string{"foo"},
|
teamRoles: []string{"foo"},
|
||||||
result: map[string]spec.PgUser{"foo": {Name: "foo", MemberOf: []string{cl.OpConfig.PamRoleName}, Flags: []string{"LOGIN", "SUPERUSER"}},
|
result: map[string]spec.PgUser{"foo": {Name: "foo", Origin: spec.RoleOriginTeamsAPI,
|
||||||
|
MemberOf: []string{cl.OpConfig.PamRoleName}, Flags: []string{"LOGIN", "SUPERUSER"}},
|
||||||
"bar": {Name: "bar", Flags: []string{"NOLOGIN"}}},
|
"bar": {Name: "bar", Flags: []string{"NOLOGIN"}}},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -646,8 +646,13 @@ func (c *Cluster) generateUserSecrets() (secrets map[string]*v1.Secret) {
|
||||||
func (c *Cluster) generateSingleUserSecret(namespace string, pgUser spec.PgUser) *v1.Secret {
|
func (c *Cluster) generateSingleUserSecret(namespace string, pgUser spec.PgUser) *v1.Secret {
|
||||||
//Skip users with no password i.e. human users (they'll be authenticated using pam)
|
//Skip users with no password i.e. human users (they'll be authenticated using pam)
|
||||||
if pgUser.Password == "" {
|
if pgUser.Password == "" {
|
||||||
|
if pgUser.Origin != spec.RoleOriginTeamsAPI {
|
||||||
|
c.logger.Warningf("could not generate secret for a non-teamsAPI role %q: role has no password",
|
||||||
|
pgUser.Name)
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
username := pgUser.Name
|
username := pgUser.Name
|
||||||
secret := v1.Secret{
|
secret := v1.Secret{
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
|
|
||||||
|
|
@ -322,6 +322,10 @@ func (c *Cluster) syncSecrets() error {
|
||||||
if err2 != nil {
|
if err2 != nil {
|
||||||
return fmt.Errorf("could not get current secret: %v", err2)
|
return fmt.Errorf("could not get current secret: %v", err2)
|
||||||
}
|
}
|
||||||
|
if secretUsername != string(curSecret.Data["username"]) {
|
||||||
|
c.logger.Warningf("secret %q does not contain the role %q", secretSpec.Name, secretUsername)
|
||||||
|
continue
|
||||||
|
}
|
||||||
c.logger.Debugf("secret %q already exists, fetching its password", util.NameFromMeta(curSecret.ObjectMeta))
|
c.logger.Debugf("secret %q already exists, fetching its password", util.NameFromMeta(curSecret.ObjectMeta))
|
||||||
if secretUsername == c.systemUsers[constants.SuperuserKeyName].Name {
|
if secretUsername == c.systemUsers[constants.SuperuserKeyName].Name {
|
||||||
secretUsername = constants.SuperuserKeyName
|
secretUsername = constants.SuperuserKeyName
|
||||||
|
|
@ -333,8 +337,17 @@ func (c *Cluster) syncSecrets() error {
|
||||||
userMap = c.pgUsers
|
userMap = c.pgUsers
|
||||||
}
|
}
|
||||||
pwdUser := userMap[secretUsername]
|
pwdUser := userMap[secretUsername]
|
||||||
pwdUser.Password = string(curSecret.Data["password"])
|
// if this secret belongs to the infrastructure role and the password has changed - replace it in the secret
|
||||||
userMap[secretUsername] = pwdUser
|
if pwdUser.Password != string(curSecret.Data["password"]) && pwdUser.Origin == spec.RoleOriginInfrastructure {
|
||||||
|
c.logger.Debugf("updating the secret %q from the infrastructure roles", secretSpec.Name)
|
||||||
|
if _, err := c.KubeClient.Secrets(secretSpec.Namespace).Update(secretSpec); err != nil {
|
||||||
|
return fmt.Errorf("could not update infrastructure role secret for role %q: %v", secretUsername, err)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// for non-infrastructure role - update the role with the password from the secret
|
||||||
|
pwdUser.Password = string(curSecret.Data["password"])
|
||||||
|
userMap[secretUsername] = pwdUser
|
||||||
|
}
|
||||||
|
|
||||||
continue
|
continue
|
||||||
} else {
|
} else {
|
||||||
|
|
|
||||||
|
|
@ -116,7 +116,7 @@ Users:
|
||||||
// in worst case we would have one line per user
|
// in worst case we would have one line per user
|
||||||
for i := 1; i <= len(data); i++ {
|
for i := 1; i <= len(data); i++ {
|
||||||
properties := []string{"user", "password", "inrole"}
|
properties := []string{"user", "password", "inrole"}
|
||||||
t := spec.PgUser{}
|
t := spec.PgUser{Origin: spec.RoleOriginInfrastructure}
|
||||||
for _, p := range properties {
|
for _, p := range properties {
|
||||||
key := fmt.Sprintf("%s%d", p, i)
|
key := fmt.Sprintf("%s%d", p, i)
|
||||||
if val, present := data[key]; !present {
|
if val, present := data[key]; !present {
|
||||||
|
|
|
||||||
|
|
@ -131,6 +131,7 @@ func TestGetInfrastructureRoles(t *testing.T) {
|
||||||
map[string]spec.PgUser{
|
map[string]spec.PgUser{
|
||||||
"testrole": {
|
"testrole": {
|
||||||
Name: "testrole",
|
Name: "testrole",
|
||||||
|
Origin: spec.RoleOriginInfrastructure,
|
||||||
Password: "testpassword",
|
Password: "testpassword",
|
||||||
MemberOf: []string{"testinrole"},
|
MemberOf: []string{"testinrole"},
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -32,6 +32,16 @@ const (
|
||||||
fileWithNamespace = "/var/run/secrets/kubernetes.io/serviceaccount/namespace"
|
fileWithNamespace = "/var/run/secrets/kubernetes.io/serviceaccount/namespace"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type RoleOrigin int
|
||||||
|
|
||||||
|
const (
|
||||||
|
RoleOriginUnknown = iota
|
||||||
|
RoleOriginInfrastructure
|
||||||
|
RoleOriginManifest
|
||||||
|
RoleOriginTeamsAPI
|
||||||
|
RoleOriginSystem
|
||||||
|
)
|
||||||
|
|
||||||
// ClusterEvent carries the payload of the Cluster TPR events.
|
// ClusterEvent carries the payload of the Cluster TPR events.
|
||||||
type ClusterEvent struct {
|
type ClusterEvent struct {
|
||||||
EventTime time.Time
|
EventTime time.Time
|
||||||
|
|
@ -62,6 +72,7 @@ type PodEvent struct {
|
||||||
|
|
||||||
// PgUser contains information about a single user.
|
// PgUser contains information about a single user.
|
||||||
type PgUser struct {
|
type PgUser struct {
|
||||||
|
Origin RoleOrigin
|
||||||
Name string
|
Name string
|
||||||
Password string
|
Password string
|
||||||
Flags []string
|
Flags []string
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue