allow specifiying more extra owner roles

This commit is contained in:
Felix Kunde 2022-03-04 11:01:07 +01:00
parent 89801ef30a
commit 78eaae2efc
12 changed files with 86 additions and 62 deletions

View File

@ -130,8 +130,11 @@ spec:
users: users:
type: object type: object
properties: properties:
cron_admin_username: additional_owner_roles:
type: string type: array
nullable: true
items:
type: string
enable_password_rotation: enable_password_rotation:
type: boolean type: boolean
default: false default: false
@ -502,6 +505,7 @@ spec:
type: string type: string
default: default:
- admin - admin
- cron_admin
role_deletion_suffix: role_deletion_suffix:
type: string type: string
default: "_deleted" default: "_deleted"

View File

@ -59,8 +59,10 @@ configGeneral:
# parameters describing Postgres users # parameters describing Postgres users
configUsers: configUsers:
# username used to grant rights to set up and maintain cron jobs to database owners # roles to be granted to database owners
cron_admin_username: cron_admin # additional_owner_roles:
# - cron_admin
# enable password rotation for app users that are not database owners # enable password rotation for app users that are not database owners
enable_password_rotation: false enable_password_rotation: false
# rotation interval for updating credentials in K8s secrets of app users # rotation interval for updating credentials in K8s secrets of app users
@ -346,6 +348,7 @@ configTeamsApi:
# List of roles that cannot be overwritten by an application, team or infrastructure role # List of roles that cannot be overwritten by an application, team or infrastructure role
protected_role_names: protected_role_names:
- admin - admin
- cron_admin
# Suffix to add if members are removed from TeamsAPI or PostgresTeam CRD # Suffix to add if members are removed from TeamsAPI or PostgresTeam CRD
role_deletion_suffix: "_deleted" role_deletion_suffix: "_deleted"
# role name to grant to team members created from the Teams API # role name to grant to team members created from the Teams API

View File

@ -177,12 +177,14 @@ under the `users` key.
Postgres username used for replication between instances. The default is Postgres username used for replication between instances. The default is
`standby`. `standby`.
* **cron_admin_username** * **additional_owner_roles**
Specifies the role that owns rights to set up cron jobs with `pg_cron` Specifies database roles that will become members of all database owners.
extension inside the `postgres` database. The must be pre-configured in the Then owners can use `SET ROLE` to obtain privileges of these roles to e.g.
docker image. In Spilo this role is called `cron_admin`. This role will create/update functionality from extensions as part of a migration script.
become a member of all database owners so that they can set up cron jobs Note, that roles listed here should be preconfigured in the docker image
e.g. as part of a migration script. Default is `empty`. and already exist in the database cluster on startup. One such role can be
`cron_admin` which is provided by the Spilo docker image to set up cron
jobs inside the `postgres` database. Default is `empty`.
* **enable_password_rotation** * **enable_password_rotation**
For all `LOGIN` roles that are not database owners the operator can rotate For all `LOGIN` roles that are not database owners the operator can rotate
@ -755,7 +757,7 @@ key.
* **protected_role_names** * **protected_role_names**
List of roles that cannot be overwritten by an application, team or List of roles that cannot be overwritten by an application, team or
infrastructure role. The default is `admin`. infrastructure role. The default list is `admin` and `cron_admin`.
* **postgres_superuser_teams** * **postgres_superuser_teams**
List of teams which members need the superuser role in each PG database List of teams which members need the superuser role in each PG database

View File

@ -3,6 +3,7 @@ kind: ConfigMap
metadata: metadata:
name: postgres-operator name: postgres-operator
data: data:
# additional_owner_roles: "cron_admin"
# additional_pod_capabilities: "SYS_NICE" # additional_pod_capabilities: "SYS_NICE"
# additional_secret_mount: "some-secret-name" # additional_secret_mount: "some-secret-name"
# additional_secret_mount_path: "/some/dir" # additional_secret_mount_path: "/some/dir"
@ -23,7 +24,6 @@ data:
# connection_pooler_schema: "pooler" # connection_pooler_schema: "pooler"
# connection_pooler_user: "pooler" # connection_pooler_user: "pooler"
crd_categories: "all" crd_categories: "all"
# cron_admin_username: "cron_admin"
# custom_service_annotations: "keyx:valuez,keya:valuea" # custom_service_annotations: "keyx:valuez,keya:valuea"
# custom_pod_annotations: "keya:valuea,keyb:valueb" # custom_pod_annotations: "keya:valuea,keyb:valueb"
db_hosted_zone: db.example.com db_hosted_zone: db.example.com

View File

@ -128,8 +128,11 @@ spec:
users: users:
type: object type: object
properties: properties:
cron_admin_username: additional_owner_roles:
type: string type: array
nullable: true
items:
type: string
enable_password_rotation: enable_password_rotation:
type: boolean type: boolean
default: false default: false
@ -500,6 +503,7 @@ spec:
type: string type: string
default: default:
- admin - admin
- cron_admin
role_deletion_suffix: role_deletion_suffix:
type: string type: string
default: "_deleted" default: "_deleted"

View File

