Skip to content

Commit 1fb8cf7

Browse files
Avoid overwriting critical users. (zalando#172)
* Avoid overwriting critical users. Disallow defining new users either in the cluster manifest, teams API or infrastructure roles with the names mentioned in the new protected_role_names parameter (list of comma-separated names) Additionally, forbid defining a user with the name matching either super_username or replication_username, so that we don't overwrite system roles required for correct working of the operator itself. Also, clear PostgreSQL roles on each sync first in order to avoid using the old definitions that are no longer present in the current manifest, infrastructure roles secret or the teams API.
1 parent 022ce29 commit 1fb8cf7

File tree

5 files changed

+58
-1
lines changed

5 files changed

+58
-1
lines changed

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -208,6 +208,8 @@ The following steps will get you the docker image built and deployed.
208208
of configuration parameters applied to the roles fetched from the API.
209209
For instance, `team_api_role_configuration: log_statement:all,search_path:'public,"$user"'`.
210210
By default is set to *"log_statement:all"*. See [PostgreSQL documentation on ALTER ROLE .. SET](https://www.postgresql.org/docs/current/static/sql-alterrole.html) for to learn about the available options.
211+
* protected_role_names - a list of role names that should be forbidden as the manifest, infrastructure and teams API roles.
212+
The default value is `admin`. Operator will also disallow superuser and replication roles to be redefined.
211213

212214

213215
### Debugging the operator itself

pkg/cluster/cluster.go

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,11 @@ func (c *Cluster) setStatus(status spec.PostgresStatus) {
168168
// initUsers populates c.systemUsers and c.pgUsers maps.
169169
func (c *Cluster) initUsers() error {
170170
c.setProcessName("initializing users")
171+
172+
// clear our the previous state of the cluster users (in case we are running a sync).
173+
c.systemUsers = map[string]spec.PgUser{}
174+
c.pgUsers = map[string]spec.PgUser{}
175+
171176
c.initSystemUsers()
172177

173178
if err := c.initInfrastructureRoles(); err != nil {
@@ -615,6 +620,9 @@ func (c *Cluster) initRobotUsers() error {
615620
return fmt.Errorf("invalid username: %q", username)
616621
}
617622

623+
if c.shouldAvoidProtectedOrSystemRole(username, "manifest robot role") {
624+
continue
625+
}
618626
flags, err := normalizeUserFlags(userFlags)
619627
if err != nil {
620628
return fmt.Errorf("invalid flags for user %q: %v", username, err)
@@ -648,6 +656,9 @@ func (c *Cluster) initHumanUsers() error {
648656
flags := []string{constants.RoleFlagLogin}
649657
memberOf := []string{c.OpConfig.PamRoleName}
650658

659+
if c.shouldAvoidProtectedOrSystemRole(username, "API role") {
660+
continue
661+
}
651662
if c.OpConfig.EnableTeamSuperuser {
652663
flags = append(flags, constants.RoleFlagSuperuser)
653664
} else {
@@ -677,6 +688,9 @@ func (c *Cluster) initInfrastructureRoles() error {
677688
if !isValidUsername(username) {
678689
return fmt.Errorf("invalid username: '%v'", username)
679690
}
691+
if c.shouldAvoidProtectedOrSystemRole(username, "infrastructure role") {
692+
continue
693+
}
680694
flags, err := normalizeUserFlags(data.Flags)
681695
if err != nil {
682696
return fmt.Errorf("invalid flags for user '%v': %v", username, err)
@@ -687,6 +701,18 @@ func (c *Cluster) initInfrastructureRoles() error {
687701
return nil
688702
}
689703

704+
func (c *Cluster) shouldAvoidProtectedOrSystemRole(username, purpose string) bool {
705+
if c.isProtectedUsername(username) {
706+
c.logger.Warnf("cannot initialize a new %s with the name of the protected user %q", purpose, username)
707+
return true
708+
}
709+
if c.isSystemUsername(username) {
710+
c.logger.Warnf("cannot initialize a new %s with the name of the system user %q", purpose, username)
711+
return true
712+
}
713+
return false
714+
}
715+
690716
// GetCurrentProcess provides name of the last process of the cluster
691717
func (c *Cluster) GetCurrentProcess() spec.Process {
692718
c.processMu.RLock()

pkg/cluster/cluster_test.go

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,18 @@ import (
44
"fmt"
55
"github.com/Sirupsen/logrus"
66
"github.com/zalando-incubator/postgres-operator/pkg/spec"
7+
"github.com/zalando-incubator/postgres-operator/pkg/util/config"
78
"github.com/zalando-incubator/postgres-operator/pkg/util/k8sutil"
89
"github.com/zalando-incubator/postgres-operator/pkg/util/teams"
910
"reflect"
1011
"testing"
1112
)
1213

1314
var logger = logrus.New().WithField("test", "cluster")
14-
var cl = New(Config{}, k8sutil.KubernetesClient{}, spec.Postgresql{}, logger)
15+
var cl = New(Config{OpConfig: config.Config{ProtectedRoles: []string{"admin"},
16+
Auth: config.Auth{SuperUsername: "postgres",
17+
ReplicationUsername: "standby"}}},
18+
k8sutil.KubernetesClient{}, spec.Postgresql{}, logger)
1519

1620
func TestInitRobotUsers(t *testing.T) {
1721
testName := "TestInitRobotUsers"
@@ -47,6 +51,12 @@ func TestInitRobotUsers(t *testing.T) {
4751
err: fmt.Errorf(`invalid flags for user "foobar": ` +
4852
`conflicting user flags: "NOINHERIT" and "INHERIT"`),
4953
},
54+
{
55+
manifestUsers: map[string]spec.UserFlags{"admin": {"superuser"}, "postgres": {"createdb"}},
56+
infraRoles: map[string]spec.PgUser{},
57+
result: map[string]spec.PgUser{},
58+
err: nil,
59+
},
5060
}
5161
for _, tt := range tests {
5262
cl.Spec.Users = tt.manifestUsers
@@ -109,6 +119,11 @@ func TestInitHumanUsers(t *testing.T) {
109119
result: map[string]spec.PgUser{"foo": {Name: "foo", MemberOf: []string{cl.OpConfig.PamRoleName}, Flags: []string{"LOGIN", "SUPERUSER"}},
110120
"bar": {Name: "bar", Flags: []string{"NOLOGIN"}}},
111121
},
122+
{
123+
existingRoles: map[string]spec.PgUser{},
124+
teamRoles: []string{"admin", "standby"},
125+
result: map[string]spec.PgUser{},
126+
},
112127
}
113128

114129
for _, tt := range tests {

pkg/cluster/util.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,19 @@ func isValidUsername(username string) bool {
6060
return userRegexp.MatchString(username)
6161
}
6262

63+
func (c *Cluster) isProtectedUsername(username string) bool {
64+
for _, protected := range c.OpConfig.ProtectedRoles {
65+
if username == protected {
66+
return true
67+
}
68+
}
69+
return false
70+
}
71+
72+
func (c *Cluster) isSystemUsername(username string) bool {
73+
return (username == c.OpConfig.SuperUsername || username == c.OpConfig.ReplicationUsername)
74+
}
75+
6376
func isValidFlag(flag string) bool {
6477
for _, validFlag := range []string{constants.RoleFlagSuperuser, constants.RoleFlagLogin, constants.RoleFlagCreateDB,
6578
constants.RoleFlagInherit, constants.RoleFlagReplication, constants.RoleFlagByPassRLS} {

pkg/util/config/config.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@ type Config struct {
7474
ClusterHistoryEntries int `name:"cluster_history_entries" default:"1000"`
7575
TeamAPIRoleConfiguration map[string]string `name:"team_api_role_configuration" default:"log_statement:all"`
7676
PodTerminateGracePeriod time.Duration `name:"pod_terminate_grace_period" default:"5m"`
77+
ProtectedRoles []string `name:"protected_role_names" default:"admin"`
7778
}
7879

7980
// MustMarshal marshals the config or panics

0 commit comments

Comments
 (0)