grant db owners to cron_admin

This commit is contained in:
Felix Kunde 2022-03-02 18:45:48 +01:00
parent ca0c27a51b
commit 89801ef30a
13 changed files with 78 additions and 4 deletions

View File

@ -130,6 +130,8 @@ spec:
users: users:
type: object type: object
properties: properties:
cron_admin_username:
type: string
enable_password_rotation: enable_password_rotation:
type: boolean type: boolean
default: false default: false

View File

@ -59,6 +59,14 @@ 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
cron_admin_username: cron_admin
# enable password rotation for app users that are not database owners
enable_password_rotation: false
# rotation interval for updating credentials in K8s secrets of app users
password_rotation_interval: 90
# retention interval to keep rotation users
password_rotation_user_retention: 180
# postgres username used for replication between instances # postgres username used for replication between instances
replication_username: standby replication_username: standby
# postgres superuser name to be created by initdb # postgres superuser name to be created by initdb

View File

@ -177,6 +177,13 @@ 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**
Specifies the role that owns rights to set up cron jobs with `pg_cron`
extension inside the `postgres` database. The must be pre-configured in the
docker image. In Spilo this role is called `cron_admin`. This role will
become a member of all database owners so that they can set up cron jobs
e.g. as part of a migration script. 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
credentials in the corresponding K8s secrets by replacing the username and credentials in the corresponding K8s secrets by replacing the username and

View File

@ -23,6 +23,7 @@ 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
@ -109,7 +110,7 @@ data:
# pod_service_account_role_binding_definition: "" # pod_service_account_role_binding_definition: ""
pod_terminate_grace_period: 5m pod_terminate_grace_period: 5m
# postgres_superuser_teams: "postgres_superusers" # postgres_superuser_teams: "postgres_superusers"
# protected_role_names: "admin" # protected_role_names: "admin,cron_admin"
ready_wait_interval: 3s ready_wait_interval: 3s
ready_wait_timeout: 30s ready_wait_timeout: 30s
repair_period: 5m repair_period: 5m

View File

@ -128,6 +128,8 @@ spec:
users: users:
type: object type: object
properties: properties:
cron_admin_username:
type: string
enable_password_rotation: enable_password_rotation:
type: boolean type: boolean
default: false default: false

View File

@ -31,6 +31,7 @@ configuration:
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:
@ -163,6 +164,7 @@ configuration:
# - postgres_superusers # - postgres_superusers
protected_role_names: protected_role_names:
- admin - admin
- cron_admin
role_deletion_suffix: "_deleted" role_deletion_suffix: "_deleted"
team_admin_role: admin team_admin_role: admin
team_api_role_configuration: team_api_role_configuration:

View File