@ -26,12 +26,13 @@ configuration:
# protocol: TCP # protocol: TCP
workers: 8 workers: 8
users: users:
# additional_owner_roles:
# - cron_admin
enable_password_rotation: false enable_password_rotation: false
password_rotation_interval: 90 password_rotation_interval: 90
password_rotation_user_retention: 180 password_rotation_user_retention: 180
replication_username: standby replication_username: standby
super_username: postgres super_username: postgres
# cron_admin_username: cron_admin
major_version_upgrade: major_version_upgrade:
major_version_upgrade_mode: "off" major_version_upgrade_mode: "off"
# major_version_upgrade_team_allow_list: # major_version_upgrade_team_allow_list:

View File

@ -1142,8 +1142,14 @@ var OperatorConfigCRDResourceValidation = apiextv1.CustomResourceValidation{
"users": { "users": {
Type: "object", Type: "object",
Properties: map[string]apiextv1.JSONSchemaProps{ Properties: map[string]apiextv1.JSONSchemaProps{
"cron_admin_username": { "additional_owner_roles": {
Type: "string", Type: "array",
Nullable: true,
Items: &apiextv1.JSONSchemaPropsOrArray{
Schema: &apiextv1.JSONSchemaProps{
Type: "string",
},
},
}, },
"enable_password_rotation": { "enable_password_rotation": {
Type: "boolean", Type: "boolean",

View File

@ -37,12 +37,12 @@ type OperatorConfigurationList struct {
// PostgresUsersConfiguration defines the system users of Postgres. // PostgresUsersConfiguration defines the system users of Postgres.
type PostgresUsersConfiguration struct { type PostgresUsersConfiguration struct {
SuperUsername string `json:"super_username,omitempty"` SuperUsername string `json:"super_username,omitempty"`
ReplicationUsername string `json:"replication_username,omitempty"` ReplicationUsername string `json:"replication_username,omitempty"`
CronAdminUsername string `json:"cron_admin_username,omitempty"` AddtionalOwnerRoles []string `json:"additional_owner_roles,omitempty"`
EnablePasswordRotation bool `json:"enable_password_rotation,omitempty"` EnablePasswordRotation bool `json:"enable_password_rotation,omitempty"`
PasswordRotationInterval uint32 `json:"password_rotation_interval,omitempty"` PasswordRotationInterval uint32 `json:"password_rotation_interval,omitempty"`
PasswordRotationUserRetention uint32 `json:"password_rotation_user_retention,omitempty"` PasswordRotationUserRetention uint32 `json:"password_rotation_user_retention,omitempty"`
} }
// MajorVersionUpgradeConfiguration defines how to execute major version upgrades of Postgres. // MajorVersionUpgradeConfiguration defines how to execute major version upgrades of Postgres.

View File

@ -401,7 +401,7 @@ func (in *OperatorConfigurationData) DeepCopyInto(out *OperatorConfigurationData
(*in)[i].DeepCopyInto(&(*out)[i]) (*in)[i].DeepCopyInto(&(*out)[i])
} }
} }
out.PostgresUsersConfiguration = in.PostgresUsersConfiguration in.PostgresUsersConfiguration.DeepCopyInto(&out.PostgresUsersConfiguration)
in.MajorVersionUpgrade.DeepCopyInto(&out.MajorVersionUpgrade) in.MajorVersionUpgrade.DeepCopyInto(&out.MajorVersionUpgrade)
in.Kubernetes.DeepCopyInto(&out.Kubernetes) in.Kubernetes.DeepCopyInto(&out.Kubernetes)
out.PostgresPodResources = in.PostgresPodResources out.PostgresPodResources = in.PostgresPodResources
@ -916,6 +916,11 @@ func (in *PostgresTeamSpec) DeepCopy() *PostgresTeamSpec {
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *PostgresUsersConfiguration) DeepCopyInto(out *PostgresUsersConfiguration) { func (in *PostgresUsersConfiguration) DeepCopyInto(out *PostgresUsersConfiguration) {
*out = *in *out = *in
if in.AddtionalOwnerRoles != nil {
in, out := &in.AddtionalOwnerRoles, &out.AddtionalOwnerRoles
*out = make([]string, len(*in))
copy(*out, *in)
}
return return
} }

View File

@ -228,7 +228,7 @@ func (c *Cluster) initUsers() error {
return fmt.Errorf("could not init human users: %v", err) return fmt.Errorf("could not init human users: %v", err)
} }
c.initCronAdmin() c.initAdditionalOwnerRoles()
return nil return nil
} }
@ -1299,36 +1299,29 @@ func (c *Cluster) initRobotUsers() error {
return nil return nil
} }
func (c *Cluster) initCronAdmin() { func (c *Cluster) initAdditionalOwnerRoles() {
cronAdminName := c.OpConfig.CronAdminUsername for _, additionalOwner := range c.OpConfig.AddtionalOwnerRoles {
if cronAdminName == "" { // fetch all database owners the additional should become a member of
return memberOf := make([]string, 0)
} for username, pgUser := range c.pgUsers {
memberOf := make([]string, 0) if pgUser.IsDbOwner {
for username, pgUser := range c.pgUsers { memberOf = append(memberOf, username)
if pgUser.IsDbOwner { }
memberOf = append(memberOf, username)
} }
}
if len(memberOf) > 1 { if len(memberOf) > 1 {
namespace := c.Namespace namespace := c.Namespace
adminRole := "" additionalOwnerPgUser := spec.PgUser{
if c.OpConfig.EnableAdminRoleForUsers && cronAdminName != c.OpConfig.TeamAdminRole { Origin: spec.RoleOriginSpilo,
adminRole = c.OpConfig.TeamAdminRole MemberOf: memberOf,
} Name: additionalOwner,
cronAdmin := spec.PgUser{ Namespace: namespace,
Origin: spec.RoleOriginSpilo, }
MemberOf: memberOf, if currentRole, present := c.pgUsers[additionalOwner]; present {
Name: cronAdminName, c.pgUsers[additionalOwner] = c.resolveNameConflict(&currentRole, &additionalOwnerPgUser)
Namespace: namespace, } else {
Flags: []string{constants.RoleFlagNoLogin}, c.pgUsers[additionalOwner] = additionalOwnerPgUser
AdminRole: adminRole, }
}
if currentRole, present := c.pgUsers[cronAdminName]; present {
c.pgUsers[cronAdminName] = c.resolveNameConflict(&currentRole, &cronAdmin)
} else {
c.pgUsers[cronAdminName] = cronAdmin
} }
} }
} }

View File

@ -101,7 +101,7 @@ type Auth struct {
InfrastructureRolesDefs string `name:"infrastructure_roles_secrets"` InfrastructureRolesDefs string `name:"infrastructure_roles_secrets"`
SuperUsername string `name:"super_username" default:"postgres"` SuperUsername string `name:"super_username" default:"postgres"`
ReplicationUsername string `name:"replication_username" default:"standby"` ReplicationUsername string `name:"replication_username" default:"standby"`
CronAdminUsername string `name:"cron_admin_username" default:""` AddtionalOwnerRoles []string `name:"additional_owner_roles" default:""`
EnablePasswordRotation bool `name:"enable_password_rotation" default:"false"` EnablePasswordRotation bool `name:"enable_password_rotation" default:"false"`
PasswordRotationInterval uint32 `name:"password_rotation_interval" default:"90"` PasswordRotationInterval uint32 `name:"password_rotation_interval" default:"90"`
PasswordRotationUserRetention uint32 `name:"password_rotation_user_retention" default:"180"` PasswordRotationUserRetention uint32 `name:"password_rotation_user_retention" default:"180"`

View File

@ -53,25 +53,31 @@ func (strategy DefaultUserSyncStrategy) ProduceSyncRequests(dbUsers spec.PgUserM
} }
} else { } else {
r := spec.PgSyncUserRequest{} r := spec.PgSyncUserRequest{}
r.User = dbUser
newMD5Password := util.NewEncryptor(strategy.PasswordEncryption).PGUserPassword(newUser) newMD5Password := util.NewEncryptor(strategy.PasswordEncryption).PGUserPassword(newUser)
if dbUser.Password != newMD5Password { // do not compare for roles coming from docker image
r.User.Password = newMD5Password if newUser.Origin != spec.RoleOriginSpilo {
r.Kind = spec.PGsyncUserAlter if dbUser.Password != newMD5Password {
r.User.Password = newMD5Password
r.Kind = spec.PGsyncUserAlter
}
if addNewFlags, equal := util.SubstractStringSlices(newUser.Flags, dbUser.Flags); !equal {
r.User.Flags = addNewFlags
r.Kind = spec.PGsyncUserAlter
}
} }
if addNewRoles, equal := util.SubstractStringSlices(newUser.MemberOf, dbUser.MemberOf); !equal { if addNewRoles, equal := util.SubstractStringSlices(newUser.MemberOf, dbUser.MemberOf); !equal {
r.User.MemberOf = addNewRoles r.User.MemberOf = addNewRoles
r.Kind = spec.PGsyncUserAlter r.Kind = spec.PGsyncUserAlter
} }
if addNewFlags, equal := util.SubstractStringSlices(newUser.Flags, dbUser.Flags); !equal {
r.User.Flags = addNewFlags
r.Kind = spec.PGsyncUserAlter
}
if r.Kind == spec.PGsyncUserAlter { if r.Kind == spec.PGsyncUserAlter {
r.User.Name = newUser.Name r.User.Name = newUser.Name
reqs = append(reqs, r) reqs = append(reqs, r)
} }
if len(newUser.Parameters) > 0 && !reflect.DeepEqual(dbUser.Parameters, newUser.Parameters) { if newUser.Origin != spec.RoleOriginSpilo &&
len(newUser.Parameters) > 0 &&
!reflect.DeepEqual(dbUser.Parameters, newUser.Parameters) {
reqs = append(reqs, spec.PgSyncUserRequest{Kind: spec.PGSyncAlterSet, User: newUser}) reqs = append(reqs, spec.PgSyncUserRequest{Kind: spec.PGSyncAlterSet, User: newUser})
} }
} }