@ -1142,6 +1142,18 @@ var OperatorConfigCRDResourceValidation = apiextv1.CustomResourceValidation{
"users": { "users": {
Type: "object", Type: "object",
Properties: map[string]apiextv1.JSONSchemaProps{ Properties: map[string]apiextv1.JSONSchemaProps{
"cron_admin_username": {
Type: "string",
},
"enable_password_rotation": {
Type: "boolean",
},
"password_rotation_interval": {
Type: "integer",
},
"password_rotation_user_retention": {
Type: "integer",
},
"replication_username": { "replication_username": {
Type: "string", Type: "string",
}, },

View File

@ -39,6 +39,7 @@ type OperatorConfigurationList struct {
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"`
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"`

View File

@ -228,6 +228,8 @@ 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()
return nil return nil
} }
@ -1297,6 +1299,40 @@ func (c *Cluster) initRobotUsers() error {
return nil return nil
} }
func (c *Cluster) initCronAdmin() {
cronAdminName := c.OpConfig.CronAdminUsername
if cronAdminName == "" {
return
}
memberOf := make([]string, 0)
for username, pgUser := range c.pgUsers {
if pgUser.IsDbOwner {
memberOf = append(memberOf, username)
}
}
if len(memberOf) > 1 {
namespace := c.Namespace
adminRole := ""
if c.OpConfig.EnableAdminRoleForUsers && cronAdminName != c.OpConfig.TeamAdminRole {
adminRole = c.OpConfig.TeamAdminRole
}
cronAdmin := spec.PgUser{
Origin: spec.RoleOriginSpilo,
MemberOf: memberOf,
Name: cronAdminName,
Namespace: namespace,
Flags: []string{constants.RoleFlagNoLogin},
AdminRole: adminRole,
}
if currentRole, present := c.pgUsers[cronAdminName]; present {
c.pgUsers[cronAdminName] = c.resolveNameConflict(&currentRole, &cronAdmin)
} else {
c.pgUsers[cronAdminName] = cronAdmin
}
}
}
func (c *Cluster) initTeamMembers(teamID string, isPostgresSuperuserTeam bool) error { func (c *Cluster) initTeamMembers(teamID string, isPostgresSuperuserTeam bool) error {
teamMembers, err := c.getTeamMembers(teamID) teamMembers, err := c.getTeamMembers(teamID)

View File

@ -1622,7 +1622,7 @@ func (c *Cluster) generateUserSecrets() 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 { if pgUser.Origin != spec.RoleOriginTeamsAPI && pgUser.Origin != spec.RoleOriginSpilo {
c.logger.Warningf("could not generate secret for a non-teamsAPI role %q: role has no password", c.logger.Warningf("could not generate secret for a non-teamsAPI role %q: role has no password",
pgUser.Name) pgUser.Name)
} }

View File

@ -55,6 +55,7 @@ func (c *Controller) importConfigurationFromCRD(fromCRD *acidv1.OperatorConfigur
// user config // user config
result.SuperUsername = util.Coalesce(fromCRD.PostgresUsersConfiguration.SuperUsername, "postgres") result.SuperUsername = util.Coalesce(fromCRD.PostgresUsersConfiguration.SuperUsername, "postgres")
result.ReplicationUsername = util.Coalesce(fromCRD.PostgresUsersConfiguration.ReplicationUsername, "standby") result.ReplicationUsername = util.Coalesce(fromCRD.PostgresUsersConfiguration.ReplicationUsername, "standby")
result.CronAdminUsername = fromCRD.PostgresUsersConfiguration.CronAdminUsername
result.EnablePasswordRotation = fromCRD.PostgresUsersConfiguration.EnablePasswordRotation result.EnablePasswordRotation = fromCRD.PostgresUsersConfiguration.EnablePasswordRotation
result.PasswordRotationInterval = util.CoalesceUInt32(fromCRD.PostgresUsersConfiguration.PasswordRotationInterval, 90) result.PasswordRotationInterval = util.CoalesceUInt32(fromCRD.PostgresUsersConfiguration.PasswordRotationInterval, 90)
result.PasswordRotationUserRetention = util.CoalesceUInt32(fromCRD.PostgresUsersConfiguration.DeepCopy().PasswordRotationUserRetention, 180) result.PasswordRotationUserRetention = util.CoalesceUInt32(fromCRD.PostgresUsersConfiguration.DeepCopy().PasswordRotationUserRetention, 180)
@ -186,7 +187,7 @@ func (c *Controller) importConfigurationFromCRD(fromCRD *acidv1.OperatorConfigur
result.TeamAdminRole = fromCRD.TeamsAPI.TeamAdminRole result.TeamAdminRole = fromCRD.TeamsAPI.TeamAdminRole
result.PamRoleName = util.Coalesce(fromCRD.TeamsAPI.PamRoleName, "zalandos") result.PamRoleName = util.Coalesce(fromCRD.TeamsAPI.PamRoleName, "zalandos")
result.PamConfiguration = util.Coalesce(fromCRD.TeamsAPI.PamConfiguration, "https://info.example.com/oauth2/tokeninfo?access_token= uid realm=/employees") result.PamConfiguration = util.Coalesce(fromCRD.TeamsAPI.PamConfiguration, "https://info.example.com/oauth2/tokeninfo?access_token= uid realm=/employees")
result.ProtectedRoles = util.CoalesceStrArr(fromCRD.TeamsAPI.ProtectedRoles, []string{"admin"}) result.ProtectedRoles = util.CoalesceStrArr(fromCRD.TeamsAPI.ProtectedRoles, []string{"admin", "cron_admin"})
result.PostgresSuperuserTeams = fromCRD.TeamsAPI.PostgresSuperuserTeams result.PostgresSuperuserTeams = fromCRD.TeamsAPI.PostgresSuperuserTeams
result.EnablePostgresTeamCRD = fromCRD.TeamsAPI.EnablePostgresTeamCRD result.EnablePostgresTeamCRD = fromCRD.TeamsAPI.EnablePostgresTeamCRD
result.EnablePostgresTeamCRDSuperusers = fromCRD.TeamsAPI.EnablePostgresTeamCRDSuperusers result.EnablePostgresTeamCRDSuperusers = fromCRD.TeamsAPI.EnablePostgresTeamCRDSuperusers

View File

@ -30,6 +30,7 @@ const (
RoleOriginManifest RoleOriginManifest
RoleOriginInfrastructure RoleOriginInfrastructure
RoleOriginTeamsAPI RoleOriginTeamsAPI
RoleOriginSpilo
RoleOriginSystem RoleOriginSystem
RoleOriginBootstrap RoleOriginBootstrap
RoleConnectionPooler RoleConnectionPooler

View File

@ -101,6 +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:""`
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"`
@ -210,7 +211,7 @@ type Config struct {
TeamAPIRoleConfiguration map[string]string `name:"team_api_role_configuration" default:"log_statement:all"` TeamAPIRoleConfiguration map[string]string `name:"team_api_role_configuration" default:"log_statement:all"`
PodTerminateGracePeriod time.Duration `name:"pod_terminate_grace_period" default:"5m"` PodTerminateGracePeriod time.Duration `name:"pod_terminate_grace_period" default:"5m"`
PodManagementPolicy string `name:"pod_management_policy" default:"ordered_ready"` PodManagementPolicy string `name:"pod_management_policy" default:"ordered_ready"`
ProtectedRoles []string `name:"protected_role_names" default:"admin"` ProtectedRoles []string `name:"protected_role_names" default:"admin,cron_admin"`
PostgresSuperuserTeams []string `name:"postgres_superuser_teams" default:""` PostgresSuperuserTeams []string `name:"postgres_superuser_teams" default:""`
SetMemoryRequestToLimit bool `name:"set_memory_request_to_limit" default:"false"` SetMemoryRequestToLimit bool `name:"set_memory_request_to_limit" default:"false"`
EnableLazySpiloUpgrade bool `name:"enable_lazy_spilo_upgrade" default:"false"` EnableLazySpiloUpgrade bool `name:"enable_lazy_spilo_upgrade" default:"false"`